811 lines
25 KiB
HLSL
811 lines
25 KiB
HLSL
|
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
|
||
|
|
||
|
#ifndef SPEEDTREE_WIND_INCLUDED
|
||
|
#define SPEEDTREE_WIND_INCLUDED
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Wind Info
|
||
|
|
||
|
CBUFFER_START(SpeedTreeWind)
|
||
|
float4 _ST_WindVector;
|
||
|
float4 _ST_WindGlobal;
|
||
|
float4 _ST_WindBranch;
|
||
|
float4 _ST_WindBranchTwitch;
|
||
|
float4 _ST_WindBranchWhip;
|
||
|
float4 _ST_WindBranchAnchor;
|
||
|
float4 _ST_WindBranchAdherences;
|
||
|
float4 _ST_WindTurbulences;
|
||
|
float4 _ST_WindLeaf1Ripple;
|
||
|
float4 _ST_WindLeaf1Tumble;
|
||
|
float4 _ST_WindLeaf1Twitch;
|
||
|
float4 _ST_WindLeaf2Ripple;
|
||
|
float4 _ST_WindLeaf2Tumble;
|
||
|
float4 _ST_WindLeaf2Twitch;
|
||
|
float4 _ST_WindFrondRipple;
|
||
|
float4 _ST_WindAnimation;
|
||
|
CBUFFER_END
|
||
|
|
||
|
#define ST_WIND_QUALITY_NONE 0
|
||
|
#define ST_WIND_QUALITY_FASTEST 1
|
||
|
#define ST_WIND_QUALITY_FAST 2
|
||
|
#define ST_WIND_QUALITY_BETTER 3
|
||
|
#define ST_WIND_QUALITY_BEST 4
|
||
|
#define ST_WIND_QUALITY_PALM 5
|
||
|
|
||
|
#define ST_GEOM_TYPE_BRANCH 0
|
||
|
#define ST_GEOM_TYPE_FROND 1
|
||
|
#define ST_GEOM_TYPE_LEAF 2
|
||
|
#define ST_GEOM_TYPE_FACINGLEAF 3
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// UnpackNormalFromFloat
|
||
|
|
||
|
float3 UnpackNormalFromFloat(float fValue)
|
||
|
{
|
||
|
float3 vDecodeKey = float3(16.0, 1.0, 0.0625);
|
||
|
|
||
|
// decode into [0,1] range
|
||
|
float3 vDecodedValue = frac(fValue / vDecodeKey);
|
||
|
|
||
|
// move back into [-1,1] range & normalize
|
||
|
return (vDecodedValue * 2.0 - 1.0);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// CubicSmooth
|
||
|
|
||
|
float4 CubicSmooth(float4 vData)
|
||
|
{
|
||
|
return vData * vData * (3.0 - 2.0 * vData);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// TriangleWave
|
||
|
|
||
|
float4 TriangleWave(float4 vData)
|
||
|
{
|
||
|
return abs((frac(vData + 0.5) * 2.0) - 1.0);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// TrigApproximate
|
||
|
|
||
|
float4 TrigApproximate(float4 vData)
|
||
|
{
|
||
|
return (CubicSmooth(TriangleWave(vData)) - 0.5) * 2.0;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// RotationMatrix
|
||
|
//
|
||
|
// Constructs an arbitrary axis rotation matrix
|
||
|
|
||
|
float3x3 RotationMatrix(float3 vAxis, float fAngle)
|
||
|
{
|
||
|
// compute sin/cos of fAngle
|
||
|
float2 vSinCos;
|
||
|
#ifdef OPENGL
|
||
|
vSinCos.x = sin(fAngle);
|
||
|
vSinCos.y = cos(fAngle);
|
||
|
#else
|
||
|
sincos(fAngle, vSinCos.x, vSinCos.y);
|
||
|
#endif
|
||
|
|
||
|
const float c = vSinCos.y;
|
||
|
const float s = vSinCos.x;
|
||
|
const float t = 1.0 - c;
|
||
|
const float x = vAxis.x;
|
||
|
const float y = vAxis.y;
|
||
|
const float z = vAxis.z;
|
||
|
|
||
|
return float3x3(t * x * x + c, t * x * y - s * z, t * x * z + s * y,
|
||
|
t * x * y + s * z, t * y * y + c, t * y * z - s * x,
|
||
|
t * x * z - s * y, t * y * z + s * x, t * z * z + c);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// mul_float3x3_float3x3
|
||
|
|
||
|
float3x3 mul_float3x3_float3x3(float3x3 mMatrixA, float3x3 mMatrixB)
|
||
|
{
|
||
|
return mul(mMatrixA, mMatrixB);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// mul_float3x3_float3
|
||
|
|
||
|
float3 mul_float3x3_float3(float3x3 mMatrix, float3 vVector)
|
||
|
{
|
||
|
return mul(mMatrix, vVector);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// cross()'s parameters are backwards in GLSL
|
||
|
|
||
|
#define wind_cross(a, b) cross((a), (b))
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Roll
|
||
|
|
||
|
float Roll(float fCurrent,
|
||
|
float fMaxScale,
|
||
|
float fMinScale,
|
||
|
float fSpeed,
|
||
|
float fRipple,
|
||
|
float3 vPos,
|
||
|
float fTime,
|
||
|
float3 vRotatedWindVector)
|
||
|
{
|
||
|
float fWindAngle = dot(vPos, -vRotatedWindVector) * fRipple;
|
||
|
float fAdjust = TrigApproximate(float4(fWindAngle + fTime * fSpeed, 0.0, 0.0, 0.0)).x;
|
||
|
fAdjust = (fAdjust + 1.0) * 0.5;
|
||
|
|
||
|
return lerp(fCurrent * fMinScale, fCurrent * fMaxScale, fAdjust);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Twitch
|
||
|
|
||
|
float Twitch(float3 vPos, float fAmount, float fSharpness, float fTime)
|
||
|
{
|
||
|
const float c_fTwitchFudge = 0.87;
|
||
|
float4 vOscillations = TrigApproximate(float4(fTime + (vPos.x + vPos.z), c_fTwitchFudge * fTime + vPos.y, 0.0, 0.0));
|
||
|
|
||
|
//float fTwitch = sin(fFreq1 * fTime + (vPos.x + vPos.z)) * cos(fFreq2 * fTime + vPos.y);
|
||
|
float fTwitch = vOscillations.x * vOscillations.y * vOscillations.y;
|
||
|
fTwitch = (fTwitch + 1.0) * 0.5;
|
||
|
|
||
|
return fAmount * pow(saturate(fTwitch), fSharpness);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Oscillate
|
||
|
//
|
||
|
// This function computes an oscillation value and whip value if necessary.
|
||
|
// Whip and oscillation are combined like this to minimize calls to
|
||
|
// TrigApproximate( ) when possible.
|
||
|
|
||
|
float Oscillate(float3 vPos,
|
||
|
float fTime,
|
||
|
float fOffset,
|
||
|
float fWeight,
|
||
|
float fWhip,
|
||
|
bool bWhip,
|
||
|
bool bRoll,
|
||
|
bool bComplex,
|
||
|
float fTwitch,
|
||
|
float fTwitchFreqScale,
|
||
|
inout float4 vOscillations,
|
||
|
float3 vRotatedWindVector)
|
||
|
{
|
||
|
float fOscillation = 1.0;
|
||
|
if (bComplex)
|
||
|
{
|
||
|
if (bWhip)
|
||
|
vOscillations = TrigApproximate(float4(fTime + fOffset, fTime * fTwitchFreqScale + fOffset, fTwitchFreqScale * 0.5 * (fTime + fOffset), fTime + fOffset + (1.0 - fWeight)));
|
||
|
else
|
||
|
vOscillations = TrigApproximate(float4(fTime + fOffset, fTime * fTwitchFreqScale + fOffset, fTwitchFreqScale * 0.5 * (fTime + fOffset), 0.0));
|
||
|
|
||
|
float fFineDetail = vOscillations.x;
|
||
|
float fBroadDetail = vOscillations.y * vOscillations.z;
|
||
|
|
||
|
float fTarget = 1.0;
|
||
|
float fAmount = fBroadDetail;
|
||
|
if (fBroadDetail < 0.0)
|
||
|
{
|
||
|
fTarget = -fTarget;
|
||
|
fAmount = -fAmount;
|
||
|
}
|
||
|
|
||
|
fBroadDetail = lerp(fBroadDetail, fTarget, fAmount);
|
||
|
fBroadDetail = lerp(fBroadDetail, fTarget, fAmount);
|
||
|
|
||
|
fOscillation = fBroadDetail * fTwitch * (1.0 - _ST_WindVector.w) + fFineDetail * (1.0 - fTwitch);
|
||
|
|
||
|
if (bWhip)
|
||
|
fOscillation *= 1.0 + (vOscillations.w * fWhip);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (bWhip)
|
||
|
vOscillations = TrigApproximate(float4(fTime + fOffset, fTime * 0.689 + fOffset, 0.0, fTime + fOffset + (1.0 - fWeight)));
|
||
|
else
|
||
|
vOscillations = TrigApproximate(float4(fTime + fOffset, fTime * 0.689 + fOffset, 0.0, 0.0));
|
||
|
|
||
|
fOscillation = vOscillations.x + vOscillations.y * vOscillations.x;
|
||
|
|
||
|
if (bWhip)
|
||
|
fOscillation *= 1.0 + (vOscillations.w * fWhip);
|
||
|
}
|
||
|
|
||
|
//if (bRoll)
|
||
|
//{
|
||
|
// fOscillation = Roll(fOscillation, _ST_WindRollingBranches.x, _ST_WindRollingBranches.y, _ST_WindRollingBranches.z, _ST_WindRollingBranches.w, vPos.xyz, fTime + fOffset, vRotatedWindVector);
|
||
|
//}
|
||
|
|
||
|
return fOscillation;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Turbulence
|
||
|
|
||
|
float Turbulence(float fTime, float fOffset, float fGlobalTime, float fTurbulence)
|
||
|
{
|
||
|
const float c_fTurbulenceFactor = 0.1;
|
||
|
|
||
|
float4 vOscillations = TrigApproximate(float4(fTime * c_fTurbulenceFactor + fOffset, fGlobalTime * fTurbulence * c_fTurbulenceFactor + fOffset, 0.0, 0.0));
|
||
|
|
||
|
return 1.0 - (vOscillations.x * vOscillations.y * vOscillations.x * vOscillations.y * fTurbulence);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// GlobalWind
|
||
|
//
|
||
|
// This function positions any tree geometry based on their untransformed
|
||
|
// position and 4 wind floats.
|
||
|
|
||
|
float3 GlobalWind(float3 vPos, float3 vInstancePos, bool bPreserveShape, float3 vRotatedWindVector, float time)
|
||
|
{
|
||
|
// WIND_LOD_GLOBAL may be on, but if the global wind effect (WIND_EFFECT_GLOBAL_ST_Wind)
|
||
|
// was disabled for the tree in the Modeler, we should skip it
|
||
|
|
||
|
float fLength = 1.0;
|
||
|
if (bPreserveShape)
|
||
|
fLength = length(vPos.xyz);
|
||
|
|
||
|
// compute how much the height contributes
|
||
|
#ifdef SPEEDTREE_Z_UP
|
||
|
float fAdjust = max(vPos.z - (1.0 / _ST_WindGlobal.z) * 0.25, 0.0) * _ST_WindGlobal.z;
|
||
|
#else
|
||
|
float fAdjust = max(vPos.y - (1.0 / _ST_WindGlobal.z) * 0.25, 0.0) * _ST_WindGlobal.z;
|
||
|
#endif
|
||
|
if (fAdjust != 0.0)
|
||
|
fAdjust = pow(abs(fAdjust), _ST_WindGlobal.w);
|
||
|
|
||
|
// primary oscillation
|
||
|
float4 vOscillations = TrigApproximate(float4(vInstancePos.x + time, vInstancePos.y + time * 0.8, 0.0, 0.0));
|
||
|
float fOsc = vOscillations.x + (vOscillations.y * vOscillations.y);
|
||
|
float fMoveAmount = _ST_WindGlobal.y * fOsc;
|
||
|
|
||
|
// move a minimum amount based on direction adherence
|
||
|
fMoveAmount += _ST_WindBranchAdherences.x / _ST_WindGlobal.z;
|
||
|
|
||
|
// adjust based on how high up the tree this vertex is
|
||
|
fMoveAmount *= fAdjust;
|
||
|
|
||
|
// xy component
|
||
|
#ifdef SPEEDTREE_Z_UP
|
||
|
vPos.xy += vRotatedWindVector.xy * fMoveAmount;
|
||
|
#else
|
||
|
vPos.xz += vRotatedWindVector.xz * fMoveAmount;
|
||
|
#endif
|
||
|
|
||
|
if (bPreserveShape)
|
||
|
vPos.xyz = normalize(vPos.xyz) * fLength;
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// SimpleBranchWind
|
||
|
|
||
|
float3 SimpleBranchWind(float3 vPos,
|
||
|
float3 vInstancePos,
|
||
|
float fWeight,
|
||
|
float fOffset,
|
||
|
float fTime,
|
||
|
float fDistance,
|
||
|
float fTwitch,
|
||
|
float fTwitchScale,
|
||
|
float fWhip,
|
||
|
bool bWhip,
|
||
|
bool bRoll,
|
||
|
bool bComplex,
|
||
|
float3 vRotatedWindVector)
|
||
|
{
|
||
|
// turn the offset back into a nearly normalized vector
|
||
|
float3 vWindVector = UnpackNormalFromFloat(fOffset);
|
||
|
vWindVector = vWindVector * fWeight;
|
||
|
|
||
|
// try to fudge time a bit so that instances aren't in sync
|
||
|
fTime += vInstancePos.x + vInstancePos.y;
|
||
|
|
||
|
// oscillate
|
||
|
float4 vOscillations;
|
||
|
float fOsc = Oscillate(vPos, fTime, fOffset, fWeight, fWhip, bWhip, bRoll, bComplex, fTwitch, fTwitchScale, vOscillations, vRotatedWindVector);
|
||
|
|
||
|
vPos.xyz += vWindVector * fOsc * fDistance;
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// DirectionalBranchWind
|
||
|
|
||
|
float3 DirectionalBranchWind(float3 vPos,
|
||
|
float3 vInstancePos,
|
||
|
float fWeight,
|
||
|
float fOffset,
|
||
|
float fTime,
|
||
|
float fDistance,
|
||
|
float fTurbulence,
|
||
|
float fAdherence,
|
||
|
float fTwitch,
|
||
|
float fTwitchScale,
|
||
|
float fWhip,
|
||
|
bool bWhip,
|
||
|
bool bRoll,
|
||
|
bool bComplex,
|
||
|
bool bTurbulence,
|
||
|
float3 vRotatedWindVector)
|
||
|
{
|
||
|
// turn the offset back into a nearly normalized vector
|
||
|
float3 vWindVector = UnpackNormalFromFloat(fOffset);
|
||
|
vWindVector = vWindVector * fWeight;
|
||
|
|
||
|
// try to fudge time a bit so that instances aren't in sync
|
||
|
fTime += vInstancePos.x + vInstancePos.y;
|
||
|
|
||
|
// oscillate
|
||
|
float4 vOscillations;
|
||
|
float fOsc = Oscillate(vPos, fTime, fOffset, fWeight, fWhip, bWhip, false, bComplex, fTwitch, fTwitchScale, vOscillations, vRotatedWindVector);
|
||
|
|
||
|
vPos.xyz += vWindVector * fOsc * fDistance;
|
||
|
|
||
|
// add in the direction, accounting for turbulence
|
||
|
float fAdherenceScale = 1.0;
|
||
|
if (bTurbulence)
|
||
|
fAdherenceScale = Turbulence(fTime, fOffset, _ST_WindAnimation.x, fTurbulence);
|
||
|
|
||
|
if (bWhip)
|
||
|
fAdherenceScale += vOscillations.w * _ST_WindVector.w * fWhip;
|
||
|
|
||
|
//if (bRoll)
|
||
|
// fAdherenceScale = Roll(fAdherenceScale, _ST_WindRollingBranches.x, _ST_WindRollingBranches.y, _ST_WindRollingBranches.z, _ST_WindRollingBranches.w, vPos.xyz, fTime + fOffset, vRotatedWindVector);
|
||
|
|
||
|
vPos.xyz += vRotatedWindVector * fAdherence * fAdherenceScale * fWeight;
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// DirectionalBranchWindFrondStyle
|
||
|
|
||
|
float3 DirectionalBranchWindFrondStyle(float3 vPos,
|
||
|
float3 vInstancePos,
|
||
|
float fWeight,
|
||
|
float fOffset,
|
||
|
float fTime,
|
||
|
float fDistance,
|
||
|
float fTurbulence,
|
||
|
float fAdherence,
|
||
|
float fTwitch,
|
||
|
float fTwitchScale,
|
||
|
float fWhip,
|
||
|
bool bWhip,
|
||
|
bool bRoll,
|
||
|
bool bComplex,
|
||
|
bool bTurbulence,
|
||
|
float3 vRotatedWindVector,
|
||
|
float3 vRotatedBranchAnchor)
|
||
|
{
|
||
|
// turn the offset back into a nearly normalized vector
|
||
|
float3 vWindVector = UnpackNormalFromFloat(fOffset);
|
||
|
vWindVector = vWindVector * fWeight;
|
||
|
|
||
|
// try to fudge time a bit so that instances aren't in sync
|
||
|
fTime += vInstancePos.x + vInstancePos.y;
|
||
|
|
||
|
// oscillate
|
||
|
float4 vOscillations;
|
||
|
float fOsc = Oscillate(vPos, fTime, fOffset, fWeight, fWhip, bWhip, false, bComplex, fTwitch, fTwitchScale, vOscillations, vRotatedWindVector);
|
||
|
|
||
|
vPos.xyz += vWindVector * fOsc * fDistance;
|
||
|
|
||
|
// add in the direction, accounting for turbulence
|
||
|
float fAdherenceScale = 1.0;
|
||
|
if (bTurbulence)
|
||
|
fAdherenceScale = Turbulence(fTime, fOffset, _ST_WindAnimation.x, fTurbulence);
|
||
|
|
||
|
//if (bRoll)
|
||
|
// fAdherenceScale = Roll(fAdherenceScale, _ST_WindRollingBranches.x, _ST_WindRollingBranches.y, _ST_WindRollingBranches.z, _ST_WindRollingBranches.w, vPos.xyz, fTime + fOffset, vRotatedWindVector);
|
||
|
|
||
|
if (bWhip)
|
||
|
fAdherenceScale += vOscillations.w * _ST_WindVector.w * fWhip;
|
||
|
|
||
|
float3 vWindAdherenceVector = vRotatedBranchAnchor - vPos.xyz;
|
||
|
vPos.xyz += vWindAdherenceVector * fAdherence * fAdherenceScale * fWeight;
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// BranchWind
|
||
|
|
||
|
// Apply only to better, best, palm winds
|
||
|
float3 BranchWind(bool isPalmWind, float3 vPos, float3 vInstancePos, float4 vWindData, float3 vRotatedWindVector, float3 vRotatedBranchAnchor)
|
||
|
{
|
||
|
if (isPalmWind)
|
||
|
{
|
||
|
vPos = DirectionalBranchWindFrondStyle(vPos, vInstancePos, vWindData.x, vWindData.y, _ST_WindBranch.x, _ST_WindBranch.y, _ST_WindTurbulences.x, _ST_WindBranchAdherences.y, _ST_WindBranchTwitch.x, _ST_WindBranchTwitch.y, _ST_WindBranchWhip.x, true, false, true, true, vRotatedWindVector, vRotatedBranchAnchor);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vPos = SimpleBranchWind(vPos, vInstancePos, vWindData.x, vWindData.y, _ST_WindBranch.x, _ST_WindBranch.y, _ST_WindBranchTwitch.x, _ST_WindBranchTwitch.y, _ST_WindBranchWhip.x, false, false, true, vRotatedWindVector);
|
||
|
}
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// LeafRipple
|
||
|
|
||
|
float3 LeafRipple(float3 vPos,
|
||
|
inout float3 vDirection,
|
||
|
float fScale,
|
||
|
float fPackedRippleDir,
|
||
|
float fTime,
|
||
|
float fAmount,
|
||
|
bool bDirectional,
|
||
|
float fTrigOffset)
|
||
|
{
|
||
|
// compute how much to move
|
||
|
float4 vInput = float4(fTime + fTrigOffset, 0.0, 0.0, 0.0);
|
||
|
float fMoveAmount = fAmount * TrigApproximate(vInput).x;
|
||
|
|
||
|
if (bDirectional)
|
||
|
{
|
||
|
vPos.xyz += vDirection.xyz * fMoveAmount * fScale;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
float3 vRippleDir = UnpackNormalFromFloat(fPackedRippleDir);
|
||
|
vPos.xyz += vRippleDir * fMoveAmount * fScale;
|
||
|
}
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// LeafTumble
|
||
|
|
||
|
float3 LeafTumble(float3 vPos,
|
||
|
inout float3 vDirection,
|
||
|
float fScale,
|
||
|
float3 vAnchor,
|
||
|
float3 vGrowthDir,
|
||
|
float fTrigOffset,
|
||
|
float fTime,
|
||
|
float fFlip,
|
||
|
float fTwist,
|
||
|
float fAdherence,
|
||
|
float3 vTwitch,
|
||
|
float4 vRoll,
|
||
|
bool bTwitch,
|
||
|
bool bRoll,
|
||
|
float3 vRotatedWindVector)
|
||
|
{
|
||
|
// compute all oscillations up front
|
||
|
float3 vFracs = frac((vAnchor + fTrigOffset) * 30.3);
|
||
|
float fOffset = vFracs.x + vFracs.y + vFracs.z;
|
||
|
float4 vOscillations = TrigApproximate(float4(fTime + fOffset, fTime * 0.75 - fOffset, fTime * 0.01 + fOffset, fTime * 1.0 + fOffset));
|
||
|
|
||
|
// move to the origin and get the growth direction
|
||
|
float3 vOriginPos = vPos.xyz - vAnchor;
|
||
|
float fLength = length(vOriginPos);
|
||
|
|
||
|
// twist
|
||
|
float fOsc = vOscillations.x + vOscillations.y * vOscillations.y;
|
||
|
float3x3 matTumble = RotationMatrix(vGrowthDir, fScale * fTwist * fOsc);
|
||
|
|
||
|
// with wind
|
||
|
float3 vAxis = wind_cross(vGrowthDir.xyz, vRotatedWindVector.xyz);
|
||
|
float fDot = clamp(dot(vRotatedWindVector, vGrowthDir), -1.0, 1.0);
|
||
|
#ifdef SPEEDTREE_Z_UP
|
||
|
vAxis.z += fDot;
|
||
|
#else
|
||
|
vAxis.y += fDot;
|
||
|
#endif
|
||
|
vAxis = normalize(vAxis);
|
||
|
|
||
|
float fAngle = acos(fDot);
|
||
|
|
||
|
float fAdherenceScale = 1.0;
|
||
|
//if (bRoll)
|
||
|
//{
|
||
|
// fAdherenceScale = Roll(fAdherenceScale, vRoll.x, vRoll.y, vRoll.z, vRoll.w, vAnchor.xyz, fTime, vRotatedWindVector);
|
||
|
//}
|
||
|
|
||
|
fOsc = vOscillations.y - vOscillations.x * vOscillations.x;
|
||
|
|
||
|
float fTwitch = 0.0;
|
||
|
if (bTwitch)
|
||
|
fTwitch = Twitch(vAnchor.xyz, vTwitch.x, vTwitch.y, vTwitch.z + fOffset);
|
||
|
|
||
|
matTumble = mul_float3x3_float3x3(matTumble, RotationMatrix(vAxis, fScale * (fAngle * fAdherence * fAdherenceScale + fOsc * fFlip + fTwitch)));
|
||
|
|
||
|
vDirection = mul_float3x3_float3(matTumble, vDirection);
|
||
|
vOriginPos = mul_float3x3_float3(matTumble, vOriginPos);
|
||
|
|
||
|
vOriginPos = normalize(vOriginPos) * fLength;
|
||
|
|
||
|
return (vOriginPos + vAnchor);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// LeafWind
|
||
|
// Optimized (for instruction count) version. Assumes leaf 1 and 2 have the same options
|
||
|
|
||
|
float3 LeafWind(bool isBestWind,
|
||
|
bool bLeaf2,
|
||
|
float3 vPos,
|
||
|
inout float3 vDirection,
|
||
|
float fScale,
|
||
|
float3 vAnchor,
|
||
|
float fPackedGrowthDir,
|
||
|
float fPackedRippleDir,
|
||
|
float fRippleTrigOffset,
|
||
|
float3 vRotatedWindVector)
|
||
|
{
|
||
|
|
||
|
vPos = LeafRipple(vPos, vDirection, fScale, fPackedRippleDir,
|
||
|
(bLeaf2 ? _ST_WindLeaf2Ripple.x : _ST_WindLeaf1Ripple.x),
|
||
|
(bLeaf2 ? _ST_WindLeaf2Ripple.y : _ST_WindLeaf1Ripple.y),
|
||
|
false, fRippleTrigOffset);
|
||
|
|
||
|
if (isBestWind)
|
||
|
{
|
||
|
float3 vGrowthDir = UnpackNormalFromFloat(fPackedGrowthDir);
|
||
|
vPos = LeafTumble(vPos, vDirection, fScale, vAnchor, vGrowthDir, fPackedGrowthDir,
|
||
|
(bLeaf2 ? _ST_WindLeaf2Tumble.x : _ST_WindLeaf1Tumble.x),
|
||
|
(bLeaf2 ? _ST_WindLeaf2Tumble.y : _ST_WindLeaf1Tumble.y),
|
||
|
(bLeaf2 ? _ST_WindLeaf2Tumble.z : _ST_WindLeaf1Tumble.z),
|
||
|
(bLeaf2 ? _ST_WindLeaf2Tumble.w : _ST_WindLeaf1Tumble.w),
|
||
|
(bLeaf2 ? _ST_WindLeaf2Twitch.xyz : _ST_WindLeaf1Twitch.xyz),
|
||
|
0.0f,
|
||
|
(bLeaf2 ? true : true),
|
||
|
(bLeaf2 ? true : true),
|
||
|
vRotatedWindVector);
|
||
|
}
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// RippleFrondOneSided
|
||
|
|
||
|
float3 RippleFrondOneSided(float3 vPos,
|
||
|
inout float3 vDirection,
|
||
|
float fU,
|
||
|
float fV,
|
||
|
float fRippleScale
|
||
|
#ifdef WIND_EFFECT_FROND_RIPPLE_ADJUST_LIGHTING
|
||
|
, float3 vBinormal
|
||
|
, float3 vTangent
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
float fOffset = 0.0;
|
||
|
if (fU < 0.5)
|
||
|
fOffset = 0.75;
|
||
|
|
||
|
float4 vOscillations = TrigApproximate(float4((_ST_WindFrondRipple.x + fV) * _ST_WindFrondRipple.z + fOffset, 0.0, 0.0, 0.0));
|
||
|
|
||
|
float fAmount = fRippleScale * vOscillations.x * _ST_WindFrondRipple.y;
|
||
|
float3 vOffset = fAmount * vDirection;
|
||
|
vPos.xyz += vOffset;
|
||
|
|
||
|
#ifdef WIND_EFFECT_FROND_RIPPLE_ADJUST_LIGHTING
|
||
|
vTangent.xyz = normalize(vTangent.xyz + vOffset * _ST_WindFrondRipple.w);
|
||
|
float3 vNewNormal = normalize(wind_cross(vBinormal.xyz, vTangent.xyz));
|
||
|
if (dot(vNewNormal, vDirection.xyz) < 0.0)
|
||
|
vNewNormal = -vNewNormal;
|
||
|
vDirection.xyz = vNewNormal;
|
||
|
#endif
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// RippleFrondTwoSided
|
||
|
|
||
|
float3 RippleFrondTwoSided(float3 vPos,
|
||
|
inout float3 vDirection,
|
||
|
float fU,
|
||
|
float fLengthPercent,
|
||
|
float fPackedRippleDir,
|
||
|
float fRippleScale
|
||
|
#ifdef WIND_EFFECT_FROND_RIPPLE_ADJUST_LIGHTING
|
||
|
, float3 vBinormal
|
||
|
, float3 vTangent
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
float4 vOscillations = TrigApproximate(float4(_ST_WindFrondRipple.x * fLengthPercent * _ST_WindFrondRipple.z, 0.0, 0.0, 0.0));
|
||
|
|
||
|
float3 vRippleDir = UnpackNormalFromFloat(fPackedRippleDir);
|
||
|
|
||
|
float fAmount = fRippleScale * vOscillations.x * _ST_WindFrondRipple.y;
|
||
|
float3 vOffset = fAmount * vRippleDir;
|
||
|
|
||
|
vPos.xyz += vOffset;
|
||
|
|
||
|
#ifdef WIND_EFFECT_FROND_RIPPLE_ADJUST_LIGHTING
|
||
|
vTangent.xyz = normalize(vTangent.xyz + vOffset * _ST_WindFrondRipple.w);
|
||
|
float3 vNewNormal = normalize(wind_cross(vBinormal.xyz, vTangent.xyz));
|
||
|
if (dot(vNewNormal, vDirection.xyz) < 0.0)
|
||
|
vNewNormal = -vNewNormal;
|
||
|
vDirection.xyz = vNewNormal;
|
||
|
#endif
|
||
|
|
||
|
return vPos;
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// RippleFrond
|
||
|
|
||
|
float3 RippleFrond(float3 vPos,
|
||
|
inout float3 vDirection,
|
||
|
float fU,
|
||
|
float fV,
|
||
|
float fPackedRippleDir,
|
||
|
float fRippleScale,
|
||
|
float fLenghtPercent
|
||
|
#ifdef WIND_EFFECT_FROND_RIPPLE_ADJUST_LIGHTING
|
||
|
, float3 vBinormal
|
||
|
, float3 vTangent
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
return RippleFrondOneSided(vPos,
|
||
|
vDirection,
|
||
|
fU,
|
||
|
fV,
|
||
|
fRippleScale
|
||
|
#ifdef WIND_EFFECT_FROND_RIPPLE_ADJUST_LIGHTING
|
||
|
, vBinormal
|
||
|
, vTangent
|
||
|
#endif
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// SpeedTreeWind
|
||
|
|
||
|
float3 SpeedTreeWind(float3 vPos, float3 vNormal, float4 vTexcoord0, float4 vTexcoord1, float4 vTexcoord2, float4 vTexcoord3, int iWindQuality, bool bBillboard, bool bCrossfade)
|
||
|
{
|
||
|
float3 vReturnPos = vPos;
|
||
|
|
||
|
// geometry type
|
||
|
int geometryType = (int)(vTexcoord3.w + 0.25);
|
||
|
bool leafTwo = false;
|
||
|
if (geometryType > ST_GEOM_TYPE_FACINGLEAF)
|
||
|
{
|
||
|
geometryType -= 2;
|
||
|
leafTwo = true;
|
||
|
}
|
||
|
|
||
|
// smooth LOD
|
||
|
if (!bCrossfade && !bBillboard)
|
||
|
{
|
||
|
vReturnPos = lerp(vReturnPos, vTexcoord2.xyz, unity_LODFade.x);
|
||
|
}
|
||
|
|
||
|
// do leaf facing even when we don't have wind
|
||
|
if (geometryType == ST_GEOM_TYPE_FACINGLEAF)
|
||
|
{
|
||
|
float3 anchor = float3(vTexcoord1.zw, vTexcoord2.w);
|
||
|
float3 facingPosition = vReturnPos - anchor;
|
||
|
|
||
|
// face camera-facing leaf to camera
|
||
|
float offsetLen = length(facingPosition);
|
||
|
facingPosition = float3(facingPosition.x, -facingPosition.z, facingPosition.y);
|
||
|
float4x4 itmv = transpose(mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V));
|
||
|
facingPosition = mul(facingPosition.xyz, (float3x3)itmv);
|
||
|
facingPosition = normalize(facingPosition) * offsetLen; // make sure the offset vector is still scaled
|
||
|
|
||
|
vReturnPos = facingPosition + anchor;
|
||
|
}
|
||
|
|
||
|
// wind
|
||
|
if ((iWindQuality > 0) && (length(_ST_WindVector) > 0))
|
||
|
{
|
||
|
float3 rotatedWindVector = TransformWorldToObjectDir(_ST_WindVector.xyz);
|
||
|
float windLength = length(rotatedWindVector);
|
||
|
if (windLength < 1.0e-5)
|
||
|
{
|
||
|
// sanity check that wind data is available
|
||
|
return vReturnPos;
|
||
|
}
|
||
|
rotatedWindVector /= windLength;
|
||
|
|
||
|
float4x4 matObjectToWorld = GetObjectToWorldMatrix();
|
||
|
float3 treePos = GetAbsolutePositionWS(float3(matObjectToWorld[0].w, matObjectToWorld[1].w, matObjectToWorld[2].w));
|
||
|
|
||
|
if (!bBillboard)
|
||
|
{
|
||
|
// leaves
|
||
|
if (geometryType > ST_GEOM_TYPE_FROND)
|
||
|
{
|
||
|
// remove anchor position
|
||
|
float3 anchor = float3(vTexcoord1.zw, vTexcoord2.w);
|
||
|
vReturnPos -= anchor;
|
||
|
|
||
|
// leaf wind
|
||
|
if ((iWindQuality == ST_WIND_QUALITY_FAST) || (iWindQuality == ST_WIND_QUALITY_BETTER) || (iWindQuality == ST_WIND_QUALITY_BEST))
|
||
|
{
|
||
|
bool bBestWind = (iWindQuality == ST_WIND_QUALITY_BEST);
|
||
|
float leafWindTrigOffset = anchor.x + anchor.y;
|
||
|
vReturnPos = LeafWind(bBestWind, leafTwo, vReturnPos, vNormal, vTexcoord3.x, float3(0, 0, 0), vTexcoord3.y, vTexcoord3.z, leafWindTrigOffset, rotatedWindVector);
|
||
|
}
|
||
|
|
||
|
// move back out to anchor
|
||
|
vReturnPos += anchor;
|
||
|
}
|
||
|
|
||
|
// frond wind
|
||
|
bool bPalmWind = false;
|
||
|
if (iWindQuality == ST_WIND_QUALITY_PALM)
|
||
|
{
|
||
|
bPalmWind = true;
|
||
|
if (geometryType == ST_GEOM_TYPE_FROND)
|
||
|
{
|
||
|
vReturnPos = RippleFrond(vReturnPos, vNormal, vTexcoord0.x, vTexcoord0.y, vTexcoord3.x, vTexcoord3.y, vTexcoord3.z);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// branch wind (applies to all 3D geometry)
|
||
|
if ((iWindQuality == ST_WIND_QUALITY_BETTER) || (iWindQuality == ST_WIND_QUALITY_BEST) || (iWindQuality == ST_WIND_QUALITY_PALM))
|
||
|
{
|
||
|
float3 rotatedBranchAnchor = TransformWorldToObjectDir(_ST_WindBranchAnchor.xyz) * _ST_WindBranchAnchor.w;
|
||
|
vReturnPos = BranchWind(bPalmWind, vReturnPos, treePos, float4(vTexcoord0.zw, 0, 0), rotatedWindVector, rotatedBranchAnchor);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// global wind
|
||
|
float globalWindTime = _ST_WindGlobal.x;
|
||
|
//#if defined(EFFECT_BILLBOARD) && defined(UNITY_INSTANCING_ENABLED)
|
||
|
// globalWindTime += UNITY_ACCESS_INSTANCED_PROP(STWind, _GlobalWindTime);
|
||
|
//#endif
|
||
|
vReturnPos = GlobalWind(vReturnPos, treePos, true, rotatedWindVector, globalWindTime);
|
||
|
}
|
||
|
|
||
|
return vReturnPos;
|
||
|
}
|
||
|
|
||
|
// This version is used by ShaderGraph
|
||
|
void SpeedTreeWind_float(float3 vPos, float3 vNormal, float4 vTexcoord0, float4 vTexcoord1, float4 vTexcoord2, float4 vTexcoord3, int iWindQuality, bool bBillboard, bool bCrossfade, out float3 outPos)
|
||
|
{
|
||
|
if (iWindQuality != ST_WIND_QUALITY_NONE)
|
||
|
{
|
||
|
outPos = SpeedTreeWind(vPos, vNormal, vTexcoord0, vTexcoord1, vTexcoord2, vTexcoord3, iWindQuality, bBillboard, bCrossfade);
|
||
|
}
|
||
|
else
|
||
|
outPos = vPos;
|
||
|
|
||
|
}
|
||
|
#endif // SPEEDTREE_WIND_INCLUDED
|