Example wind shader
SpeedTree's wind system can be used in either a full Runtime SDK integration or a light/partial integration. The example code below is a DirectX 11 example of using the Runtime SDK shader wind functions. It also shows how to unpack the wind data if it was packed with the standard Runtime SDK/Standard games vertex packers in the SpeedTree Modeler. These is the approach we used for the reference application, but developers can override any or all of it as needed.
///////////////////////////////////////////////////////////////////////
// Main SpeedTree wind system for shaders and C++
#include "SpeedTree/Core/SpeedTreeWind.h"
///////////////////////////////////////////////////////////////////////
// Utility_UnpackNormalFibonacci
//
// Used for unpacking normals packed in our vertex packing example
// using our Lua function pack_normal_fibonacci().
float3 UnpackNormalFibonacci(float fPacked)
{
float fZ = 0.99609375 - 0.0078125 * fPacked;
float fRadius = sqrt(1.0 - fZ * fZ);
float fTheta = fPacked * 2.39996322973;
return float3(cos(fTheta) * fRadius, sin(fTheta) * fRadius, fZ);
}
///////////////////////////////////////////////////////////////////////
// Utility_UnpackInteger3
//
// This is used for unpacking integers in the speedtree vertex packing
// example using our Lua function pack_integer().
float3 UnpackInteger3(float fValue, float3 vCoef)
{
float3 vReturn;
float fXY = vCoef.x * vCoef.y;
vReturn.z = floor(fValue / fXY);
fValue -= vReturn.z * fXY;
vReturn.y = floor(fValue / vCoef.x);
fValue -= vReturn.y * vCoef.x;
vReturn.x = fValue;
return vReturn / (vCoef - float3(1,1,1));
}
// example shader constants
//
// in our reference application, these values are made available in the
// shader constant "u_sBaseTree"
float3 vTreeExtentsMin;
float3 vTreeExtentsMax;
// in C++, the SWindStateSdk value is given by the CWindStateMgr class defined
// in SpeedTreeWind.h. Specifically, CWindStateMgr::GetShaderConstants() -- see
// the SDK reference application and online docs for details.
SWindStateRuntimeSdk sCpuWindState;
///////////////////////////////////////////////////////////////////////
// main
void main(// mesh input
float4 vPos : POSITION,
float3 vNormal : TEXCOORD0,
float3 vBinormal : TEXCOORD1,
float3 vTangent : TEXCOORD2,
float4 vWindBranch1 : TEXCOORD3, // x=weight, y=dir, z=offset, w=ripple
float3 vWindBranch2 : TEXCOORD4, // x=weight, y=dir, z=offset
// per-instance input
float4 vInstance0 : TEXCOORD5, // xyz=position, w=scalar
float3 vInstance1 : TEXCOORD6, // xyz=up vector
float3 vInstance2 : TEXCOORD7, // xyz=right vector
// output
out float4 vOutputPos : SV_POSITION,
out float3 vOutputNormal : TEXCOORD0)
{
// skipping several routine vs items for brevity, including orienting pos
// and normals based on instance orientation
// pass data into speedtree's game wind system -- SWindInputSdk has
// four sections:
// - m_sVertex: all per-vertex data
// - m_sInstance: data about the pos and orientation of this instance
// - m_sOptions: compile-time flags for which wind effects to enable
// - m_sState: values from the CWindStateMgr class running on CPU
SWindInputRuntimeSdk sWindInput;
// vertex
sWindInput.m_sVertex.m_vPosition = vPos.xyz;
sWindInput.m_sVertex.m_vNormal = vNormal;
sWindInput.m_sVertex.m_vBinormal = vBinormal;
sWindInput.m_sVertex.m_vTangent = vTangent;
// wind data
sWindInput.m_sVertex.m_fRippleWeight = vWindBranch1.w;
sWindInput.m_sVertex.m_sBranch1.m_fWeight = vWindBranch1.x;
sWindInput.m_sVertex.m_sBranch1.m_vDir = UnpackNormalFibonacci(float(vWindBranch1.y));;
sWindInput.m_sVertex.m_sBranch1.m_vNoiseOffset = UnpackInteger3(vWindBranch1.z * 255, float3(9, 9, 3)) *
(vTreeExtentsMax - vTreeExtentsMin);
sWindInput.m_sVertex.m_sBranch2.m_fWeight = vWindBranch2.x;
sWindInput.m_sVertex.m_sBranch2.m_vDir = UnpackNormalFibonacci(float(vWindBranch2.y));;
sWindInput.m_sVertex.m_sBranch2.m_vNoiseOffset = UnpackInteger3(vWindBranch2.z * 255, float3(9, 9, 3)) *
(vTreeExtentsMax - vTreeExtentsMin);
// wind toggles
sWindInput.m_sOptions.m_bDoShared = true;
sWindInput.m_sOptions.m_bDoBranch1 = true;
sWindInput.m_sOptions.m_bDoBranch2 = true;
sWindInput.m_sOptions.m_bDoRipple = true;
sWindInput.m_sOptions.m_bDoShimmer = true;
sWindInput.m_sOptions.m_fWindIndependence = 0.5;
// instance
sWindInput.m_sInstance.m_vPosition = vInstance0.xyz;
sWindInput.m_sInstance.m_fScalar = vInstance0.w;
sWindInput.m_sInstance.m_vOrientationUp = vInstance1;
sWindInput.m_sInstance.m_vOrientationRight = vInstance2;
sWindInput.m_sInstance.m_fLodValue = 0.0; // unused
sWindInput.m_sInstance.m_fLodTransition = 0.0; // unused
// cpu-side state
sWindInput.m_sState = sCpuWindState;
// call main wind function
WindRuntimeSdk(sWindInput);
// WindSdk modifies position and can modify the normal (if m_bDoShimmer is enabled)
vOutputPos = float4(sWindInput.m_sVertex.m_vPosition, 1.0);
vOutputNormal = sWindInput.m_sVertex.m_vNormal;
// position projection skipped for brevity...
}
The vertex shader above can be compiled using the following HLSL compiler fxc.exe compilation command for DirectX 11, assuming a working directory of [SDK]/SampleForest/Shaders/ and a source filename of speedtree_wind_example_vs.hlsl.
fxc.exe /Tvs_5_0 /I "..\..\..\Include" "speedtree_wind_example_vs.hlsl" /O2 /nologo /DST_DIRECTX11=1