14 Comments

Wind animations for vegetation

(Note: the original post and the included visual studio solution had some inaccuracies. These have been fixed as of 2:25PM PST 10/19/2011)

GPU Gems 3 provides a nice article on vegetation shading and animation in the Crysis engine. Since one of the features of the game I have in development is a dynamic weather system with sun, rain, and wind, I decided to adapt the wind animations described in this article for my purposes.

Up to this point, I’d been using a very simple wind animation for my trees. They would simply move back and forth along the horizontal plane in the direction of the wind (and back again). This introduced some stretching, but it wasn’t very noticeable. I thought the effect was pleasing, but a friend told me it didn’t look realistic at all. The leaves didn’t even wiggle!

Here is an example of my old way in action:

I was planning to revisit this at some point, and I knew about the Crytek  paper on the topic. I got back to modelling wind for the weather systems, and so this seemed like a good time.

The Crytek paper cited above includes some code snippets, but they are the kind that were cut and pasted out of something bigger and “don’t compile”, or are full of mysterious constants that aren’t explained in the text. So in this post I’ll attempt to explain the details as I understand them, and provide working XNA implementation.

The paper describes two types of wind bending: main bending and detail bending.

Main bending

This just bends the entire plant in the direction of the wind. It’s basically the simple implementation I had before except without the phase that moves the plant back and forth (that was unrealistic anyway), and with logic that eliminates the stretching by ensuring that the scalar length of the branches/leaves is maintained. The following image shows the same plant with different levels of main bending:

Detail Bending

This is the more complicated one that makes things start to look realistic by providing “chaotic” back-and-forth motion for the leaves and/or branches. The article and code mention both leaves and branches as if there were some distinction, but I in my opinion this just causes confusion. For the purpose of the animation these can fundamentally be treated as the same thing (along with the main trunk of the plant). Detail bending can be applied to the entire plant, but it is likely that the trunk and/or branches may have “degenerate” values that result in no actual detail blending.

There are two types of detail bending motion. The Crytek paper calls one “Leaves’ bending” and the other “per leaf bending”, which isn’t very clear. However, they can be roughly categorized as up-and-down and side-to-side. The up-and-down motion is a relatively slow movement that decreases in amplitude closer to the core of the plant (e.g. where the leaf attaches to the stem, for instance). The side-to-side motion is a rapid wiggling. The different types of motion are shown clearly in the video at the end of this post.

Controlling detail bending

Per Crytek convention, there are three per-vertex attributes that control the detail bending: Overall stiffness, leaf edge stiffness and per-leaf phase. Using the term “leaf” here is a bit inaccurate, since this doesn’t necessarily just apply to leaves. Crytek uses vertex color to control these three attributes. Here is the plant above shown with vertex colors that I painstakingly added:

It looks like a mess with all color components showing. Let’s look at each of these in more detail and show how they are used in the plant. Pay close attention to the stems (just barely visible in the center), as these are good examples of “degenerate” values that essentially eliminate detail bending.

Overall stiffness

This controls the up-and-down motion that we talked about. Generally, the main trunk, and base of the stems would have a value of 0, while the outermost bits of the plant would have a value of 1: they move up and down the most in response to wind. For some reason, Crytek uses Blue = 255 to represent 0 (maximum stiffness). I’ll keep that convention, but it means that we need to invert the value in the vertex shader (one of the things that isn’t shown in Crytek’s sample code). This also affects the side-to-side motion’s amplitude.

So here you can see that stiff parts of the plant (e.g. the stems) are full blue, while the outer edges of the leaves are black.

Leaf edge stiffness

This ends up controlling the side-to-side wiggle of the plant parts. The general rule is that the side edges of leaves should be full red (the most wiggle), while the center line of the leaves (and all of the branches and the trunk) should be black (no wiggle). This does deform the leaves a bit, but the effect is subtle. The vertex color channel looks like the following (it’s not perfect because this is the first time I’ve used Blender for something like this):

Per-leaf phase

This one is probably the easiest to understand. Each leaf, or “cohesive unit” (my term) on the plant should share the same value among all its vertices, but each leaf or cohesive unit should have a different random value. The random values should be well-distributed across the plant. This is just used to control the phase of the up-and-down motion and ensures the leaves are out-of-sync with each other, ensuring a more natural chaotic look. I didn’t do a perfect job here (hopefully I’ll get better at Blender), you can see some of the leaves have different values among them (look at the one at the top):

Considering what we’ve just seen, the parts of the plant that don’t participate in detail bending at all should have their vertex color set to pure blue RGB(0, 0, 255).

Putting it all together

