您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
883 行
60 KiB
883 行
60 KiB
// Various shadow algorithms
|
|
// 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.
|
|
|
|
float4 EvalShadow_WorldToShadow( ShadowData sd, real3 positionWS, bool perspProj )
|
|
{
|
|
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 ) );
|
|
}
|
|
// function called by spot, point and directional eval routines to calculate shadow coordinates
|
|
real3 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real3 posNDC, bool perspProj )
|
|
{
|
|
real4 posCS = EvalShadow_WorldToShadow( sd, positionWS, perspProj );
|
|
posNDC = perspProj ? (posCS.xyz / posCS.w) : posCS.xyz;
|
|
// calc TCs
|
|
real3 posTC = real3( posNDC.xy * 0.5 + 0.5, posNDC.z );
|
|
posTC.xy = posTC.xy * sd.scaleOffset.xy + sd.scaleOffset.zw;
|
|
|
|
return posTC;
|
|
}
|
|
|
|
real3 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, bool perspProj )
|
|
{
|
|
real3 ndc;
|
|
return EvalShadow_GetTexcoords( sd, positionWS, ndc, perspProj );
|
|
}
|
|
|
|
real2 EvalShadow_GetTexcoords( ShadowData sd, real3 positionWS, out real2 closestSampleNDC, bool perspProj )
|
|
{
|
|
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;
|
|
return posTC * sd.scaleOffset.xy + sd.scaleOffset.zw;
|
|
}
|
|
|
|
uint2 EvalShadow_GetIntTexcoords( ShadowData sd, real3 positionWS, out real2 closestSampleNDC, bool perspProj )
|
|
{
|
|
real2 texCoords = EvalShadow_GetTexcoords(sd, positionWS, closestSampleNDC, perspProj);
|
|
return uint2(texCoords * sd.textureSize.xy);
|
|
}
|
|
|
|
//
|
|
// Biasing functions
|
|
//
|
|
|
|
// helper function to get the world texel size
|
|
real EvalShadow_WorldTexelSize( ShadowData sd, float L_dist, bool perspProj )
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
#else
|
|
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;
|
|
|
|
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
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
e1 = float3( 1.0, 0.0, -normalWS.x / normalWS.z );
|
|
e2 = float3( 0.0, 1.0, -normalWS.y / normalWS.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
|
|
{
|
|
e1 = float3( -normalWS.y / normalWS.x, 1.0, 0.0 );
|
|
e2 = float3( -normalWS.z / normalWS.x, 0.0, 1.0 );
|
|
}
|
|
|
|
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
|
|
|
|
|
|
//
|
|
// Point shadows
|
|
//
|
|
real EvalShadow_PointDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist )
|
|
{
|
|
ShadowData sd = shadowContext.shadowDatas[index + CubeMapFaceID( -L ) + 1];
|
|
// get the algorithm
|
|
uint shadowType, shadowAlgorithm;
|
|
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
|
|
// get the texture
|
|
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 );
|
|
// 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, 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 )
|
|
#undef EvalShadow_PointDepth_
|
|
|
|
//
|
|
// Spot shadows
|
|
//
|
|
real EvalShadow_SpotDepth( ShadowContext shadowContext, 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];
|
|
// get the algorithm
|
|
uint shadowType, shadowAlgorithm;
|
|
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
|
|
// sample the texture according to the given algorithm
|
|
uint 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, 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 )
|
|
#undef EvalShadow_SpotDepth_
|
|
|
|
//
|
|
// Punctual shadows for Point and Spot
|
|
//
|
|
real EvalShadow_PunctualDepth( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L, real L_dist )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
uint shadowType, shadowAlgorithm;
|
|
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
|
|
|
|
// 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;
|
|
}
|
|
|
|
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 );
|
|
// 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_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 ); \
|
|
}
|
|
EvalShadow_PunctualDepth_( SamplerComparisonState )
|
|
EvalShadow_PunctualDepth_( SamplerState )
|
|
#undef EvalShadow_PunctualDepth_
|
|
|
|
|
|
//
|
|
// Directional shadows (cascaded shadow map)
|
|
//
|
|
|
|
#define kMaxShadowCascades 4
|
|
#define SHADOW_REPEAT_CASCADE( _x ) _x, _x, _x, _x
|
|
|
|
void EvalShadow_LoadCascadeData( ShadowContext shadowContext, uint index, inout ShadowData sd )
|
|
{
|
|
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;
|
|
}
|
|
|
|
int EvalShadow_GetSplitIndex( ShadowContext shadowContext, int index, real3 positionWS, out uint payloadOffset, out real alpha, out int cascadeCount )
|
|
{
|
|
payloadOffset = shadowContext.shadowDatas[index].payloadOffset;
|
|
|
|
int i = 0;
|
|
real relDistance = 0.0;
|
|
real3 wposDir, splitSphere;
|
|
|
|
// 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 > 0.0 && relDistance <= 1.0 )
|
|
{
|
|
splitSphere = sphere.xyz;
|
|
wposDir /= sqrt( distSq );
|
|
break;
|
|
}
|
|
}
|
|
int shadowSplitIndex = i < kMaxShadowCascades ? i : -1;
|
|
|
|
payloadOffset = shadowContext.shadowDatas[index].payloadOffset + kMaxShadowCascades;
|
|
real3 cascadeDir = asfloat( shadowContext.payloads[payloadOffset].xyz );
|
|
cascadeCount = shadowContext.payloads[payloadOffset].w;
|
|
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 = lerp( alpha, 0.0, saturate( -cascDot * 4.0 ) );
|
|
|
|
return shadowSplitIndex;
|
|
}
|
|
|
|
real EvalShadow_CascadedDepth_Blend( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L )
|
|
{
|
|
// load the right shadow data for the current face
|
|
uint payloadOffset;
|
|
real alpha;
|
|
int cascadeCount;
|
|
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha, cascadeCount );
|
|
|
|
if( shadowSplitIndex < 0 )
|
|
return 1.0;
|
|
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd );
|
|
|
|
// sample the texture
|
|
uint texIdx, sampIdx;
|
|
UnpackShadowmapId( sd.id, texIdx, sampIdx );
|
|
uint shadowType, shadowAlgorithm;
|
|
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
|
|
|
|
// normal based bias
|
|
real3 orig_pos = positionWS;
|
|
uint orig_payloadOffset = payloadOffset;
|
|
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 );
|
|
|
|
// get shadowmap texcoords
|
|
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 < cascadeCount )
|
|
{
|
|
shadow1 = shadow;
|
|
|
|
if( alpha > 0.0 )
|
|
{
|
|
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd );
|
|
positionWS = EvalShadow_ReceiverBias( sd, orig_pos, normalWS, L, 1.0, recvBiasWeight, false );
|
|
real3 posNDC;
|
|
posTC = EvalShadow_GetTexcoords( sd, positionWS, posNDC, false );
|
|
// sample the texture
|
|
sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS );
|
|
|
|
UNITY_BRANCH
|
|
if( all( abs( posNDC.xy ) <= (1.0 - sd.texelSizeRcp.zw * 0.5) ) )
|
|
shadow1 = SampleShadow_SelectAlgorithm( shadowContext, sd, orig_payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
|
|
}
|
|
}
|
|
shadow = lerp( shadow, shadow1, alpha );
|
|
return shadow;
|
|
}
|
|
|
|
|
|
#define EvalShadow_CascadedDepth_( _samplerType ) \
|
|
real EvalShadow_CascadedDepth_Blend( ShadowContext shadowContext, uint shadowAlgorithms[kMaxShadowCascades], Texture2DArray tex, _samplerType samp, real3 positionWS, real3 normalWS, int index, real3 L ) \
|
|
{ \
|
|
uint payloadOffset; \
|
|
real alpha; \
|
|
int cascadeCount; \
|
|
int shadowSplitIndex = EvalShadow_GetSplitIndex(shadowContext, index, positionWS, payloadOffset, alpha, cascadeCount ); \
|
|
\
|
|
if( shadowSplitIndex < 0 ) \
|
|
return 1.0; \
|
|
\
|
|
ShadowData sd = shadowContext.shadowDatas[index]; \
|
|
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd ); \
|
|
\
|
|
/* normal based bias */ \
|
|
real3 orig_pos = positionWS; \
|
|
uint orig_payloadOffset = payloadOffset; \
|
|
real recvBiasWeight = EvalShadow_ReceiverBiasWeight( sd, tex, samp, positionWS, normalWS, L, 1.0, false ); \
|
|
positionWS = EvalShadow_ReceiverBias( sd, positionWS, normalWS, L, 1.0, recvBiasWeight, false ); \
|
|
\
|
|
/* get shadowmap texcoords */ \
|
|
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 < cascadeCount ) \
|
|
{ \
|
|
shadow1 = shadow; \
|
|
\
|
|
if( alpha > 0.0 ) \
|
|
{ \
|
|
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd ); \
|
|
positionWS = EvalShadow_ReceiverBias( sd, orig_pos, normalWS, L, 1.0, recvBiasWeight, false ); \
|
|
real3 posNDC; \
|
|
posTC = EvalShadow_GetTexcoords( sd, positionWS, posNDC, false ); \
|
|
/* sample the texture */ \
|
|
sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS ); \
|
|
\
|
|
UNITY_BRANCH \
|
|
if( all( abs( posNDC.xy ) <= (1.0 - sd.texelSizeRcp.zw * 0.5) ) ) \
|
|
shadow1 = SampleShadow_SelectAlgorithm( shadowContext, sd, orig_payloadOffset, posTC, sampleBias, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
|
|
} \
|
|
} \
|
|
shadow = lerp( shadow, shadow1, alpha ); \
|
|
return shadow; \
|
|
} \
|
|
\
|
|
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 ); \
|
|
}
|
|
|
|
EvalShadow_CascadedDepth_( SamplerComparisonState )
|
|
EvalShadow_CascadedDepth_( SamplerState )
|
|
#undef EvalShadow_CascadedDepth_
|
|
|
|
|
|
real EvalShadow_hash12( real2 pos )
|
|
{
|
|
real3 p3 = frac( pos.xyx * real3( 443.8975, 397.2973, 491.1871 ) );
|
|
p3 += dot( p3, p3.yzx + 19.19 );
|
|
return frac( (p3.x + p3.y) * p3.z );
|
|
}
|
|
|
|
real EvalShadow_CascadedDepth_Dither( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real3 L )
|
|
{
|
|
// load the right shadow data for the current face
|
|
uint payloadOffset;
|
|
real alpha;
|
|
int cascadeCount;
|
|
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha, cascadeCount );
|
|
|
|
if( shadowSplitIndex < 0 )
|
|
return 1.0;
|
|
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd );
|
|
|
|
// get texture description
|
|
uint texIdx, sampIdx;
|
|
UnpackShadowmapId( sd.id, texIdx, sampIdx );
|
|
uint shadowType, shadowAlgorithm;
|
|
UnpackShadowType( sd.shadowType, shadowType, shadowAlgorithm );
|
|
|
|
// normal based bias
|
|
real3 orig_pos = positionWS;
|
|
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 );
|
|
// get shadowmap texcoords
|
|
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, false );
|
|
|
|
int nextSplit = min( shadowSplitIndex+1, cascadeCount-1 );
|
|
|
|
if( shadowSplitIndex < nextSplit && step( EvalShadow_hash12( posTC.xy ), alpha ) )
|
|
{
|
|
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 );
|
|
}
|
|
// sample the texture
|
|
real2 sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS );
|
|
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithm, texIdx, sampIdx );
|
|
return shadowSplitIndex < (cascadeCount-1) ? shadow : lerp( shadow, 1.0, alpha );
|
|
}
|
|
|
|
#define EvalShadow_CascadedDepth_( _samplerType ) \
|
|
real EvalShadow_CascadedDepth_Dither( 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 */ \
|
|
uint payloadOffset; \
|
|
real alpha; \
|
|
int cascadeCount; \
|
|
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha, cascadeCount ); \
|
|
\
|
|
if( shadowSplitIndex < 0 ) \
|
|
return 1.0; \
|
|
\
|
|
ShadowData sd = shadowContext.shadowDatas[index]; \
|
|
EvalShadow_LoadCascadeData( shadowContext, index + 1 + shadowSplitIndex, sd ); \
|
|
\
|
|
/* normal based bias */ \
|
|
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 ); \
|
|
/* get shadowmap texcoords */ \
|
|
real3 posTC = EvalShadow_GetTexcoords( sd, positionWS, false ); \
|
|
\
|
|
int nextSplit = min( shadowSplitIndex+1, cascadeCount-1 ); \
|
|
\
|
|
if( shadowSplitIndex != nextSplit && step( EvalShadow_hash12( posTC.xy ), alpha ) ) \
|
|
{ \
|
|
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 ); \
|
|
} \
|
|
/* sample the texture */ \
|
|
real2 sampleBias = EvalShadow_SampleBias_Ortho( sd, normalWS ); \
|
|
real shadow = SampleShadow_SelectAlgorithm( shadowContext, sd, payloadOffset, posTC, sampleBias, shadowAlgorithms[shadowSplitIndex], tex, samp ); \
|
|
return shadowSplitIndex < (cascadeCount-1) ? shadow : lerp( shadow, 1.0, alpha ); \
|
|
} \
|
|
\
|
|
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 ); \
|
|
}
|
|
|
|
|
|
EvalShadow_CascadedDepth_( SamplerComparisonState )
|
|
EvalShadow_CascadedDepth_( SamplerState )
|
|
#undef EvalShadow_CascadedDepth_
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
real3 EvalShadow_GetClosestSample_Point( ShadowContext shadowContext, real3 positionWS, int index, real3 L )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
// load the right shadow data for the current face
|
|
int faceIndex = CubeMapFaceID( -L ) + 1;
|
|
sd = shadowContext.shadowDatas[index + faceIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
|
|
|
|
// load the texel
|
|
uint texIdx, sampIdx;
|
|
UnpackShadowmapId( sd.id, texIdx, sampIdx );
|
|
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
|
|
real3 EvalShadow_GetClosestSample_Point( ShadowContext shadowContext, Texture2DArray tex, real3 positionWS, int index, real3 L )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
// load the right shadow data for the current face
|
|
int faceIndex = CubeMapFaceID( -L ) + 1;
|
|
sd = shadowContext.shadowDatas[index + faceIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
|
|
|
|
// load the texel
|
|
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
real3 EvalShadow_GetClosestSample_Spot( ShadowContext shadowContext, real3 positionWS, int index )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
|
|
|
|
// load the texel
|
|
uint texIdx, sampIdx;
|
|
UnpackShadowmapId( sd.id, texIdx, sampIdx );
|
|
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
|
|
real3 EvalShadow_GetClosestSample_Spot( ShadowContext shadowContext, Texture2DArray tex, real3 positionWS, int index )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
|
|
|
|
// load the texel
|
|
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
real3 EvalShadow_GetClosestSample_Punctual( ShadowContext shadowContext, real3 positionWS, int index, real3 L )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
uint shadowType;
|
|
UnpackShadowType( sd.shadowType, shadowType );
|
|
// load the right shadow data for the current face
|
|
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (CubeMapFaceID( -L ) + 1) : 0;
|
|
sd = shadowContext.shadowDatas[index + faceIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
|
|
|
|
// load the texel
|
|
uint texIdx, sampIdx;
|
|
UnpackShadowmapId( sd.id, texIdx, sampIdx );
|
|
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
real3 EvalShadow_GetClosestSample_Punctual( ShadowContext shadowContext, Texture2DArray tex, real3 positionWS, int index, real3 L )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
uint shadowType;
|
|
UnpackShadowType( sd.shadowType, shadowType );
|
|
// load the right shadow data for the current face
|
|
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (CubeMapFaceID( -L ) + 1) : 0;
|
|
sd = shadowContext.shadowDatas[index + faceIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, true );
|
|
|
|
// load the texel
|
|
closestNDC.z = LOAD_TEXTURE2D_ARRAY_LOD( tex, texelIdx, sd.slice, 0 ).x;
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
real EvalShadow_SampleClosestDistance_Punctual( ShadowContext shadowContext, Texture2DArray tex, SamplerState sampl,
|
|
real3 positionWS, int index, real3 L, real3 lightPositionWS )
|
|
{
|
|
// get the algorithm
|
|
ShadowData sd = shadowContext.shadowDatas[index];
|
|
uint shadowType;
|
|
UnpackShadowType( sd.shadowType, shadowType );
|
|
// load the right shadow data for the current face
|
|
int faceIndex = shadowType == GPUSHADOWTYPE_POINT ? (CubeMapFaceID( -L ) + 1) : 0;
|
|
sd = shadowContext.shadowDatas[index + faceIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
real2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, true );
|
|
|
|
// sample the shadow map
|
|
closestNDC.z = SAMPLE_TEXTURE2D_ARRAY_LOD( tex, sampl, texelIdx, sd.slice, 0 ).x;
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
real3 occluderPosWS = closestWS.xyz / closestWS.w;
|
|
|
|
return distance( occluderPosWS, lightPositionWS );
|
|
}
|
|
|
|
real3 EvalShadow_GetClosestSample_Cascade( ShadowContext shadowContext, real3 positionWS, real3 normalWS, int index, real4 L )
|
|
{
|
|
// load the right shadow data for the current face
|
|
uint payloadOffset;
|
|
real alpha;
|
|
int cascadeCount;
|
|
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha, cascadeCount );
|
|
|
|
if( shadowSplitIndex < 0 )
|
|
return 0.0;
|
|
|
|
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, false );
|
|
|
|
// load the texel
|
|
uint texIdx, sampIdx;
|
|
UnpackShadowmapId( sd.id, texIdx, sampIdx );
|
|
closestNDC.z = LoadShadow_T2DA( shadowContext, texIdx, texelIdx, sd.slice );
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
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
|
|
uint payloadOffset;
|
|
real alpha;
|
|
int cascadeCount;
|
|
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha, cascadeCount );
|
|
|
|
if( shadowSplitIndex < 0 )
|
|
return 0.0;
|
|
|
|
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
uint2 texelIdx = EvalShadow_GetIntTexcoords( sd, positionWS, closestNDC.xy, false );
|
|
|
|
// load the texel
|
|
uint texIdx, sampIdx;
|
|
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 );
|
|
return closestWS.xyz / closestWS.w;
|
|
}
|
|
|
|
real EvalShadow_SampleClosestDistance_Cascade( ShadowContext shadowContext, Texture2DArray tex, SamplerState sampl,
|
|
real3 positionWS, real3 normalWS, int index, real4 L, out real3 nearPlanePositionWS )
|
|
{
|
|
// load the right shadow data for the current face
|
|
uint payloadOffset;
|
|
real alpha;
|
|
int cascadeCount;
|
|
int shadowSplitIndex = EvalShadow_GetSplitIndex( shadowContext, index, positionWS, payloadOffset, alpha, cascadeCount );
|
|
|
|
if( shadowSplitIndex < 0 )
|
|
return 0.0;
|
|
|
|
ShadowData sd = shadowContext.shadowDatas[index + 1 + shadowSplitIndex];
|
|
|
|
real4 closestNDC = { 0,0,0,1 };
|
|
real2 texelIdx = EvalShadow_GetTexcoords( sd, positionWS, closestNDC.xy, false );
|
|
|
|
// sample the shadow map
|
|
uint texIdx, sampIdx;
|
|
UnpackShadowmapId( sd.id, texIdx, sampIdx );
|
|
closestNDC.z = SAMPLE_TEXTURE2D_ARRAY_LOD( tex, sampl, texelIdx, sd.slice, 0 ).x;
|
|
|
|
// reconstruct depth position
|
|
real4 closestWS = mul( closestNDC, sd.shadowToWorld );
|
|
real3 occluderPosWS = closestWS.xyz / closestWS.w;
|
|
|
|
// TODO: avoid the matrix multiplication here.
|
|
real4 nearPlanePos = mul( real4( 0,0,1,1 ), sd.shadowToWorld ); // Note the reversed Z
|
|
nearPlanePositionWS = nearPlanePos.xyz / nearPlanePos.w;
|
|
|
|
return distance( occluderPosWS, nearPlanePositionWS );
|
|
}
|