Home
Shaders and presets

Vegetation Shaders

GLSL shaders for wind animation on grass and trees

Shaders for realistic wind animation on instanced vegetation including grass, trees, and bushes.

Available Shaders

import {
  grassWindVertexShader,
  treeWindVertexShader,
} from 'strata-game-library/shaders';

Grass Wind Shader

Wind animation for grass instances:

Uniforms

UniformTypeDescription
uTimefloatAnimation time
uWindSpeedfloatWind animation speed
uWindStrengthfloatWind bend amount
uWindDirectionvec3Wind direction
uWindNoiseScalefloatNoise pattern scale
uWindTurbulencefloatTurbulence intensity

Usage with InstancedMesh

import { grassWindVertexShader } from 'strata-game-library/shaders';

const grassMaterial = new THREE.ShaderMaterial({
  vertexShader: grassWindVertexShader,
  fragmentShader: `
    varying vec3 vColor;
    void main() {
      gl_FragColor = vec4(vColor, 1.0);
    }
  `,
  uniforms: {
    uTime: { value: 0 },
    uWindSpeed: { value: 1 },
    uWindStrength: { value: 0.3 },
    uWindDirection: { value: new THREE.Vector3(1, 0, 0.5).normalize() },
    uWindNoiseScale: { value: 0.05 },
    uWindTurbulence: { value: 0.2 },
  },
});

const grassGeometry = createGrassBladeGeometry();
const grassMesh = new THREE.InstancedMesh(grassGeometry, grassMaterial, 10000);

Tree Wind Shader

More complex wind for trees with branch hierarchy:

Uniforms

UniformTypeDescription
uTimefloatAnimation time
uWindSpeedfloatWind animation speed
uWindStrengthfloatWind bend amount
uWindDirectionvec3Wind direction
uTrunkStiffnessfloatTrunk resistance to wind
uBranchStiffnessfloatBranch resistance
uLeafStiffnessfloatLeaf resistance

Usage

import { treeWindVertexShader } from 'strata-game-library/shaders';

const treeMaterial = new THREE.ShaderMaterial({
  vertexShader: treeWindVertexShader,
  fragmentShader: standardFragmentShader,
  uniforms: {
    uTime: { value: 0 },
    uWindSpeed: { value: 0.5 },
    uWindStrength: { value: 0.15 },
    uWindDirection: { value: new THREE.Vector3(1, 0, 0).normalize() },
    uTrunkStiffness: { value: 0.9 },
    uBranchStiffness: { value: 0.5 },
    uLeafStiffness: { value: 0.2 },
  },
});

GLSL Code Samples

Simple Grass Wind

vec3 grassWind(vec3 position, float time, vec3 windDir, float strength) {
  // Height-based influence (more at top)
  float heightFactor = position.y;

  // Wind noise
  float noise = sin(position.x * 0.5 + time) * cos(position.z * 0.5 + time * 0.7);

  // Apply wind
  vec3 offset = windDir * noise * strength * heightFactor;

  return position + offset;
}

Layered Grass Wind

vec3 layeredGrassWind(vec3 position, vec3 worldPos, float time) {
  float heightFactor = position.y;

  // Primary wind wave
  float primaryWave = sin(worldPos.x * uWindNoiseScale + time * uWindSpeed);
  primaryWave += sin(worldPos.z * uWindNoiseScale * 0.7 + time * uWindSpeed * 0.8);
  primaryWave *= 0.5;

  // Secondary turbulence
  float turbulence = sin(worldPos.x * uWindNoiseScale * 3.0 + time * uWindSpeed * 2.0);
  turbulence *= uWindTurbulence;

  // Combine
  float totalWind = (primaryWave + turbulence) * uWindStrength * heightFactor;

  vec3 windOffset = uWindDirection * totalWind;

  return position + windOffset;
}

Tree Branch Wind

vec3 treeBranchWind(vec3 position, vec3 worldPos, float time, float stiffness) {
  // Distance from trunk (using vertex color or UV)
  float distanceFromTrunk = length(position.xz);

  // Reduce effect near trunk
  float influence = smoothstep(0.0, 1.0, distanceFromTrunk) * (1.0 - stiffness);

  // Multi-frequency wind
  float wind = sin(worldPos.x * 0.1 + time * uWindSpeed) * 0.5;
  wind += sin(worldPos.z * 0.15 + time * uWindSpeed * 0.7) * 0.3;
  wind += sin(time * uWindSpeed * 2.0 + distanceFromTrunk) * 0.2; // Branch flutter

  vec3 offset = uWindDirection * wind * uWindStrength * influence;

  return position + offset;
}

Leaf Flutter

vec3 leafFlutter(vec3 position, vec3 normal, float time, float leafStiffness) {
  // Random flutter per vertex
  float flutter = sin(time * 10.0 + position.x * 50.0) *
                  cos(time * 8.0 + position.z * 50.0);
  flutter *= (1.0 - leafStiffness);

  // Flutter along normal
  vec3 offset = normal * flutter * 0.02;

  return position + offset;
}

Integration with Instancing

Per-Instance Wind Offset

// In vertex shader with instanceMatrix
attribute vec3 instanceOffset; // Per-instance world position

void main() {
  // Get instance world position
  vec3 worldPos = (instanceMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz;

  // Apply wind based on world position
  vec3 windedPosition = layeredGrassWind(position, worldPos, uTime);

  // Transform
  vec4 mvPosition = modelViewMatrix * instanceMatrix * vec4(windedPosition, 1.0);
  gl_Position = projectionMatrix * mvPosition;
}

Wind Zones

Support for multiple wind sources:

uniform vec3 uWindZones[4];      // Position
uniform float uWindZoneStrength[4]; // Strength
uniform float uWindZoneRadius[4];   // Radius

vec3 calculateWindZones(vec3 worldPos) {
  vec3 totalWind = uWindDirection * uWindStrength; // Base wind

  for (int i = 0; i < 4; i++) {
    float dist = length(worldPos - uWindZones[i]);
    float influence = 1.0 - smoothstep(0.0, uWindZoneRadius[i], dist);
    vec3 zoneDir = normalize(worldPos - uWindZones[i]);
    totalWind += zoneDir * uWindZoneStrength[i] * influence;
  }

  return totalWind;
}