Let’s summarize the different types of motion:

  1. Main bending. This applies equally to the whole plant, pushing it in the direction of the wind.
  2. Detail bending up-and-down. Amplitude is controlled by the blue vertex channel, and each leaf will typically have a different phase, controlled by the green channel. Sometimes referred to as “branch bending”
  3. Detail bending side-to-side. The wiggle motion, controlled by the red vertex channel. The blue channel also affects the overall amplitude. Sometimes referred to as “leaf bending”.

When everything is put together, the final wind animation looks something like following (this shows scenes with and without detail bending):

Note that I didn’t do a perfect job of coloring the vertices in Blender, and the plant model I used isn’t ideally set up for this wind animation (it lacks sufficient leaf tessellation). Also I didn’t spend a lot of time tweaking the animation parameters in the shader. So it is likely you could improve on this and make an even more convincing wind animation.

Now let’s get to the specifics: the vertex shader implementation for this. I’ll provide more heavily commented versions than the code snippets in the Crytek article.

This is the main bending:

// This bends the entire plant in the direction of the wind.
// vPos:		The world position of the plant *relative* to the base of the plant.
//			(That means we assume the base is at (0, 0, 0). Ensure this before calling this function).
// vWind:		The current direction and strength of the wind.
// fBendScale:	How much this plant is affected by the wind.
void ApplyMainBending(inout float3 vPos, float2 vWind, float fBendScale)
{
	// Calculate the length from the ground, since we'll need it.
	float fLength = length(vPos);
	// Bend factor - Wind variation is done on the CPU.
	float fBF = vPos.y * fBendScale;
	// Smooth bending factor and increase its nearby height limit.
	fBF += 1.0;
	fBF *= fBF;
	fBF = fBF * fBF - fBF;
	// Displace position
	float3 vNewPos = vPos;
	vNewPos.xz += vWind.xy * fBF;
	// Rescale - this keeps the plant parts from "stretching" by shortening the y (height) while
	// they move about the xz.
	vPos.xyz = normalize(vNewPos.xyz)* fLength;
}

And the detail bending:

// The suggested frequencies from the Crytek paper
// The side-to-side motion has a much higher frequency than the up-and-down.
#define SIDE_TO_SIDE_FREQ1 1.975
#define SIDE_TO_SIDE_FREQ2 0.793
#define UP_AND_DOWN_FREQ1 0.375
#define UP_AND_DOWN_FREQ2 0.193

// This provides "chaotic" motion for leaves and branches (the entire plant, really)
void ApplyDetailBending(
	inout float3 vPos,		// The final world position of the vertex being modified
	float3 vNormal,			// The world normal for this vertex
	float3 objectPosition,	        // The world position of the plant instance (same for all vertices)
	float fDetailPhase,		// Optional phase for side-to-side. This is used to vary the phase for side-to-side motion
	float fBranchPhase,		// The green vertex channel per Crytek's convention
	float fTime,			// Ever-increasing time value (e.g. seconds ellapsed)
	float fEdgeAtten,		// "Leaf stiffness", red vertex channel per Crytek's convention
	float fBranchAtten,		// "Overall stiffness", *inverse* of blue channel per Crytek's convention
	float fBranchAmp,		// Controls how much up and down
	float fSpeed,			// Controls how quickly the leaf oscillates
	float fDetailFreq,		// Same thing as fSpeed (they could really be combined, but I suspect
					// this could be used to let you additionally control the speed per vertex).
	float fDetailAmp)		// Controls how much back and forth
{
	// Phases (object, vertex, branch)
	// fObjPhase: This ensures phase is different for different plant instances, but it should be
	// the same value for all vertices of the same plant.
	float fObjPhase = dot(objectPosition.xyz, 1);  

	// In this sample fBranchPhase is always zero, but if you want you could somehow supply a
	// different phase for each branch.
	fBranchPhase += fObjPhase;

	// Detail phase is (in this sample) controlled by the GREEN vertex color. In your modelling program,
	// assign the same "random" phase color to each vertex in a single leaf/branch so that the whole leaf/branch
	// moves together.
	float fVtxPhase = dot(vPos.xyz, fDetailPhase + fBranchPhase);  

	float2 vWavesIn = fTime + float2(fVtxPhase, fBranchPhase );
	float4 vWaves = (frac( vWavesIn.xxyy *
					   float4(SIDE_TO_SIDE_FREQ1, SIDE_TO_SIDE_FREQ2, UP_AND_DOWN_FREQ1, UP_AND_DOWN_FREQ2) ) *
					   2.0 - 1.0 ) * fSpeed * fDetailFreq;
	vWaves = SmoothTriangleWave( vWaves );
	float2 vWavesSum = vWaves.xz + vWaves.yw;  

	// -fBranchAtten is how restricted this vertex of the leaf/branch is. e.g. close to the stem
	//  it should be 0 (maximum stiffness). At the far outer edge it might be 1.
	//  In this sample, this is controlled by the blue vertex color.
	// -fEdgeAtten controls movement in the plane of the leaf/branch. It is controlled by the
	//  red vertex color in this sample. It is supposed to represent "leaf stiffness". Generally, it
	//  should be 0 in the middle of the leaf (maximum stiffness), and 1 on the outer edges.
	// -Note that this is different from the Crytek code, in that we use vPos.xzy instead of vPos.xyz,
	//  because I treat y as the up-and-down direction.
	vPos.xzy += vWavesSum.xxy * float3(fEdgeAtten * fDetailAmp *
							vNormal.xz, fBranchAtten * fBranchAmp

        vPos.xyz += vWavesSum.x * float3(fEdgeAtten * fDetailAmp * vNormal.xyz);
        vPos.y += vWavesSum.y * fBranchAtten * fBranchAmp;
);
}

