Skip to content

Instantly share code, notes, and snippets.

@nickludlam
Last active March 18, 2023 10:44
Show Gist options
  • Save nickludlam/02cb409e983a642b8dc1d2175f5aa2e9 to your computer and use it in GitHub Desktop.
Save nickludlam/02cb409e983a642b8dc1d2175f5aa2e9 to your computer and use it in GitHub Desktop.
#ifndef FOLIAGE_WIND_INCLUDED
#define FOLIAGE_WIND_INCLUDED
// This is the Unity Shadergraph port of https://mtnphil.wordpress.com/2011/10/18/wind-animations-for-vegetation/
#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
half4 SmoothCurve( half4 x )
{
return x * x *( 3.0 - 2.0 * x );
}
half4 TriangleWave( half4 x )
{
return abs( frac( x + 0.5 ) * 2.0 - 1.0 );
}
half4 SmoothTriangleWave( half4 x )
{
return SmoothCurve( TriangleWave( x ) );
}
// 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 vPosWS, half2 vWind, half fBendScale)
{
// Calculate the length from the ground, since we'll need it.
float fLength = length(vPosWS);
// Bend factor - Wind variation is done on the CPU.
float fBF = vPosWS.y * fBendScale;
// Smooth bending factor and increase its nearby height limit.
fBF += 1.0;
fBF *= fBF;
fBF = fBF * fBF - fBF;
// Displace position
float3 vNewPos = vPosWS;
vNewPos.xz += vWind.xy * fBF;
// Rescale - this keeps the plant parts from "stretching" by shortening the y (height) while
// they move about the xz.
vPosWS.xyz = normalize(vNewPos.xyz)* fLength;
}
// This provides "chaotic" motion for leaves and branches (the entire plant, really)
void ApplyDetailBending(
inout float3 vPosWS, // The final world position of the vertex being modified
float3 vNormalWS, // The world normal for this vertex
float3 objectPosition, // The world position of the plant instance (same for all vertices)
half fDetailPhase, // Optional phase for side-to-side. This is used to vary the phase for side-to-side motion
half fBranchPhase, // The green vertex channel per Crytek's convention
float fTime, // Ever-increasing time value (e.g. seconds ellapsed)
half fEdgeAtten, // "Leaf stiffness", red vertex channel per Crytek's convention
half fBranchAtten, // "Overall stiffness", *inverse* of blue channel per Crytek's convention
half fBranchAmp, // Controls how much up and down
half fSpeed, // Controls how quickly the leaf oscillates
half 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).
half 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(vPosWS.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 vPosWS.xzy instead of vPosWS.xyz,
// because I treat y as the up-and-down direction.
vPosWS.xyz += vWavesSum.x * float3(fEdgeAtten * fDetailAmp * vNormalWS.xyz);
vPosWS.y += vWavesSum.y * fBranchAtten * fBranchAmp;
}
void CalculateFoliageWindDeformation_float(float3 positionWS, half3 normalWS, half4 vertexColor, half2 windSpeed, half bendScale, half branchAmplitude, half detailAmplitude, out float3 positionWSWind)
{
float3 vPosWS = positionWS;
float3 objectPositionWS = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)); // find the offset to the pivot/origin
vPosWS -= objectPositionWS;// Reset the vertex to base-zero
ApplyMainBending(vPosWS, windSpeed, bendScale);
vPosWS += objectPositionWS; // Restore it.
half windStrength = length(windSpeed);
ApplyDetailBending(
vPosWS,
normalWS,
objectPositionWS,
0, // Leaf phase - not used in this scenario, but would allow for variation in side-to-side motion
vertexColor.g, // Branch phase - should be the same for all verts in a leaf/branch.
_Time.x,
vertexColor.r, // edge attenuation, leaf stiffness
vertexColor.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.
);
positionWSWind = vPosWS.xyz;
}
#endif
@nickludlam
Copy link
Author

This is a basic implementation of the Crytek wind deformation system for foliage, as outlined in GPU Gems 3. I use this in combination with trees from http://www.evolved-software.com/treeit/treeit and the code itself is a port of some shader code from https://mtnphil.wordpress.com/2011/10/18/wind-animations-for-vegetation/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment