‘At Crytek, one of our primary goals for Crysis was to define a new standard for computer game graphics. For our latest game, we have developed several new technologies that together comprise CryENGINE 2. A key feature of Crysis among these technologies is vegetation rendering, which is composed of several parts, including procedural breaking and physics interaction, shading, procedural animation, and distant sprite generation, among others.’
‘Vegetation in games has always been mainly static, with some sort of simple bending to give the illusion of wind. Our game scenes can have thousands of different vegetations, but still we pushed the envelope further by making vegetation react to global and local wind sources, and we bend not only the vegetation but also the leaves, in detail, with all computations procedurally and efficiently done on the GPU.’
There are two parts in creating vegetation animation. (1) the main bending, which animates the entire vegetation along the wind direction; and (2) the detail bending, which animates the leaves. A wind vector is computed per-instance, in world space, by summing up the wind forces affecting the instance. A wind area can be a directional or an omnidirectional wind source. You compute this sum in a very similar way to light sources affecting a single point, taking direction and attenuation into account. Also, each instance has its own stiffness, and the wind strength gets dampened over time when the instance stops being affected by any wind sources.
Designers can place wind sources at specific locations, attach them to an entity, and attach then to particle systems as well. With this approach, a large number of wind sources can be obtained while keeping the per-vertex cost constant.
You generate the main bending by using the xy components of the wind vector, which gives us the wind direction and its strength, using the vegetation mesh height as a scale to apply a directional vertex deformation. Note that care must be taken to limit the amount of deformation; otherwise, the results will not look believable.
For leaves’ detail bending, we approach things in a similar fashion, but in this case only wind strength is taken into account. Artists paint one RGB color per-vertex, using a common 3D modeling software. This color gives us extra information about the detail bending. As shown in Figure 1, the red channel is used for the stiffness of leaves’ edges, the green channel for per-leaf phase variation, and the blue channel for the overall stiffness of the leaves. The alpha channel is used for precomputed ambient occlusion.
Figure 1
Implementation Details
One of our main objectives was to achieve an intuitive system, as simple as possible for an artist or designer to use. Therefore, designers’ main inputs are wind direction and speed. If required in particular cases, artists can still override default settings, for leaves’ wind speed (frequency), edges, and per-leaf amplitude, to make the vegetation animation more visually pleasing.
Functions Used for Wave Generation
float4 SmoothCurve( float4 x ) {
return x * x *( 3.0 – 2.0 * x );
}
float4 TriangleWave( float4 x ) {
return abs( frac( x + 0.5 ) * 2.0 – 1.0 );
}
float4 SmoothTriangleWave( float4 x ) {
return SmoothCurve( TriangleWave( x ) );
}
Detail Bending
Leaves’ bending, as we have mentioned, is done by deforming the edges, using the vertex color’s red channel for controlling edge stiffness control. This deformation is done along the world-space vertex normal xy direction.
Finally, we come to per-leaf bending, which we produce simply by deforming up and down along the z-axis, using the blue channel for leaf stiffness.
The vertex color’s green channel contains a per-leaf phase variation, which we use to give each individual leaf its own phase, so that every leaf moves differently. Listing 16-2 shows the code for our detail-bending approach.
Leaves’ shape and the number of vertices can vary depending on the vegetation type. For example, we model an entire leaf for a palm tree, which gives us the ability to nicely animate it in a very controllable way. For bigger trees, however, we model the leaves as several planes; we accept that total control is not possible, because several leaves can be on a relatively low-polygon-count plane placed as a texture. Still, we use the same approach for all different cases with good results.
Main Bending
We accomplish the main vegetation bending by displacing vertices’ xy positions along the wind direction, using normalized height to scale the amount of deformation. Performing a simple displace is not enough, because we need to restrict vertex movement to minimize deformation artifacts. We achieve this by computing the vertex’s distance to the mesh center and using this distance for rescaling the new displaced normalized position.
This process results in a spherical limited movement, which is enough for our vegetation case, because it is a single vegetation object. The amount of bending deformation needs to be carefully tweaked as well: too much bending will ruin the illusion. Listing 16-3 shows our implementation. Figure 2 shows some examples produced using this main bending technique. This process results in a spherical limited movement, which is enough for our vegetation case, because it is a single vegetation object. The amount of bending deformation needs to be carefully tweaked as well: too much bending will ruin the illusion. Listing 16-3 shows our implementation. Figure 2 shows some examples produced using this main bending technique.
Figure 2 Bending on Different Types of Vegetation
The Implementation of Our Detail-Bending Approach
// Phases (object, vertex, branch)
float fObjPhase = dot(worldPos.xyz, 1);
fBranchPhase += fObjPhase;
float fVtxPhase = dot(vPos.xyz, fDetailPhase + fBranchPhase);
// x is used for edges; y is used for branches
float2 vWavesIn = fTime + float2(fVtxPhase, fBranchPhase );
// 1.975, 0.793, 0.375, 0.193 are good frequencies
float4 vWaves = (frac( vWavesIn.xxyy *
float4(1.975, 0.793, 0.375, 0.193) ) *
2.0 – 1.0 ) * fSpeed * fDetailFreq;
vWaves = SmoothTriangleWave( vWaves );
float2 vWavesSum = vWaves.xz + vWaves.yw;
// Edge (xy) and branch bending (z)
vPos.xyz += vWavesSum.xxy * float3(fEdgeAtten * fDetailAmp *
vNormal.xy, fBranchAtten * fBranchAmp);
The Main Bending Implementation
// Bend factor – Wind variation is done on the CPU.
float fBF = vPos.z * 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.xy += vWind.xy * fBF;
// Rescale
vPos.xyz = normalize(vNewPos.xyz)* fLength;
Reference
Game Developer Zone, GPU Gems 3 Webpage
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch16.html