// There are two variants provided, one takes the texture and sampler explicitly so they can be statically passed in.
// The variant without resource parameters dynamically accesses the texture when sampling.
// Helper function to offset depth based on the surface normal and light direction.
// If the light hits the surface perpendicularly there will be no offset.
real3 EvalShadow_NormalBias( real3 normalWS, real NoL, real2 texelSize, real normalBias )
float4 EvalShadow_WorldToShadow( ShadowData sd, real3 positionWS, bool perspProj )
return max( texelSize.x, texelSize.y ) * normalBias * (1.0 - NoL) * normalWS;
}
if( perspProj )
{
positionWS = positionWS - sd.pos;
float3x3 view = { sd.rot0, sd.rot1, sd.rot2 };
positionWS = mul( view, positionWS );
}
else
{
float3x4 view;
view[0] = float4( sd.rot0, sd.pos.x );
view[1] = float4( sd.rot1, sd.pos.y );
view[2] = float4( sd.rot2, sd.pos.z );
positionWS = mul( view, float4( positionWS, 1.0 ) ).xyz;
}
float4x4 proj;
proj = 0.0;
proj._m00 = sd.proj[0];
proj._m11 = sd.proj[1];
proj._m22 = sd.proj[2];
proj._m23 = sd.proj[3];
if( perspProj )
proj._m32 = -1.0;
else
proj._m33 = 1.0;
return mul( proj, float4( positionWS, 1.0 ) );
}
real3 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real3 posNDC, bool clampToRect )
real3 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real3 posNDC, bool perspProj )
real4 posCS = mul(real4(positionWS, 1.0), sd.worldToShadow);
posCS.z -= sd.bias * posCS.w;
posNDC = posCS.xyz / posCS.w;
real4 posCS = EvalShadow_WorldToShadow( sd, positionWS, perspProj );
posNDC = perspProj ? (posCS.xyz / posCS.w) : posCS.xyz;
real3 posTC = posNDC * 0.5 + 0.5;
posTC.xy = clampToRect ? clamp( posTC.xy, sd.texelSizeRcp.zw*0.5, 1.0.xx - sd.texelSizeRcp.zw*0.5 ) : posTC.xy;
real3 posTC = real3( posNDC.xy * 0.5 + 0.5, posNDC.z );
#if UNITY_REVERSED_Z
posTC.z = 1.0 - posTC.z;
#endif
real3 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS )
real3 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, bool perspProj )
return EvalShadow_GetTexcoords( sd, positionWS, ndc, false );
return EvalShadow_GetTexcoords( sd, positionWS, ndc, perspProj );
uint2 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real2 closestSampleNDC )
uint2 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real2 closestSampleNDC, bool perspProj )
real4 posCS = mul( real4( positionWS, 1.0 ), sd.worldToShadow );
real2 posNDC = posCS.xy / posCS.w;
real4 posCS = EvalShadow_WorldToShadow( sd, positionWS, perspProj );
real2 posNDC = perspProj ? (posCS.xy / posCS.w) : posCS.xy;
// calc TCs
real2 posTC = posNDC * 0.5 + 0.5;
closestSampleNDC = (floor(posTC * sd.textureSize.zw) + 0.5) * sd.texelSizeRcp.zw * 2.0 - 1.0.xx;
int EvalShadow_GetCubeFaceID( real3 sampleToLight )
//
// Biasing functions
//
// helper function to get the world texel size
real EvalShadow_WorldTexelSize( ShadowData sd, float L_dist, bool perspProj )
real3 lightToSample = -sampleToLight; // TODO: pass the correct (flipped) direction
return perspProj ? (sd.viewBias.w * L_dist) : sd.viewBias.w;
}
// used to scale down view biases to mitigate light leaking across shadowed corners
#if SHADOW_USE_VIEW_BIAS_SCALING != 0
real EvalShadow_ReceiverBiasWeightFlag( float flag )
{
return (asint( flag ) & 2) ? 1.0 : 0.0;
}
bool EvalShadow_ReceiverBiasWeightUseNormalFlag( float flag )
{
return (asint( flag ) & 4) ? true : false;
}
#ifdef INTRINSIC_CUBEMAP_FACE_ID
return (int)CubeMapFaceID(lightToSample);
real3 EvalShadow_ReceiverBiasWeightPos( real3 positionWS, real3 normalWS, real3 L, real worldTexelSize, real tolerance, bool useNormal )
{
#if SHADOW_USE_ONLY_VIEW_BASED_BIASING != 0
return positionWS + L * worldTexelSize * tolerance;
// TODO: use CubeMapFaceID() defined in Common.hlsl for all pipelines on all platforms.
real3 dir = sampleToLight;
real3 adir = abs(dir);
return positionWS + (useNormal ? normalWS : L) * worldTexelSize * tolerance;
#endif
}
real EvalShadow_ReceiverBiasWeight( ShadowContext shadowContext, uint shadowAlgorithm, ShadowData sd, uint texIdx, uint sampIdx, real3 positionWS, real3 normalWS, real3 L, real L_dist, bool perspProj )
{
real weight = 1.0;
UNITY_BRANCH
if( shadowAlgorithm <= GPUSHADOWALGORITHM_PCF_TENT_7X7 )
{
real3 pos = EvalShadow_ReceiverBiasWeightPos( positionWS, normalWS, L, EvalShadow_WorldTexelSize( sd, L_dist, perspProj ), sd.edgeTolerance, EvalShadow_ReceiverBiasWeightUseNormalFlag( sd.normalBias.w ) );
real3 tcs = EvalShadow_GetTexcoords( sd, pos, perspProj );
weight = SampleCompShadow_T2DA( shadowContext, texIdx, sampIdx, tcs, sd.slice ).x;
}
return lerp( 1.0, weight, EvalShadow_ReceiverBiasWeightFlag( sd.normalBias.w ) );
}
real EvalShadow_ReceiverBiasWeight( ShadowData sd, Texture2DArray tex, SamplerComparisonState samp, real3 positionWS, real3 normalWS, real3 L, real L_dist, bool perspProj )
{
real3 pos = EvalShadow_ReceiverBiasWeightPos( positionWS, normalWS, L, EvalShadow_WorldTexelSize( sd, L_dist, perspProj ), sd.edgeTolerance, EvalShadow_ReceiverBiasWeightUseNormalFlag( sd.normalBias.w ) );
return lerp( 1.0, SAMPLE_TEXTURE2D_ARRAY_SHADOW( tex, samp, EvalShadow_GetTexcoords( sd, pos, perspProj ), sd.slice ).x, EvalShadow_ReceiverBiasWeightFlag( sd.normalBias.w ) );
}
real EvalShadow_ReceiverBiasWeight( ShadowData sd, Texture2DArray tex, SamplerState samp, real3 positionWS, real3 normalWS, real3 L, real L_dist, bool perspProj )
{
// only used by PCF filters
return 1.0;
}
#else // SHADOW_USE_VIEW_BIAS_SCALING != 0
real EvalShadow_ReceiverBiasWeight( ShadowContext shadowContext, uint shadowAlgorithm, ShadowData sd, uint texIdx, uint sampIdx, real3 positionWS, real3 normalWS, real3 L, real L_dist, bool perspProj ) { return 1.0; }
real EvalShadow_ReceiverBiasWeight( ShadowData sd, Texture2DArray tex, SamplerComparisonState samp, real3 positionWS, real3 normalWS, real3 L, real L_dist, bool perspProj ) { return 1.0; }
real EvalShadow_ReceiverBiasWeight (ShadowData sd, Texture2DArray tex, SamplerState samp, real3 positionWS, real3 normalWS, real3 L, real L_dist, bool perspProj ) { return 1.0; }
#endif // SHADOW_USE_VIEW_BIAS_SCALING != 0
// receiver bias either using the normal to weight normal and view biases, or just light view biasing
float3 EvalShadow_ReceiverBias( ShadowData sd, float3 positionWS, float3 normalWS, float3 L, float L_dist, float lightviewBiasWeight, bool perspProj )
{
#if SHADOW_USE_ONLY_VIEW_BASED_BIASING != 0 // only light vector based biasing
float viewBiasScale = sd.viewBias.z;
return positionWS + L * viewBiasScale * lightviewBiasWeight * EvalShadow_WorldTexelSize( sd, L_dist, perspProj );
#else // biasing based on the angle between the normal and the light vector
float viewBiasMin = sd.viewBias.x;
float viewBiasMax = sd.viewBias.y;
float viewBiasScale = sd.viewBias.z;
float normalBiasMin = sd.normalBias.x;
float normalBiasMax = sd.normalBias.y;
float normalBiasScale = sd.normalBias.z;
// +Z -Z
int faceIndex = dir.z > 0.0 ? CUBEMAPFACE_NEGATIVE_Z : CUBEMAPFACE_POSITIVE_Z;
float NdotL = dot( normalWS, L );
float sine = sqrt( saturate( 1.0 - NdotL * NdotL ) );
float tangent = abs( NdotL ) > 0.0 ? (sine / NdotL) : 0.0;
sine = clamp( sine * normalBiasScale, normalBiasMin, normalBiasMax );
tangent = clamp( tangent * viewBiasScale * lightviewBiasWeight, viewBiasMin, viewBiasMax );
float3 view_bias = L * tangent;
float3 normal_bias = normalWS * sine;
return positionWS + (normal_bias + view_bias) * EvalShadow_WorldTexelSize( sd, L_dist, perspProj );
#endif
}
// +X -X
if (adir.x > adir.y && adir.x > adir.z)
// sample bias used by wide PCF filters to offset individual taps
#if SHADOW_USE_SAMPLE_BIASING != 0
real EvalShadow_SampleBiasFlag( float flag )
{
return (asint( flag ) & 1) ? 1.0 : 0.0;
}
float2 EvalShadow_SampleBias_Persp( ShadowData sd, float3 positionWS, float3 normalWS, float3 tcs )
{
float3 e1, e2;
if( abs( normalWS.z ) > 0.65 )
faceIndex = dir.x > 0.0 ? CUBEMAPFACE_NEGATIVE_X : CUBEMAPFACE_POSITIVE_X;
e1 = float3( 1.0, 0.0, -normalWS.x / normalWS.z );
e2 = float3( 0.0, 1.0, -normalWS.y / normalWS.z );
// +Y -Y
else if (adir.y > adir.x && adir.y > adir.z)
else if( abs( normalWS.y ) > 0.65 )
{
e1 = float3( 1.0, -normalWS.x / normalWS.y, 0.0 );
e2 = float3( 0.0, -normalWS.z / normalWS.y, 1.0 );
}
else
faceIndex = dir.y > 0.0 ? CUBEMAPFACE_NEGATIVE_Y : CUBEMAPFACE_POSITIVE_Y;
e1 = float3( -normalWS.y / normalWS.x, 1.0, 0.0 );
e2 = float3( -normalWS.z / normalWS.x, 0.0, 1.0 );
return faceIndex;
#endif
float4 p1 = EvalShadow_WorldToShadow( sd, positionWS + e1, true );
float4 p2 = EvalShadow_WorldToShadow( sd, positionWS + e2, true );
p1.xyz /= p1.w;
p2.xyz /= p2.w;
p1.xyz = float3( p1.xy * 0.5 + 0.5, p1.z );
p2.xyz = float3( p2.xy * 0.5 + 0.5, p2.z );
p1.xy = p1.xy * sd.scaleOffset.xy + sd.scaleOffset.zw;
p2.xy = p2.xy * sd.scaleOffset.xy + sd.scaleOffset.zw;
float3 nrm = cross( p1.xyz - tcs, p2.xyz - tcs );
nrm.xy /= -nrm.z;
return isfinite( nrm.xy ) ? (EvalShadow_SampleBiasFlag( sd.normalBias.w ) * nrm.xy) : 0.0.xx;
float2 EvalShadow_SampleBias_Ortho( ShadowData sd, float3 normalWS )
{
float3x3 view = float3x3( sd.rot0, sd.rot1, sd.rot2 );
float3 nrm = mul( view, normalWS );
nrm.x /= sd.proj[0];
nrm.y /= sd.proj[1];
nrm.z /= sd.proj[2];
nrm.x *= sd.scaleOffset.y;
nrm.y *= sd.scaleOffset.x;
nrm.z *= sd.scaleOffset.x * sd.scaleOffset.y;
nrm.xy /= -nrm.z;
return isfinite( nrm.xy ) ? (EvalShadow_SampleBiasFlag( sd.normalBias.w ) * nrm.xy) : 0.0.xx;
}
#else // SHADOW_USE_SAMPLE_BIASING != 0
float2 EvalShadow_SampleBias_Persp( ShadowData sd, float3 positionWS, float3 normalWS, float3 tcs ) { return 0.0.xx; }
float2 EvalShadow_SampleBias_Ortho( ShadowData sd, float3 normalWS ) { return 0.0.xx; }
#endif // SHADOW_USE_SAMPLE_BIASING != 0
real EvalShadow_PointDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real4 L )
real EvalShadow_PointDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist )
ShadowData sd = shadowContext.shadowDatas[index];
real3 biased_posWS = positionWS + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias );
real3 lpos = positionWS + L.xyz * L.w;
positionWS = biased_posWS;
int faceIndex = EvalShadow_GetCubeFaceID( lpos - biased_posWS ) + 1;
// load the right shadow data for the current face
sd = shadowContext.shadowDatas[index + faceIndex];
uint payloadOffset = GetPayloadOffset( sd );
// normal based bias
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias );
// get shadowmap texcoords
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
ShadowData sd = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1];
// sample the texture according to the given algorithm
// get the texture
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
UnpackShadowmapId( sd.id, texIdx, sampIdx );
// bias the world position
float recvBiasWeight = EvalShadow_ReceiverBiasWeight( shadowContext, shadowAlgorithm, sd, texIdx, sampIdx, positionWS, normalWS, L, L_dist, true );
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, L_dist, recvBiasWeight, true );
// get shadowmap texcoords
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, true );
// get the per sample bias
real2 sampleBias = EvalShadow_SampleBias_Persp( sd, positionWS, normalWS, posTC );
// sample the texture according to the given algorithm
uint payloadOffset = GetPayloadOffset( sd );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
#define EvalShadow_PointDepth_( _samplerType ) \
real EvalShadow_PointDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real4 L ) \
{ \
ShadowData sd = shadowContext.shadowDatas[index]; \
real3 biased_posWS = positionWS + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias ); \
real3 lpos = positionWS + L.xyz * L.w; \
positionWS = biased_posWS; \
int faceIndex = EvalShadow_GetCubeFaceID( lpos - biased_posWS ) + 1; \
/* load the right shadow data for the current face */ \
sd = shadowContext.shadowDatas[index + faceIndex]; \
uint payloadOffset = GetPayloadOffset( sd ); \
/* normal based bias */ \
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias ); \
/* get shadowmap texcoords */ \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
/* sample the texture */ \
real slice; \
UnpackShadowmapId( sd.id, slice ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, tex, samp ); \
#define EvalShadow_PointDepth_( _samplerType ) \
real EvalShadow_PointDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist ) \
{ \
ShadowData sd = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1]; \
/* bias the world position */ \
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( sd, tex, samp, positionWS, normalWS, L, L_dist, true ); \
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, L_dist, recvBiasWeight, true ); \
/* get shadowmap texcoords */ \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, true ); \
/* get the per sample bias */ \
real2 sampleBias = EvalShadow_SampleBias_Persp( sd, positionWS, normalWS, posTC ); \
/* sample the texture */ \
uint payloadOffset = GetPayloadOffset( sd ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, tex, samp ); \
}
EvalShadow_PointDepth_( SamplerComparisonState )
EvalShadow_PointDepth_( SamplerState )
// Spot shadows
//
real EvalShadow_SpotDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L )
real EvalShadow_SpotDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist )
uint payloadOffset = GetPayloadOffset( sd );
// normal based bias
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), sd.texelSizeRcp.zw, sd.normalBias );
// get shadowmap texcoords
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
UnpackShadowmapId( sd.id, texIdx, sampIdx );
// bias the world position
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( shadowContext, shadowAlgorithm, sd, texIdx, sampIdx, positionWS, normalWS, L, L_dist, true );
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, L_dist, recvBiasWeight, true );
// get shadowmap texcoords
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, true );
// get the per sample bias
real2 sampleBias = EvalShadow_SampleBias_Persp( sd, positionWS, normalWS, posTC );
// sample the texture according to the given algorithm
uint payloadOffset = GetPayloadOffset( sd );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
#define EvalShadow_SpotDepth_( _samplerType ) \
real EvalShadow_SpotDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
{ \
/* load the right shadow data for the current face */ \
ShadowData sd = shadowContext.shadowDatas[index]; \
uint payloadOffset = GetPayloadOffset( sd ); \
/* normal based bias */ \
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), sd.texelSizeRcp.zw, sd.normalBias ); \
/* get shadowmap texcoords */ \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
/* sample the texture */ \
real slice; \
UnpackShadowmapId( sd.id, slice ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, tex, samp ); \
#define EvalShadow_SpotDepth_( _samplerType ) \
real EvalShadow_SpotDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist ) \
{ \
/* load the right shadow data for the current face */ \
ShadowData sd = shadowContext.shadowDatas[index]; \
/* bias the world position */ \
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( sd, tex, samp, positionWS, normalWS, L, L_dist, true ); \
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, L_dist, recvBiasWeight, true ); \
/* get shadowmap texcoords */ \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, true ); \
/* get the per sample bias */ \
real2 sampleBias = EvalShadow_SampleBias_Persp( sd, positionWS, normalWS, posTC ); \
/* sample the texture */ \
uint payloadOffset = GetPayloadOffset( sd ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, tex, samp ); \
}
EvalShadow_SpotDepth_( SamplerComparisonState )
EvalShadow_SpotDepth_( SamplerState )
// Punctual shadows for Point and Spot
//
real EvalShadow_PunctualDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real4 L )
real EvalShadow_PunctualDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist )
// load the right shadow data for the current face
int faceIndex = 0;
UnpackShadowType( sd.shadowType, shadowType );
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
[branch]
// load the right shadow data for the current face
UNITY_BRANCH
real3 biased_posWS = positionWS + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias );
real3 lpos = positionWS + L.xyz * L.w;
positionWS = biased_posWS;
faceIndex = EvalShadow_GetCubeFaceID( lpos - biased_posWS ) + 1;
sd.rot0 = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].rot0;
sd.rot1 = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].rot1;
sd.rot2 = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].rot2;
sd.shadowToWorld = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].shadowToWorld;
sd.scaleOffset.zw = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].scaleOffset.zw;
sd.slice = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].slice;
else
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias );
sd = shadowContext.shadowDatas[index + faceIndex];
uint payloadOffset = GetPayloadOffset( sd );
uint texIdx, sampIdx;
UnpackShadowmapId( sd.id, texIdx, sampIdx );
// bias the world position
float recvBiasWeight = EvalShadow_ReceiverBiasWeight( shadowContext, shadowAlgorithm, sd, texIdx, sampIdx, positionWS, normalWS, L, L_dist, true );
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, L_dist, recvBiasWeight, true );
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS );
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, true );
// get the per sample bias
real2 sampleBias = EvalShadow_SampleBias_Persp( sd, positionWS, normalWS, posTC );
uint texIdx, sampIdx;
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
uint payloadOffset = GetPayloadOffset( sd );
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
#define EvalShadow_PunctualDepth_( _samplerType ) \
real EvalShadow_PunctualDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real4 L ) \
{ \
/* load the right shadow data for the current face */ \
int faceIndex = 0; \
/* get the shadow type */ \
ShadowData sd = shadowContext.shadowDatas[index]; \
uint shadowType; \
UnpackShadowType( sd.shadowType, shadowType ); \
\
[branch] \
if( shadowType == GPUSHADOWTYPE_POINT ) \
{ \
real3 biased_posWS = positionWS + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias ); \
real3 lpos = positionWS + L.xyz * L.w; \
positionWS = biased_posWS; \
faceIndex = EvalShadow_GetCubeFaceID( lpos - biased_posWS ) + 1; \
} \
else \
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L.xyz ) ), sd.texelSizeRcp.zw, sd.normalBias ); \
\
sd = shadowContext.shadowDatas[index + faceIndex]; \
uint payloadOffset = GetPayloadOffset( sd ); \
/* get shadowmap texcoords */ \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
/* sample the texture */ \
real slice; \
UnpackShadowmapId( sd.id, slice ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, tex, samp ); \
#define EvalShadow_PunctualDepth_( _samplerType ) \
real EvalShadow_PunctualDepth( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist ) \
{ \
int faceIndex = 0; \
/* get the shadow type */ \
ShadowData sd = shadowContext.shadowDatas[index]; \
uint shadowType; \
UnpackShadowType( sd.shadowType, shadowType ); \
\
/* load the right shadow data for the current face */ \
UNITY_BRANCH \
if( shadowType == GPUSHADOWTYPE_POINT ) \
{ \
sd.rot0 = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].rot0; \
sd.rot1 = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].rot1; \
sd.rot2 = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].rot2; \
sd.shadowToWorld = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].shadowToWorld; \
sd.scaleOffset.zw = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].scaleOffset.zw; \
sd.slice = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1].slice; \
} \
\
/* bias the world position */ \
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( sd, tex, samp, positionWS, normalWS, L, L_dist, true ); \
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, L_dist, recvBiasWeight, true ); \
/* get shadowmap texcoords */ \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, true ); \
/* get the per sample bias */ \
real2 sampleBias = EvalShadow_SampleBias_Persp( sd, positionWS, normalWS, posTC ); \
/* sample the texture */ \
uint payloadOffset = GetPayloadOffset( sd ); \
return SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, tex, samp ); \
//
// Directional shadows (cascaded shadow map)
#define SHADOW_REPEAT_CASCADE( _x ) _x, _x, _x, _x
int EvalShadow_GetSplitSphereIndexForDirshadows( real3 positionWS, real4 dirShadowSplitSpheres[4], out real relDistance )
void EvalShadow_LoadCascadeData( ShadowContext shadowContext, uint index, inout ShadowData sd )
real3 fromCenter0 = positionWS.xyz - dirShadowSplitSpheres[0].xyz;
real3 fromCenter1 = positionWS.xyz - dirShadowSplitSpheres[1].xyz;
real3 fromCenter2 = positionWS.xyz - dirShadowSplitSpheres[2].xyz;
real3 fromCenter3 = positionWS.xyz - dirShadowSplitSpheres[3].xyz;
real4 distances2 = real4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));
sd.shadowToWorld = shadowContext.shadowDatas[index].shadowToWorld;
sd.proj = shadowContext.shadowDatas[index].proj;
sd.pos = shadowContext.shadowDatas[index].pos;
sd.scaleOffset.zw = shadowContext.shadowDatas[index].scaleOffset.zw;
sd.slice = shadowContext.shadowDatas[index].slice;
sd.viewBias.w = shadowContext.shadowDatas[index].viewBias.w;
}
real4 dirShadowSplitSphereSqRadii;
dirShadowSplitSphereSqRadii.x = dirShadowSplitSpheres[0].w;
dirShadowSplitSphereSqRadii.y = dirShadowSplitSpheres[1].w;
dirShadowSplitSphereSqRadii.z = dirShadowSplitSpheres[2].w;
dirShadowSplitSphereSqRadii.w = dirShadowSplitSpheres[3].w;
int EvalShadow_GetSplitIndex( ShadowContext shadowContext, int index, real3 positionWS, out uint payloadOffset, out real alpha )
{
payloadOffset = shadowContext.shadowDatas[index].payloadOffset;
real4 weights = real4( distances2 < dirShadowSplitSphereSqRadii );
weights.yzw = saturate( weights.yzw - weights.xyz );
int i = 0;
real relDistance = 0.0;
real3 wposDir, splitSphere;
int idx = int( 4.0 - dot( weights, real4( 4.0, 3.0, 2.0, 1.0 ) ) );
relDistance = distances2[idx] / dirShadowSplitSphereSqRadii[idx];
return idx <= 3 ? idx : -1;
}
// find the current cascade
for( ; i < kMaxShadowCascades; i++, payloadOffset++ )
{
float4 sphere = asfloat( shadowContext.payloads[payloadOffset] );
wposDir = -sphere.xyz + positionWS;
float distSq = dot( wposDir, wposDir );
relDistance = distSq / sphere.w;
if( relDistance <= 1.0 )
{
splitSphere = sphere.xyz;
wposDir /= sqrt( distSq );
break;
}
}
int shadowSplitIndex = i < kMaxShadowCascades ? i : -1;
int EvalShadow_GetSplitSphereIndexForDirshadows( real3 positionWS, real4 dirShadowSplitSpheres[4] )
{
real relDist;
return EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres, relDist );
}
payloadOffset = shadowContext.shadowDatas[index].payloadOffset + kMaxShadowCascades;
real3 cascadeDir = asfloat( shadowContext.payloads[payloadOffset].xyz );
payloadOffset++;
real border = asfloat( shadowContext.payloads[payloadOffset][shadowSplitIndex] );
payloadOffset++;
alpha = border <= 0.0 ? 0.0 : saturate( (relDistance - (1.0 - border)) / border );
real cascDot = dot( cascadeDir, wposDir );
alpha = cascDot > 0.0 ? alpha : lerp( alpha, 0.0, saturate( -cascDot * 4.0 ) );
uint EvalShadow_LoadSplitSpheres( ShadowContext shadowContext, int index, out real4 splitSpheres[4] )
{
uint offset = GetPayloadOffset( shadowContext.shadowDatas[index] );
splitSpheres[0] = asfloat( shadowContext.payloads[offset + 0] );
splitSpheres[1] = asfloat( shadowContext.payloads[offset + 1] );
splitSpheres[2] = asfloat( shadowContext.payloads[offset + 2] );
splitSpheres[3] = asfloat( shadowContext.payloads[offset + 3] );
return offset + 4;
return shadowSplitIndex;
real4 dirShadowSplitSpheres[4];
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres );
real relDistance;
int shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres, relDistance );
uint payloadOffset;
real alpha;
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha );
real4 scales = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
real4 borders = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
real border = borders[shadowSplitIndex];
real alpha = border <= 0.0 ? 0.0 : saturate( (relDistance - (1.0 - border)) / border );
ShadowData sd = shadowContext.shadowDatas[index];
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd );
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex];
// sample the texture
uint texIdx, sampIdx;
UnpackShadowmapId( sd.id, texIdx, sampIdx );
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[shadowSplitIndex] * sd.texelSizeRcp.zw, sd.normalBias );
// Be careful of this code, we need it here before the if statement otherwise the compiler screws up optimizing dirShadowSplitSpheres VGPRs away
real3 splitSphere = dirShadowSplitSpheres[shadowSplitIndex].xyz;
real3 cascadeDir = normalize( -splitSphere + dirShadowSplitSpheres[min( shadowSplitIndex+1, kMaxShadowCascades-1 )].xyz );
real3 wposDir = normalize( -splitSphere + positionWS );
real cascDot = dot( cascadeDir, wposDir );
alpha = cascDot > 0.0 ? alpha : lerp( alpha, 0.0, saturate( -cascDot * 4.0 ) );
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( shadowContext, shadowAlgorithm, sd, texIdx, sampIdx, positionWS, normalWS, L, 1.0, false );
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, 1.0, recvBiasWeight, false );
real3 posNDC;
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, posNDC, true );
// sample the texture
uint texIdx, sampIdx;
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
real shadow1 = 1.0;
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, false );
// evaluate the first cascade
real2 sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS );
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
real shadow1 = 1.0;
shadowSplitIndex++;
if( shadowSplitIndex < kMaxShadowCascades )
if( alpha > 0.0 )
{
sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex];
positionWS = orig_pos + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[shadowSplitIndex] * sd.texelSizeRcp.zw, sd.normalBias );
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd );
positionWS = EvalShadow_ReceiverBias( sd, orig_pos, normalWS, L, 1.0, recvBiasWeight, false );
real3 posNDC;
UnpackShadowmapId( sd.id, slice );
sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS );
[branch]
UNITY_BRANCH
shadow1 = SampleShadow_SelectAlgorithm( shadowContext, sd, orig_payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
shadow1 = SampleShadow_SelectAlgorithm( shadowContext, sd, orig_payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
}
}
shadow = lerp( shadow, shadow1, alpha );
real EvalShadow_CascadedDepth_Blend( ShadowContext shadowContext, uint shadowAlgorithms[kMaxShadowCascades], Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
real EvalShadow_CascadedDepth_Blend( ShadowContext shadowContext, uint shadowAlgorithms[kMaxShadowCascades], Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
/* load the right shadow data for the current face */ \
real4 dirShadowSplitSpheres[kMaxShadowCascades]; \
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres ); \
real relDistance; \
int shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres, relDistance ); \
uint payloadOffset; \
real alpha; \
int shadowSplitIndex = EvalShadow_GetSplitIndex(shadowContext, index, positionWS, payloadOffset, alpha ); \
\
real4 scales = asfloat( shadowContext.payloads[payloadOffset] ); \
payloadOffset++; \
real4 borders = asfloat( shadowContext.payloads[payloadOffset] ); \
payloadOffset++; \
real border = borders[shadowSplitIndex]; \
real alpha = border <= 0.0 ? 0.0 : saturate( (relDistance - (1.0 - border)) / border ); \
ShadowData sd = shadowContext.shadowDatas[index]; \
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd ); \
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex]; \
real3 orig_pos = positionWS; \
real3 orig_pos = positionWS; \
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[shadowSplitIndex] * sd.texelSizeRcp.zw, sd.normalBias ); \
/* Be careful of this code, we need it here before the if statement otherwise the compiler screws up optimizing dirShadowSplitSpheres VGPRs away */ \
real3 splitSphere = dirShadowSplitSpheres[shadowSplitIndex].xyz; \
real3 cascadeDir = normalize( -splitSphere + dirShadowSplitSpheres[min( shadowSplitIndex+1, kMaxShadowCascades-1 )].xyz ); \
real3 wposDir = normalize( -splitSphere + positionWS ); \
real cascDot = dot( cascadeDir, wposDir ); \
alpha = cascDot > 0.0 ? alpha : lerp( alpha, 0.0, saturate( -cascDot * 4.0 ) ); \
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( sd, tex, samp, positionWS, normalWS, L, 1.0, false ); \
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, 1.0, recvBiasWeight, false ); \
real3 posNDC; \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, posNDC, true ); \
\
/* sample the texture */ \
real slice; \
UnpackShadowmapId( sd.id, slice ); \
\
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
real shadow1 = 1.0; \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, false ); \
/* evalute the first cascade */ \
real2 sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS ); \
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
real shadow1 = 1.0; \
\
shadowSplitIndex++; \
if( shadowSplitIndex < kMaxShadowCascades ) \
if( alpha > 0.0 ) \
{ \
sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex]; \
positionWS = orig_pos + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[shadowSplitIndex] * sd.texelSizeRcp.zw, sd.normalBias ); \
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd ); \
positionWS = EvalShadow_ReceiverBias( sd, orig_pos, normalWS, L, 1.0, recvBiasWeight, false ); \
real3 posNDC; \
UnpackShadowmapId( sd.id, slice ); \
sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS ); \
[branch] \
UNITY_BRANCH \
shadow1 = SampleShadow_SelectAlgorithm( shadowContext, sd, orig_payloadOffset, posTC, sd.bias, slice, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
shadow1 = SampleShadow_SelectAlgorithm( shadowContext, sd, orig_payloadOffset, posTC, sampleBias, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
} \
} \
shadow = lerp( shadow, shadow1, alpha ); \
real EvalShadow_CascadedDepth_Blend( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
real EvalShadow_CascadedDepth_Blend( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
{ \
uint shadowAlgorithms[kMaxShadowCascades] = { SHADOW_REPEAT_CASCADE( shadowAlgorithm ) }; \
return EvalShadow_CascadedDepth_Blend( shadowContext, shadowAlgorithms, tex, samp, positionWS, normalWS, index, L ); \
real EvalShadow_CascadedDepth_Dither( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L )
{
// load the right shadow data for the current face
real4 dirShadowSplitSpheres[kMaxShadowCascades];
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres );
real relDistance;
int shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres, relDistance );
uint payloadOffset;
real alpha;
int shadowSplitIndex = EvalShadow_GetSplitIndex(shadowContext, index, positionWS, payloadOffset, alpha);
real4 scales = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
real4 borders = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
real border = borders[shadowSplitIndex];
real alpha = border <= 0.0 ? 0.0 : saturate( (relDistance - (1.0 - border)) / border );
ShadowData sd = shadowContext.shadowDatas[index];
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd );
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex];
// get texture description
uint texIdx, sampIdx;
UnpackShadowmapId( sd.id, texIdx, sampIdx );
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[shadowSplitIndex] * sd.texelSizeRcp.zw, sd.normalBias );
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( shadowContext, shadowAlgorithm, sd, texIdx, sampIdx, positionWS, normalWS, L, 1.0, false );
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, 1.0, recvBiasWeight, false );
real3 posNDC;
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, posNDC, true );
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, false );
int nextSplit = min( shadowSplitIndex+1, kMaxShadowCascades-1 );
real3 splitSphere = dirShadowSplitSpheres[shadowSplitIndex].xyz;
real3 cascadeDir = normalize( -splitSphere + dirShadowSplitSpheres[min( 3, shadowSplitIndex + 1 )].xyz );
real3 wposDir = normalize( -splitSphere + positionWS );
real cascDot = dot( cascadeDir, wposDir );
alpha = cascDot > 0.0 ? alpha : lerp( alpha, 0.0, saturate( -cascDot * 4.0 ) );
int nextSplit = min( shadowSplitIndex+1, kMaxShadowCascades-1 );
sd = shadowContext.shadowDatas[index + 1 + nextSplit];
positionWS = orig_pos + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[nextSplit] * sd.texelSizeRcp.zw, sd.normalBias );
posTC = EvalShadow_GetTexcoords( sd, positionWS );
EvalShadow_LoadCascadeData( shadowContext, index + 1 + nextSplit, sd );
positionWS = EvalShadow_ReceiverBias( sd, orig_pos, normalWS, L, 1.0, recvBiasWeight, false );
posTC = EvalShadow_GetTexcoords( sd, positionWS, false );
uint texIdx, sampIdx;
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
uint shadowType, shadowAlgorithm;
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithm, texIdx, sampIdx );
real2 sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS );
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
real EvalShadow_CascadedDepth_Dither( ShadowContext shadowContext, uint shadowAlgorithms[kMaxShadowCascades], Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
real EvalShadow_CascadedDepth_Dither( ShadowContext shadowContext, uint shadowAlgorithms[kMaxShadowCascades], Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
real4 dirShadowSplitSpheres[kMaxShadowCascades]; \
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres ); \
real relDistance; \
int shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres, relDistance ); \
uint payloadOffset; \
real alpha; \
int shadowSplitIndex = EvalShadow_GetSplitIndex(shadowContext, index, positionWS, payloadOffset, alpha ); \
\
\
real4 scales = asfloat( shadowContext.payloads[payloadOffset] ); \
payloadOffset++; \
real4 borders = asfloat( shadowContext.payloads[payloadOffset] ); \
payloadOffset++; \
real border = borders[shadowSplitIndex]; \
real alpha = border <= 0.0 ? 0.0 : saturate( (relDistance - (1.0 - border)) / border ); \
\
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex]; \
\
ShadowData sd = shadowContext.shadowDatas[index]; \
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd ); \
\
real3 orig_pos = positionWS; \
positionWS += EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[shadowSplitIndex] * sd.texelSizeRcp.zw, sd.normalBias ); \
real3 orig_pos = positionWS; \
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( sd, tex, samp, positionWS, normalWS, L, 1.0, false ); \
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, 1.0, recvBiasWeight, false ); \
real3 posNDC; \
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, posNDC, true ); \
\
int nextSplit = min( shadowSplitIndex+1, kMaxShadowCascades-1 ); \
real3 splitSphere = dirShadowSplitSpheres[shadowSplitIndex].xyz; \
real3 cascadeDir = normalize( -splitSphere + dirShadowSplitSpheres[nextSplit].xyz ); \
real3 wposDir = normalize( -splitSphere + positionWS ); \
real cascDot = dot( cascadeDir, wposDir ); \
alpha = cascDot > 0.0 ? alpha : lerp( alpha, 0.0, saturate( -cascDot * 4.0 ) ); \
\
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, false ); \
\
int nextSplit = min( shadowSplitIndex+1, kMaxShadowCascades-1 ); \
\
sd = shadowContext.shadowDatas[index + 1 + nextSplit]; \
positionWS = orig_pos + EvalShadow_NormalBias( normalWS, saturate( dot( normalWS, L ) ), scales[nextSplit] * sd.texelSizeRcp.zw, sd.normalBias ); \
posTC = EvalShadow_GetTexcoords( sd, positionWS ); \
EvalShadow_LoadCascadeData( shadowContext, index + 1 + nextSplit, sd ); \
positionWS = EvalShadow_ReceiverBias( sd, orig_pos, normalWS, L, 1.0, recvBiasWeight, false ); \
posTC = EvalShadow_GetTexcoords( sd, positionWS, false ); \
real slice; \
UnpackShadowmapId( sd.id, slice ); \
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sd.bias, slice, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
real2 sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS ); \
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
\
real EvalShadow_CascadedDepth_Dither( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
\
real EvalShadow_CascadedDepth_Dither( ShadowContext shadowContext, uint shadowAlgorithm, Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
{ \
uint shadowAlgorithms[kMaxShadowCascades] = { SHADOW_REPEAT_CASCADE( shadowAlgorithm ) }; \
return EvalShadow_CascadedDepth_Dither( shadowContext, shadowAlgorithms, tex, samp, positionWS, normalWS, index, L ); \
// get the algorithm
ShadowData sd = shadowContext.shadowDatas[index];
// load the right shadow data for the current face
int faceIndex = EvalShadow_GetCubeFaceID( L ) + 1;
int faceIndex = CubeMapFaceID( -L ) + 1;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, slice );
UnpackShadowmapId( sd.id, texIdx, sampIdx );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
// get the algorithm
ShadowData sd = shadowContext.shadowDatas[index];
// load the right shadow data for the current face
int faceIndex = EvalShadow_GetCubeFaceID( L ) + 1;
int faceIndex = CubeMapFaceID( -L ) + 1;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
real slice;
UnpackShadowmapId(sd.id, slice);
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, slice, 0 ).x;
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
ShadowData sd = shadowContext.shadowDatas[index];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, slice );
UnpackShadowmapId( sd.id, texIdx, sampIdx );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
ShadowData sd = shadowContext.shadowDatas[index];
real4 closestNDC = { 0,0,0,1 };
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
real slice;
UnpackShadowmapId(sd.id, slice);
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, slice, 0 ).x;
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
uint shadowType;
UnpackShadowType( sd.shadowType, shadowType );
// load the right shadow data for the current face
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (EvalShadow_GetCubeFaceID( L ) + 1) : 0;
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (CubeMapFaceID( -L ) + 1) : 0;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, slice );
UnpackShadowmapId( sd.id, texIdx, sampIdx );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
uint shadowType;
UnpackShadowType( sd.shadowType, shadowType );
// load the right shadow data for the current face
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (EvalShadow_GetCubeFaceID( L ) + 1) : 0;
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (CubeMapFaceID( -L ) + 1) : 0;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
real slice;
UnpackShadowmapId(sd.id, slice);
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, slice, 0 ).x;
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
real3 EvalShadow_GetClosestSample_Cascade( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real4 L )
{
// load the right shadow data for the current face
real4 dirShadowSplitSpheres[4];
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres );
real relDistance;
int shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres, relDistance );
uint payloadOffset;
real alpha;
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha );
real4 scales = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
real4 borders = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, false );
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, slice );
UnpackShadowmapId( sd.id, texIdx, sampIdx );
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
real3 EvalShadow_GetClosestSample_Cascade( ShadowContext shadowContext, Texture2DArray tex, real3 positionWS, real3 normalWS, int index, real4 L )
{
// load the right shadow data for the current face
real4 dirShadowSplitSpheres[4];
uint payloadOffset = EvalShadow_LoadSplitSpheres( shadowContext, index, dirShadowSplitSpheres );
real relDistance;
int shadowSplitIndex = EvalShadow_GetSplitSphereIndexForDirshadows( positionWS, dirShadowSplitSpheres, relDistance );
uint payloadOffset;
real alpha;
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha );
real4 scales = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
real4 borders = asfloat( shadowContext.payloads[payloadOffset] );
payloadOffset++;
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy );
uint2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, false );
real slice;
UnpackShadowmapId( sd.id, texIdx, sampIdx, slice );
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, slice, 0 ).x;
UnpackShadowmapId( sd.id, texIdx, sampIdx );
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;
// reconstruct depth position
real4 closestWS = mul( closestNDC, sd.shadowToWorld );