User Tools

Site Tools


Example Wind Shader

SpeedTree's wind system can be used in either a full SDK integration or a light/partial integration. The example code below is a DirectX 11 example of using the SDK shader wind functions. It also shows how to unpack the wind data if it was packed with the standard 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.

// this header sets up a series of macros that help us write a single shader that
// covers dx11, dx12, opengl, ps5, and xbox series x
#include "SpeedTree/Core/UnifyHlslAndGlsl.h"
 
// set up the example constant buffers used by the SDK and reference application;
// this source file is meant to be compiled into both C++ and shader source;
// #define ST_WIND_CODE_GPU for shaders
#define ST_WIND_CODE_GPU
#include "SpeedTree/Core/SdkConstantBuffers.h"
 
// the speedtree wind system supports several modes (e.g. vfx, games, legacy) that
// are all defined in WindShaders_All.h; define ST_WIND_SHADER_MODE_SDK to activate
// the games 8 sdk mode
#define ST_WIND_SHADER_MODE_SDK 1
#include "SpeedTree/Core/WindShaders_All.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;
 
 
///////////////////////////////////////////////////////////////////////
//	main
 
void main(// mesh input
              float4 vPos          : POSITION,
              float3 vNormal       : TEXCOORD0,
              float3 vBinormal     : TEXCOORD1,
              float3 vTangent      : TEXCOORD2,
              float4 vWindData     : TEXCOORD3,
 
          // per-instance input
              float4 vInstance0    : TEXCOORD4, // xyz = position, w = scalar
              float3 vInstance1    : TEXCOORD5, // xyz = up vector
              float3 vInstance2    : TEXCOORD6, // 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
    SWindInputSdk 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;
 
    // unpack vWindData (packing is same used by
    // on "Standard" vertex packer in the Modeler):
    //
    // .x = packed wind branch direction
    // .y = wind weight
    // .z = wind ripple
    // .w = wind_branch_offset
    sWindInput.m_sVertex.m_vWindBranchDir = UnpackNormalFibonacci(float(vWindData.x));
    sWindInput.m_sVertex.m_fWindBranchWeight = vWindData.y;
    sWindInput.m_sVertex.m_fWindRippleWeight = vWindData.z;
    sWindInput.m_sVertex.m_vBranchNoiseOffset = UnpackInteger3(vWindData.w * 255, float3(6, 6, 7)) *
                                                (vTreeExtentsMax - vTreeExtentsMin);
 
    // 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
 
    // options
    sWindInput.m_sOptions.m_bDoShared = true;
    sWindInput.m_sOptions.m_bDoBranch = true;
    sWindInput.m_sOptions.m_bDoRipple = true;
    sWindInput.m_sOptions.m_bDoShimmer = true;
    sWindInput.m_sOptions.m_bLodFade = false;
    sWindInput.m_sOptions.m_bIsGrass = false;
    sWindInput.m_sOptions.m_fWindIndependence = 0.5;
 
    // cpu-side state
    sWindInput.m_sState = u_sBaseTree.m_sWind;
 
    // call main wind function
    WindSdk(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 example_vs.hlsl.