NatureGL Grassv1.1.0

Guides

Wind field

One wind moves everything. Gust fronts roll downwind across the field, and any material you patch with the same WindField sways with the same gusts at the same moment.

windy (speed 1.6, gust 1.2, towards 70°). A gust you see in the far field reaches the near blades a moment later.

#Three numbers

FieldRange
speed0..2Overall strength. Also sets how fast gust fronts travel: 2.5 + 3.5 × speed m/s
gust0..1.5How much the gust fronts add on top of the steady wind
directiondegreesThe direction the wind blows towards. 0 = +X, 90 = +Z

Set them through the grass, or on the wind itself:

js
grass.setParams({ wind: { speed: 1.2, gust: 0.9, direction: 250 } });

grass.wind.speed = 0.3;     // getters and setters on the WindField
grass.wind.direction = 90;

Each GrassSystem makes its own WindField unless you pass one in create({ wind }). Pass the same field to several systems to share one wind.

#Gust sheen

Bent blades show their paler side. The grass brightens with the local gust strength, so gust fronts read as waves of light rolling across the field. gustSheen (0..2) scales the effect, and patchTerrainMaterial() applies the same sheen to the turf colour on your ground.

js
grass.setParams({ grass: { gustSheen: 1.4 } });   // what windy uses

#Make other meshes sway

wind.patchMaterial() adds the same wind to a built-in material. It works on Mesh and InstancedMesh, and on a MeshDepthMaterial so the shadows move too.

js
const leafMat = new THREE.MeshStandardMaterial({ map: leafTex, alphaTest: 0.45, side: THREE.DoubleSide });
const leafDepth = new THREE.MeshDepthMaterial({ map: leafTex, alphaTest: 0.45, side: THREE.DoubleSide });

grass.wind.patchMaterial(leafMat, { height: 8, strength: 0.03, flutter: 0.035 });
grass.wind.patchMaterial(leafDepth, { height: 8, strength: 0.03, flutter: 0.035 });
treeMesh.customDepthMaterial = leafDepth;
OptionDefault
height1Object-space height (at scale 1) where the sway reaches full strength. Displacement grows with (y / height)², so the roots stay put
strength1Lean at the top as a fraction of height. The built-in trees use 0.03, bushes 0.06 and flowers 0.35
flutter0High-frequency motion along the normal, for leaves
tramplenullPass grass.trample to also flatten the object under tramplers

The patch chains with other onBeforeCompile hooks, such as NatureGL Sky's cloud shadows or NatureGL Weather's wetness, in either order. The scatter layers use exactly these calls, so trees, bushes and flowers already sway.

#Use it in your own shader

WindField.glsl declares the wind uniforms and vec2 windAt(vec2 worldXZ, float time). Spread getUniforms() into your material. The uniform objects are shared by reference, so there's nothing to copy per frame.

js
import { WindField } from 'naturegl-grass';

const mat = new THREE.ShaderMaterial({
  uniforms: { ...grass.wind.getUniforms() },
  vertexShader: `${WindField.glsl}
    void main() {
      vec3 p = position;
      vec2 w = windAt((modelMatrix * vec4(p, 1.0)).xz, uWindTime);
      p.xz += w * p.y * 0.3;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
    }`,
  fragmentShader: `void main() { gl_FragColor = vec4(1.0); }`,
});

windAt returns a horizontal bend vector of roughly 0 to 1.5. gpGustAt(p, t) returns just the gust front mask (0..1).

#Sample it on the CPU

js
const w = grass.wind.sample(player.position.x, player.position.z);   // THREE.Vector2
audio.gain = Math.min(1, w.length());
leafParticle.velocity.x += w.x * dt;

sample() is a CPU approximation of windAt, for gameplay, audio and particles. grass.wind.time is the elapsed wind time in seconds.