NatureGL Grassv1.1.0

Guides

Trampling

Register any object and the grass flattens under it, leans the way it moved, and springs back over a few seconds. The trample buffer is a GPU texture that follows the camera, so it works anywhere on the terrain.

The demo ball rolling a curved path at 4.5 m/s. It was registered with grass.addTrampler(ball, 0.95).

#Add a trampler

js
const remove = grass.addTrampler(player, 0.6);        // radius in metres, strength defaults to 1
grass.addTrampler(cartWheel, 0.4, 0.8);

remove();                                              // or grass.removeTrampler(player)

On every update() the grass reads each trampler's world position, works out its velocity from the last frame, and stamps the buffer. The push direction blends away from the centre with along the motion, so a moving object leaves blades combed in its direction of travel.

Behaviour
FalloffFull strength inside 35 % of the radius, fading to 0 at the radius
Lift-offFull strength up to 1.2 × radius above the ground, fading to 0 at 2.7 × radius. A jumping player stops flattening mid-air
Hidden objectsAn object with visible = false does not trample
Limit16 stamps per frame, tramplers and one-shot stamps combined

#One-shot stamps

trample.stamp() flattens for a single frame. Call it every frame for a brush, or once for an impact.

js
// the demo's cursor brush: radius 0.6 m, strength 0.85, pushed along the cursor motion
grass.trample.stamp(hit.x, hit.z, 0.6, 0.85, move.x, move.y);

// an explosion
grass.trample.stamp(x, z, 6, 1);

dirX and dirZ are a normalised push direction multiplied by a speed factor from 0 to 1.

#Spring-back and control

js
grass.trample.recover = 0.16;   // 1/s, the default: about half the flattening is gone after 4 s
grass.trample.enabled = false;  // pause trampling (the demo's Trample toggle)
grass.trample.reset();          // clear all flattening now

The flatten amount decays by exp(−recover × dt) each frame. Raise recover for springier grass.

#How the buffer works

#A square that follows the camera

The buffer covers trampleSize metres (default 160, so ±80 m) around the camera. When the camera strays more than a quarter of that from the centre, the buffer scrolls by whole texels, so the flattening stays put in the world.

#Ping-pong half floats

Two half-float render targets alternate each frame. Red is the flatten amount, green and blue the push direction. One fullscreen pass decays the old state and applies the frame's stamps.

#Read by the blades

The blade pass samples the buffer at each blade's root. It bends the blade towards the push direction, lowers it by up to half, and brightens it a little, so trodden grass reads lighter.

The resolution comes from the quality tier: 512², 768², 1024² and 1024² texels from low to ultra. At 1024² one texel is about 0.16 m.

js
const grass = await GrassSystem.create({ renderer, scene, camera, terrain, trampleSize: 240 });   // wider, coarser

#Flowers and bushes trample too

Anything patched with wind.patchMaterial(mat, { trample: grass.trample }) flattens in the same places. The built-in flower and bush layers do this already. See Trees, flowers and rocks.

For your own shaders, TRAMPLE_GLSL declares vec3 trampleAt(vec2 xz) (flatten amount, push x, push z). Pair it with grass.trample.uniforms.