NatureGL Grassv1.1.0

More

Troubleshooting

The problems people hit most often, and the fix for each.

#No grass at all

  • update() is never called. The chunk lists are built in grass.update(dt). Until the first call, nothing is drawn.
  • The camera is outside bounds. Grass never grows outside the baked rectangle. Check grass.terrain.bounds.
  • The mask is 0 everywhere. grass.terrain.maskAt(x, z) shows what was baked. Masks are clamped to 0..1.
  • The terrain is far below or above the ground mesh. The blades sit on heightAt, not on your mesh. Build both from the same function.
  • grass.enabled is false.

#A hard line where the grass ends

Call grass.patchTerrainMaterial(groundMaterial). Without it, the edge of the far ring shows as a line against your ground colour. If your ground uses a custom ShaderMaterial, wire in GRASS_COLOR_GLSL and turfColor() yourself. See No visible ring.

#Blades poke through the water

Return 0 from mask wherever there is water or beach, then call grass.setTerrain() if you changed it at runtime. The fireflies stay off masked ground too.

#The grass doesn't receive shadows

  • Turn on renderer.shadowMap.enabled and keep renderer.shadowMap.type at the default THREE.PCFShadowMap. The grass is unshadowed with VSM or basic shadow maps.
  • The light needs castShadow = true, and it must be bound (bindLights(sun)) or passed as setLighting({ shadowLight: sun }).
  • The shadow camera must cover the area around the camera. The grass beyond it is lit but unshadowed.

Blades never cast shadows. That is by design.

#The grass is too bright or too dark

The grass reads light in three.js physically based units. sunColor means DirectionalLight.color × intensity, and ambient means HemisphereLight.color × intensity. If you pass values by hand from an older, non-physical setup, multiply them by π, as the demo does. With bindLights(), match your lights to the rest of the scene and the grass follows.

#Trampling doesn't show

  • The object must be visible. Hidden tramplers are skipped.
  • It must be near the ground. Flattening fades out from 1.2 × radius above the terrain and stops at 2.7 × radius. The terrain height is grass.terrain.heightAt(x, z).
  • grass.trample.enabled must be true.
  • There are at most 16 stamps per frame. Extra tramplers and one-shot stamps are dropped for that frame.
  • It must be inside the trample buffer, trampleSize metres (default 160) around the camera.

#The gusts run at double speed

Two grass systems share one WindField. Each one calls wind.update(dt). See Who advances the wind.

#The chunks pop at the screen edge

Chunk culling uses the baked height range. Terrain spikes narrower than one bake texel (about 0.9 m at the default resolution) can make a chunk's box too low. Raise terrain.resolution, up to what your heightAt can afford at bake time.

#A slow setTerrain()

The bake calls heightAt and mask once per texel: about a million calls at 1024². Make those functions cheap, lower resolution, or re-bake only when the terrain actually changes.

#Scatter layers ignore my options

Before 1.1.0, addFlowers(), addRocks(), addTrees() and addBushes() dropped the layer options they didn't name, such as filter or maskPower. Update to 1.1.0, where they are forwarded to addLayer(). See the changelog.

#It fails on a device

The grass needs WebGL2 and float colour buffers (EXT_color_buffer_float). three enables the extension, and all desktop GPUs support it. On other devices, check renderer.getContext().getExtension('EXT_color_buffer_float').