General-purpose wind shader for foliage

Here’s a tricky problem; How can you write a vertex shader that will give a reasonable simulation of wind on any plant mesh? It’s easy to move vertices around, but much harder to make it look good. When there are millions of instances of grass, they need to be drawn as fast as possible, so it needs to be very compact and efficient. Any ideas?

If you have played Crysis, that is the effect I am going for.

With an advection simulation?

Did not see crysis, but an idea :
animated 2d (horizontal) luminance noise (so it is almost a 3d noise) with scrolling. White move the foliage in the wind direction, black does not move, and greys are in between.

The animated 2d noise can be just precalculated as a couple of noise textures being dynamically interpolated.

GPU Gems 3 has an article from a Crytek guy, that explains how they do it. Basically you modulate the vertex-position with different functions and they weights, which function to apply how much, are stored in the mesh and thus artists can define the behaviour of the plants differently for leafs, branches, trunks etc.

Jan.

When I stuck to problem like this, I tried simple oscillation based on plants’ center positions, it is very easy and looks reasonable.


#include "../../include/dynamic_lights.cgh"

// main volumetric grass multi light shader
void mainVP(
   // dynamic lighting
   VP_LIGHT_DATA

   in varying float3 inCenter             : POSITION,
   in varying float4 inColor              : COLOR0,
   in varying float2 inUV                 : TEXCOORD0,
   in varying float3 inOffset             : ATTR5,
   in varying float3 inSurfNormal         : ATTR6,
   in varying float3 inObscur             : ATTR7,

   out varying float4 oHPos          : POSITION,
   out varying float4 oUVDetUV       : TEXCOORD0,
   out varying float4 oAmbientShadow : TEXCOORD1,
   out varying float4 oDiffSpecDetS  : TEXCOORD2,
   out varying float4 oXYZW          : TEXCOORD3,
   out varying float  oFogC          : FOGC,

   // terrain-like shading tunable constants
   in uniform float3 TerrainAmbient,
   in uniform float3 TerrainDiffuse,
   in uniform float4 TerrainObscurParams,

   // lighting modification params
   in uniform float4 DiffuseRemapping,

   // for wind tracking
   in uniform float    TimeStamp,
   in uniform float3   WindParam
)
{
  // modelview matrices and so on
  float4x4 ModelViewProjMatrix = glstate.matrix.mvp;
  float3x4 ModelViewMatrix = (float3x4)glstate.matrix.modelview[0];
  float4x4 ViewToDepthMap = glstate.matrix.program[7];
  float4x4 ViewToTerrainColorMap = glstate.matrix.program[6];
  float3 vLightView = (float3)glstate.light[0].position;

  // SHORT coords repack scales
  inCenter *= float3(glstate.material.back.emission);

  // wind tracking
  float param = inCenter.x + inCenter.y;
  float Arg = TimeStamp + param * 0.25f + frac(param);
  float OffsetForce = max(inOffset.z, 0.f);
  float oscillation = (min(WindParam.z * 0.02f, 0.50f) + 0.20f * sin(Arg)) * OffsetForce;
  float3 delta = float3(WindParam.x, WindParam.y, 0.f) * oscillation;
  float3 vOffset = inOffset + delta;

  // get transition frame
  float fTransition = inColor.a;
  // get point position
  float3 vPos = inCenter + vOffset * fTransition;
  // homogenous division
  oHPos = mul(ModelViewProjMatrix, float4(vPos, 1.f));

  // terrain normal is point normal
  float3 vPseudoNormal = inSurfNormal * 2.0f - 1.0f;
  vPseudoNormal = mul((float3x3)ModelViewMatrix, vPseudoNormal);

  // get shadow attenuation force
  float3 vPosView = mul(ModelViewMatrix, float4(vPos, 1.f));
  float vPosLengthInv = rsqrt(dot(vPosView, vPosView));
  float4 ShadowAttenuation = glstate.light[0].attenuation;
  oAmbientShadow.w = (-ShadowAttenuation.x) * vPosLengthInv + ShadowAttenuation.y;

  // texture coords
  oUVDetUV.xy = inUV;
  float4 vTerrainColorMapTexCoords = mul(ViewToTerrainColorMap, float4(vPosView, 1.0f));
  oUVDetUV.zw = vTerrainColorMapTexCoords.xy;
  oDiffSpecDetS.w = vTerrainColorMapTexCoords.w;

  // get shadow coords
  oXYZW = mul(ViewToDepthMap, float4(vPosView, 1.0f));

  // save fog coordinate
  oFogC = vPosView.z;

  // get obscurance
  float3 cObscurance = inObscur * TerrainObscurParams.x;
  // save ambient
  oAmbientShadow.rgb = TerrainAmbient * cObscurance;
  // save diffuse and specular
  oDiffSpecDetS.rgb = TerrainDiffuse * max(min(dot(vPseudoNormal, vLightView) * DiffuseRemapping.x + DiffuseRemapping.y, 0.9999f), 0.0001f);

  // Needed for point lights
  VP_ALLOC_DATA;
  VP_PROCESS_POINT_LIGHTS_VP40HACK(vPosView, vPseudoNormal, oAmbientShadow.rgb);
  VP_CALC_SPOT_LIGHTS;
  VP_PASS_LIGHT_DATA;
}

Jackis, do you have a demo?

GPU Gems 3 has an article from a Crytek guy, that explains how they do it. Basically you modulate the vertex-position with different functions and they weights, which function to apply how much, are stored in the mesh and thus artists can define the behaviour of the plants differently for leafs, branches, trunks etc.

I figured there was something like that going on, but how the heck does an artist control that?

Vertex-painting the weights seems reasonable.

That’s a good idea.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.