NatureGL Grassv1.1.0

More

Under the hood

What the grass does each frame, and what changed from the single-file grasspro.html page it grew from. Drag each slider to compare the two from the same camera and preset.

#The frame

#Wind and trample

wind.update(dt) advances the wind time. The trample pass decays last frame's flattening, scrolls the buffer if the camera has moved far enough, and applies up to 16 stamps.

#Chunk lists

For each of the three rings, the CPU gathers the chunks in range and drops those that are fully masked or outside the frustum. It uses the baked height range of each chunk, cached. The survivors are sorted front to back and written to a small float texture of chunk origins.

#Blade pass

One fullscreen draw per ring into four float render targets (MRT), one texel per blade. Each fragment works out everything that is constant along its blade:

TargetHolds
APosition, ground height, blade height (≤ 0 = culled)
BBend from wind, lean, clumping and trampling; facing angle; width
CColour multiplier; packed root AO, dryness and trodden amount
DFar turf colour; packed far blend, dry tip and gust strength

#Blade draw

Each ring is one instanced draw. The blade vertex shader fetches its four texels and bends a strip of 2 × segments + 1 vertices along an arc. It no longer runs noise, clump searches or wind per vertex.

#Shading

Hemispherical ambient with root AO, the sun with the PCF shadow map and the optional cloud shadow, back-lit translucency, a Kajiya-Kay sheen along the blade, gust sheen, and scene fog.

#No grass ring

The original drew two camera-centred grids that ended about 50 m out on pale bare ground. The new rings reach 110 m on high. The far blades blend into a calibrated turf colour, and patchTerrainMaterial() paints the same colour on the ground, so the field carries on to the horizon.

NatureGL Grass
grasspro.html
grasspro.htmlNatureGL Grass
Looking across the field from the same camera. The pale band where the original's grids end is gone.
NatureGL Grass
grasspro.html
grasspro.htmlNatureGL Grass
Over the hills from the same camera. The grass now covers the far slopes too.

#More visible blades for the cost

The original ran the full vertex shader for 0.6 M instances every frame, including all the off-screen ones. Now:

  • the CPU culls whole chunks against the frustum and the view distance,
  • chunks are drawn front to back, so hidden blades fail the depth test early,
  • and the per-blade work runs once per blade in the blade pass, not once per vertex.

#A better blade

  • Clumping. Voronoi tufts share height and tint, and their blades splay outward from the tuft centre.
  • Dry tips on some blades, and dry patches across the field.
  • Root AO. Darker at the base where the sward is denser.
  • Anisotropic sheen. A Kajiya-Kay highlight along the blade.
  • Gust sheen. Bent blades show their paler side, so gust fronts read as waves of light.
  • Translucency. Reworked into light on the far face plus forward scatter.
NatureGL Grass
grasspro.html
grasspro.htmlNatureGL Grass
golden: back-lit blades, same camera.

#Everything else that changed

AreaNatureGL Grass
TerrainWas hard-coded in both JS and GLSL. Now any heightAt, mask and colorAt, baked once. The demo's Path toggle and the grass-free rock footprints use the mask
Lightsthree.js lights through bindLights(), including real PCF shadow maps through sampler2DShadow, in physically based light units
SkyAn optional cloud-shadow input
SceneMeshes go into the caller's scene
WindA shared field that other materials adopt. It chains with other onBeforeCompile patches
TramplingWas a fixed ±80 m square. Now follows the camera, with 16 tramplers plus one-shot stamps. Objects that lift off stop flattening
PropsScatterSystem: flowers, rocks, trees and bushes streamed in chunks with distance fade. Flowers use the wind and trampling
Tree leavesBack-lit. The original's leaf glow was front-lit by mistake
three.js0.160 → 0.186: THREE.Timer replaces Clock, PCFShadowMap replaces the removed PCFSoftShadowMap, DataTextures replace canvas textures, and GL errors fail the tests