These are nice snippets, but how are they used? Here is the rest of the vertex shader function:

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
	VertexShaderOutput output;

	float4 worldPosition = mul(input.Position, World);
	float3 vPos = worldPosition.xyz;

	// Grab the object position from the translation part of the world matrix.
	// We need the object position because we want to temporarily translate the vertex
	// back to the position it would be if the plant were at (0, 0, 0).
	// This is necessary for the main bending to work.
	float3 objectPosition = float3(World._m30, World._m31, World._m32);
	vPos -= objectPosition;	// Reset the vertex to base-zero
	ApplyMainBending(vPos, WindSpeed, BendScale);
	vPos += objectPosition;	// Restore it.
	output.Normal = normalize(mul(input.Normal, World));

	float windStrength = length(WindSpeed);

	ApplyDetailBending(
		vPos,
		output.Normal,
		objectPosition,
		0,					// Leaf phase - not used in this scenario, but would allow for variation in side-to-side motion
		input.Color.g,		// Branch phase - should be the same for all verts in a leaf/branch.
		Time,
		input.Color.r,		// edge attenuation, leaf stiffness
		1 - input.Color.b,  // branch attenuation. High values close to stem, low values furthest from stem.
							// For some reason, Crysis uses solid blue for non-moving, and black for most movement.
							// So we invert the blue value here.
		BranchAmplitude * windStrength, // branch amplitude. Play with this until it looks good.
		2,					// Speed. Play with this until it looks good.
		1,					// Detail frequency. Keep this at 1 unless you want to have different per-leaf frequency
		DetailAmplitude * windStrength	// Detail amplitude. Play with this until it looks good.
		);

	float4 viewPosition = mul(float4(vPos, worldPosition.w), View);
	output.Position = mul(viewPosition, Projection);
	output.TexCoord = input.TexCoord;
	output.Color = input.Color;
	if (InvertNormal)
	{
		output.Normal = -output.Normal;
	}
	return output;
}

A few more notes:

  • This implementation assumes the y-axis is up. The Crytek implementation assumed the z-axis was up.
  • The sine wave approximation used (SmoothTriangleWave) doesn’t have a distinct phase, which means we can’t loop the time value back to zero periodically to avoid float point accuracy issues. Doing so would cause a noticeable “jump” in the plant’s motion. So eventually, the time value will get very large and our calculations will begin to be off. In real code, we could probably take advantage of times when the wind is weak to reset the time to zero to make the “jump” not so noticeable.
  • The optimized compiled vertex shader ends up using 62 instruction slots.
  • There are a number of magic constants here BEND_SCALE, BRANCH_AMPLITUDE, DETAIL_AMPLITUDE, and the general values of the wind speed. You’ll need to adjust these to your situation.
  • The vertices are moved, but the normals are not transformed. This means the lighting stays the same even though the leaves wiggle. This would probably be a bunch of work to fix (I’m not sure what CryENGINE does), so wind animations look best if they are subtle.
Here is the visual studio solution with the full implementation, based on the XNA picking sample (look for the file VegetationWind.zip).

I don’t have the rights to distribute the source files for the plant model I use, so I create it in code. The original can be purchased from  turbosquid here.

Advertisements

14 comments on “Wind animations for vegetation

  1. […] the modelling program I’m using to draw the vertex colors used for wind animations (see last post), doesn’t allow you to draw to a specific component of a vertex channel. This makes it […]

  2. Hi,

    I was wondering what format did you export to from blender that saves all 3 of the vertex color channels you’re using for the object? Also how do you show the channels combined like that in blender? Any help is appreciated 🙂

    • I export as fbx, since that’s the format that XNA likes most. It should include whatever vertex color channels you added – you don’t need to do anything special. I assume that’s true for most formats.

      As for the picture with the combined vertex colors, that was done in Photoshop by combining the three “single color” screenshots from Blender :-).

  3. […] expensive and/or hard to come by. Plus, in order to have them behave nicely with my wind, I have to tediously paint the vertex colors in a modelling app to identify which parts of the vegetation are […]

  4. I should note that, a year later, I’ve found a bug in the implementation. I’ve fixed it in the blog post above, but not in the visual studio solution,

    At the end of the ApplyDetailBending, these is this line:

    vPos.xzy += vWavesSum.xxy * float3(fEdgeAtten * fDetailAmp * vNormal.xz, fBranchAtten * fBranchAmp);

    It needs to be replaced with:

    vPos.xyz += vWavesSum.x * float3(fEdgeAtten * fDetailAmp * vNormal.xyz);
    vPos.y += vWavesSum.y * fBranchAtten * fBranchAmp;

    This makes it so that for the “fluttering” at the edges of the leaves, the leaf is displaced along the worldspace normal (instead of the xz plane). The glitch that would occur was that vertical leaves would have much more flutter than horizontal leaves.

  5. Neat code. I’ve just ported this to Unity and works like a charm. Thank you.

  6. I was reading GPU gems 3 today about the wind and came across your post. I know its old, but I would love to know if there is an easy way, logical way, to create the color channel from just the vertices or do you have to manually do it?

  7. Nice work!I’m trying to reproduce your work recently.I modify the shader,and remove the code of DetailMove part,but after that my tree just stop moving.Is MainBending part must work with DetailMove?

    • Looks like it should work fine without the detail bending. The vPos variable in the shader should be modified by ApplyMainBending (since it’s in/out), and that’s what’s used to determine the final vertex position.

      Check the shader disassembly to ensure that logic hasn’t been compiled out for some reason. And make sure the plant’s up vector is the y-axis, WindSpeed is a good value, and BendScale is appropriate for the size of the plant.

  8. I am trying this shader in Unity.I find the matrix translation part confused.Could you please explain the Unity Cg shader equivalent of these matrice in your codes:

    World = unity_ObjectToWorld ?
    View = UNITY_MATRIX_MV ?
    Projection = UNITY_MATRIX_VP ?

    I surmise the connection above,is it right?

    And about the matrix multipliy,I find it is common in Unity shader to place the matrix before the value being transformed.

    So should the order be :

    Yours: float4 worldPosition = mul(input.Position, World);
    I guess in Unity: float4 worldPosition = mul(unity_ObjectToWorld ,input.Position);

    Yours : output.Normal = normalize(mul(input.Normal, World));
    I guess in Unity : I don’t know.

    Yours float4 viewPosition = mul(float4(vPos, worldPosition.w), View);
    I guess in Unity: float4 viewPosition = mul(UNITY_MATRIX_MV ,float4(vPos, worldPosition.w));

    Yours: output.Position = mul(viewPosition, Projection);
    I guess in Unity: output.Position = mul(UNITY_MATRIX_VP ,viewPosition);

    I know I didn’t did it right,because I result is wrong.Could you please correct my mistakes?
    Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Space Quest Historian

Adventure game blogs, Let's Plays, live streams, and more

Harebrained Schemes

Developer's blog for IceFall Games

kosmonaut's blog

3d GFX and more

Halogenica

Turn up the rez!

bitsquid: development blog

Developer's blog for IceFall Games

Game Development by Sean

Developer's blog for IceFall Games

Lost Garden

Developer's blog for IceFall Games

Memories

Developer's blog for IceFall Games

Casey's Blog

Developer's blog for IceFall Games

Blog

Developer's blog for IceFall Games

Rendering Evolution

Developer's blog for IceFall Games

Simon schreibt.

Developer's blog for IceFall Games

Dev & Techno-phage

Do Computers Dream of Electric Developper?

- Woolfe -

Developer's blog for IceFall Games

Ferrara Fabio

Game & Application Developer, 3D Animator, Composer.

Clone of Duty: Stonehenge

First Person Shooter coming soon to the XBOX 360

Low Tide Productions

Games and other artsy stuff...

BadCorporateLogo

Just another WordPress.com site

Sipty's Writing

Take a look inside the mind of a game developer.

Jonas Kyratzes

Writer, game designer, filmmaker.

%d bloggers like this: