|
@@ -6,6 +6,10 @@
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
+#ifndef USING_COMPUTE_SHADER_DIRECTIONAL_LIGHT
|
|
|
+#define USING_COMPUTE_SHADER_DIRECTIONAL_LIGHT 0
|
|
|
+#endif
|
|
|
+
|
|
|
enum class ShadowFilterMethod {None, Pcf, Esm, EsmPcf};
|
|
|
|
|
|
// avoiding artifact between cascade levels.
|
|
@@ -22,11 +26,11 @@ class DirectionalShadowCalculator
|
|
|
{
|
|
|
m_blendBetweenCascadesEnable = enable;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
void SetShadowmaps(Texture2DArray<float> dirShadowMap, Texture2DArray<float> expShadowmap)
|
|
|
{
|
|
|
- m_directionalLightShadowmap = dirShadowMap;
|
|
|
- m_directionalLightExponentialShadowmap = expShadowmap;
|
|
|
+ m_directionalLightShadowmap = dirShadowMap;
|
|
|
+ m_directionalLightExponentialShadowmap = expShadowmap;
|
|
|
}
|
|
|
|
|
|
void SetReceiverShadowPlaneBiasEnable(const bool enable)
|
|
@@ -38,17 +42,17 @@ class DirectionalShadowCalculator
|
|
|
{
|
|
|
m_worldNormal = worldNormal;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
void SetWorldPos(const float3 worldPos)
|
|
|
{
|
|
|
m_worldPos = worldPos;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
void SetLightIndex(const int lightIndex)
|
|
|
{
|
|
|
- m_lightIndex = lightIndex;
|
|
|
+ m_lightIndex = lightIndex;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
void SetFilterMode(const ShadowFilterMethod filterMode)
|
|
|
{
|
|
|
m_filterMode = filterMode;
|
|
@@ -57,18 +61,18 @@ class DirectionalShadowCalculator
|
|
|
|
|
|
bool IsShadowCoordInCascade(float3 shadowCoord, uint shadowMapSize);
|
|
|
|
|
|
- float GetVisibility();
|
|
|
+ float GetVisibility();
|
|
|
float GetThickness();
|
|
|
-
|
|
|
- float GetVisibilityFromLightNoFilter();
|
|
|
- float SamplePcfBicubic(const float3 shadowCoord, const uint indexOfCascade);
|
|
|
- float GetVisibilityFromLightPcf();
|
|
|
- float GetVisibilityFromLightEsm();
|
|
|
+
|
|
|
+ float GetVisibilityFromLightNoFilter();
|
|
|
+ float SamplePcfBicubic(const float3 shadowCoord, const int indexOfCascade);
|
|
|
+ float GetVisibilityFromLightPcf();
|
|
|
+ float GetVisibilityFromLightEsm();
|
|
|
float GetVisibilityFromLightEsmPcf();
|
|
|
float CalculateCascadeBlendAmount(const float3 texCoord);
|
|
|
- bool2 IsShadowed(const float3 shadowCoord, const uint indexOfCascade);
|
|
|
+ bool2 IsShadowed(const float3 shadowCoord, const int indexOfCascade);
|
|
|
void ComputeShadowCoords();
|
|
|
-
|
|
|
+
|
|
|
float3 m_shadowCoords[ViewSrg::MaxCascadeCount];
|
|
|
float m_slopeBias[ViewSrg::MaxCascadeCount];
|
|
|
float3 m_shadowPosDX[ViewSrg::MaxCascadeCount];
|
|
@@ -81,6 +85,7 @@ class DirectionalShadowCalculator
|
|
|
|
|
|
int m_lightIndex;
|
|
|
int m_cascadeIndex;
|
|
|
+ int m_cascadeCount;
|
|
|
ShadowFilterMethod m_filterMode;
|
|
|
|
|
|
Texture2DArray<float> m_directionalLightShadowmap;
|
|
@@ -106,16 +111,16 @@ float DirectionalShadowCalculator::GetThickness()
|
|
|
Texture2DArray<float> shadowmap = m_directionalLightShadowmap;
|
|
|
|
|
|
if(m_cascadeIndex >= 0)
|
|
|
- {
|
|
|
+ {
|
|
|
float3 shadowCoord = m_shadowCoords[m_cascadeIndex];
|
|
|
if (IsShadowCoordInCascade(shadowCoord, size))
|
|
|
{
|
|
|
- const float depthBufferValue = shadowmap.Sample(PassSrg::LinearSampler, float3(shadowCoord.xy, m_cascadeIndex)).r;
|
|
|
-
|
|
|
+ const float depthBufferValue = shadowmap.SampleLevel(PassSrg::LinearSampler, float3(shadowCoord.xy, m_cascadeIndex), 0).r;
|
|
|
+
|
|
|
// Normalized thickness (avoid negative values given by precision errors or shrinking offsets)
|
|
|
const float deltaDepth = max(shadowCoord.z - depthBufferValue,0.0);
|
|
|
|
|
|
- const float viewSpaceThickness = ViewSrg::m_directionalLightShadows[m_lightIndex].m_far_minus_near * deltaDepth;
|
|
|
+ const float viewSpaceThickness = ViewSrg::m_directionalLightShadows[m_lightIndex].m_far_minus_near * deltaDepth;
|
|
|
return viewSpaceThickness;
|
|
|
}
|
|
|
}
|
|
@@ -131,16 +136,17 @@ void DirectionalShadowCalculator::ComputeShadowCoords()
|
|
|
const float4x4 worldToLightViewMatrices[ViewSrg::MaxCascadeCount] = ViewSrg::m_directionalLightShadows[m_lightIndex].m_worldToLightViewMatrices;
|
|
|
|
|
|
const uint size = ViewSrg::m_directionalLightShadows[m_lightIndex].m_shadowmapSize;
|
|
|
- const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
|
|
|
const float3 shadowOffset = ComputeNormalShadowOffset(ViewSrg::m_directionalLightShadows[m_lightIndex].m_normalShadowBias, m_worldNormal, ViewSrg::m_directionalLightShadows[m_lightIndex].m_shadowmapSize);
|
|
|
|
|
|
+ m_cascadeCount = min(ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount, ViewSrg::MaxCascadeCount);
|
|
|
m_cascadeIndex = -1;
|
|
|
- for (uint index = 0; index < cascadeCount; ++index)
|
|
|
- {
|
|
|
+
|
|
|
+ for (int index = 0; index < m_cascadeCount; ++index)
|
|
|
+ {
|
|
|
float4 lightSpacePos = mul(worldToLightViewMatrices[index], float4(m_worldPos + shadowOffset, 1.));
|
|
|
lightSpacePos.z += shadowBias;
|
|
|
-
|
|
|
- const float4 clipSpacePos = mul(lightViewToShadowmapMatrices[index], lightSpacePos);
|
|
|
+
|
|
|
+ const float4 clipSpacePos = mul(lightViewToShadowmapMatrices[index], lightSpacePos);
|
|
|
m_shadowCoords[index] = clipSpacePos.xyz / clipSpacePos.w;
|
|
|
|
|
|
// Cache Cascade index
|
|
@@ -149,19 +155,21 @@ void DirectionalShadowCalculator::ComputeShadowCoords()
|
|
|
m_cascadeIndex = index;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+// Todo: No ddx in compute, find a way to leverage world space normal for this
|
|
|
+#if !USING_COMPUTE_SHADER_DIRECTIONAL_LIGHT
|
|
|
if (m_receiverShadowPlaneBiasEnable)
|
|
|
{
|
|
|
- [unroll]
|
|
|
- for(int i = 0 ; i < ViewSrg::MaxCascadeCount ; ++i)
|
|
|
+ for(int i = 0 ; i < m_cascadeCount; ++i)
|
|
|
{
|
|
|
m_shadowPosDX[i] = ddx_fine(m_shadowCoords[i]);
|
|
|
- m_shadowPosDY[i] = ddy_fine(m_shadowCoords[i]);
|
|
|
- }
|
|
|
- }
|
|
|
+ m_shadowPosDY[i] = ddy_fine(m_shadowCoords[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
-bool2 DirectionalShadowCalculator::IsShadowed(const float3 shadowCoord, const uint indexOfCascade)
|
|
|
+bool2 DirectionalShadowCalculator::IsShadowed(const float3 shadowCoord, const int indexOfCascade)
|
|
|
{
|
|
|
// size is the shadowap's width and height.
|
|
|
const uint size = ViewSrg::m_directionalLightShadows[m_lightIndex].m_shadowmapSize;
|
|
@@ -170,7 +178,7 @@ bool2 DirectionalShadowCalculator::IsShadowed(const float3 shadowCoord, const ui
|
|
|
if (IsShadowCoordInCascade(shadowCoord, size))
|
|
|
{
|
|
|
const float3 coord = float3(shadowCoord.xy, indexOfCascade);
|
|
|
- const float depthInShadowmap = m_directionalLightShadowmap.Sample(PassSrg::LinearSampler, coord).r;
|
|
|
+ const float depthInShadowmap = m_directionalLightShadowmap.SampleLevel(PassSrg::LinearSampler, coord, 0).r;
|
|
|
const float depthDiff = shadowCoord.z - depthInShadowmap;
|
|
|
return bool2(true, (depthDiff > m_slopeBias[indexOfCascade]));
|
|
|
}
|
|
@@ -178,7 +186,7 @@ bool2 DirectionalShadowCalculator::IsShadowed(const float3 shadowCoord, const ui
|
|
|
}
|
|
|
|
|
|
float DirectionalShadowCalculator::GetVisibility()
|
|
|
-{
|
|
|
+{
|
|
|
ComputeShadowCoords();
|
|
|
|
|
|
// Todo: slope bias exhibits noticeable artifacts, especially with sharp normal values derived from fullscreen
|
|
@@ -196,12 +204,12 @@ float DirectionalShadowCalculator::GetVisibility()
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Calculate slope bias
|
|
|
NdotL = max(NdotL, 0.01);
|
|
|
const float sinTheta = sqrt(1 - NdotL * NdotL);
|
|
|
const float tanTheta = sinTheta / NdotL;
|
|
|
- for (uint cascadeIndex = 0; cascadeIndex < ViewSrg::MaxCascadeCount; ++cascadeIndex)
|
|
|
+ for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
|
|
|
{
|
|
|
const float slopeBiasBase = ViewSrg::m_directionalLightShadows[m_lightIndex].m_slopeBiasBase[cascadeIndex];
|
|
|
m_slopeBias[cascadeIndex] = slopeBiasBase * tanTheta;
|
|
@@ -209,13 +217,20 @@ float DirectionalShadowCalculator::GetVisibility()
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- for (uint cascadeIndex = 0; cascadeIndex < ViewSrg::MaxCascadeCount; ++cascadeIndex)
|
|
|
+ for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
|
|
|
{
|
|
|
m_slopeBias[cascadeIndex] = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
float lit = 1;
|
|
|
+
|
|
|
+ [branch]
|
|
|
+ if (m_cascadeIndex < 0)
|
|
|
+ {
|
|
|
+ return lit;
|
|
|
+ }
|
|
|
+
|
|
|
switch(m_filterMode)
|
|
|
{
|
|
|
case ShadowFilterMethod::None:
|
|
@@ -231,25 +246,21 @@ float DirectionalShadowCalculator::GetVisibility()
|
|
|
lit = GetVisibilityFromLightEsmPcf();
|
|
|
break;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return lit;
|
|
|
}
|
|
|
|
|
|
float DirectionalShadowCalculator::GetVisibilityFromLightNoFilter()
|
|
|
-{
|
|
|
- if (m_cascadeIndex >= 0)
|
|
|
+{
|
|
|
+ const bool2 checkedShadowed = IsShadowed(m_shadowCoords[m_cascadeIndex], m_cascadeIndex);
|
|
|
+ if (checkedShadowed.x)
|
|
|
{
|
|
|
- const bool2 checkedShadowed = IsShadowed(m_shadowCoords[m_cascadeIndex], m_cascadeIndex);
|
|
|
-
|
|
|
- if (checkedShadowed.x)
|
|
|
- {
|
|
|
- return checkedShadowed.y ? 0. : 1.;
|
|
|
- }
|
|
|
+ return checkedShadowed.y ? 0. : 1.;
|
|
|
}
|
|
|
return 1.;
|
|
|
-}
|
|
|
+}
|
|
|
|
|
|
-float DirectionalShadowCalculator::SamplePcfBicubic(const float3 shadowCoord, const uint indexOfCascade)
|
|
|
+float DirectionalShadowCalculator::SamplePcfBicubic(const float3 shadowCoord, const int indexOfCascade)
|
|
|
{
|
|
|
const uint filteringSampleCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_filteringSampleCount;
|
|
|
const uint size = ViewSrg::m_directionalLightShadows[m_lightIndex].m_shadowmapSize;
|
|
@@ -261,10 +272,15 @@ float DirectionalShadowCalculator::SamplePcfBicubic(const float3 shadowCoord, co
|
|
|
param.invShadowMapSize = rcp(size);
|
|
|
param.comparisonValue = shadowCoord.z;
|
|
|
param.samplerState = SceneSrg::m_hwPcfSampler;
|
|
|
+
|
|
|
+#if USING_COMPUTE_SHADER_DIRECTIONAL_LIGHT
|
|
|
+ param.receiverPlaneDepthBias = 0;
|
|
|
+#else
|
|
|
param.receiverPlaneDepthBias = m_receiverShadowPlaneBiasEnable ? ComputeReceiverPlaneDepthBias(m_shadowPosDX[indexOfCascade], m_shadowPosDY[indexOfCascade]) : 0;
|
|
|
+#endif
|
|
|
|
|
|
- [branch]
|
|
|
- if (filteringSampleCount <= 4)
|
|
|
+ [branch]
|
|
|
+ if (filteringSampleCount <= 4)
|
|
|
{
|
|
|
return SampleShadowMapBicubic_4Tap(param);
|
|
|
}
|
|
@@ -279,51 +295,41 @@ float DirectionalShadowCalculator::SamplePcfBicubic(const float3 shadowCoord, co
|
|
|
}
|
|
|
|
|
|
float DirectionalShadowCalculator::GetVisibilityFromLightPcf()
|
|
|
-{
|
|
|
- const uint size = ViewSrg::m_directionalLightShadows[m_lightIndex].m_shadowmapSize;
|
|
|
- const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
|
|
|
+{
|
|
|
+ float lit = SamplePcfBicubic(m_shadowCoords[m_cascadeIndex], m_cascadeIndex);
|
|
|
|
|
|
- [branch]
|
|
|
- if (m_cascadeIndex >= 0)
|
|
|
+ if(m_blendBetweenCascadesEnable)
|
|
|
{
|
|
|
- float lit = SamplePcfBicubic(m_shadowCoords[m_cascadeIndex], m_cascadeIndex);
|
|
|
-
|
|
|
- if(m_blendBetweenCascadesEnable)
|
|
|
- {
|
|
|
- const float blendBetweenCascadesAmount = CalculateCascadeBlendAmount(m_shadowCoords[m_cascadeIndex].xyz);
|
|
|
-
|
|
|
- const int nextCascadeIndex = m_cascadeIndex + 1;
|
|
|
- [branch]
|
|
|
- if (blendBetweenCascadesAmount < 1.0f && nextCascadeIndex < cascadeCount)
|
|
|
- {
|
|
|
- const float nextLit = SamplePcfBicubic(m_shadowCoords[nextCascadeIndex], nextCascadeIndex);
|
|
|
- lit = lerp(nextLit, lit, blendBetweenCascadesAmount);
|
|
|
- }
|
|
|
- }
|
|
|
+ const float blendBetweenCascadesAmount = CalculateCascadeBlendAmount(m_shadowCoords[m_cascadeIndex].xyz);
|
|
|
|
|
|
- return lit;
|
|
|
- }
|
|
|
+ const int nextCascadeIndex = m_cascadeIndex + 1;
|
|
|
+ [branch]
|
|
|
+ if (blendBetweenCascadesAmount < 1.0f && nextCascadeIndex < m_cascadeCount)
|
|
|
+ {
|
|
|
+ const float nextLit = SamplePcfBicubic(m_shadowCoords[nextCascadeIndex], nextCascadeIndex);
|
|
|
+ lit = lerp(nextLit, lit, blendBetweenCascadesAmount);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- return 1.;
|
|
|
+ return lit;
|
|
|
}
|
|
|
|
|
|
float DirectionalShadowCalculator::GetVisibilityFromLightEsm()
|
|
|
{
|
|
|
- const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
|
|
|
-
|
|
|
- for (uint indexOfCascade = m_cascadeIndex; indexOfCascade < cascadeCount; ++indexOfCascade)
|
|
|
+ for (int indexOfCascade = m_cascadeIndex; indexOfCascade < m_cascadeCount; ++indexOfCascade)
|
|
|
{
|
|
|
const float3 shadowCoord = m_shadowCoords[indexOfCascade];
|
|
|
- const float distanceMin = ViewSrg::m_esmsDirectional[indexOfCascade].m_lightDistanceOfCameraViewFrustum;
|
|
|
bool2 checkedShadowed = IsShadowed(shadowCoord, indexOfCascade);
|
|
|
+
|
|
|
+ const float distanceMin = ViewSrg::m_esmsDirectional[indexOfCascade].m_lightDistanceOfCameraViewFrustum;
|
|
|
const float depthDiff = shadowCoord.z - distanceMin;
|
|
|
-
|
|
|
+
|
|
|
[branch]
|
|
|
if (checkedShadowed.x && depthDiff >= 0)
|
|
|
{
|
|
|
const float distanceWithinCameraView = depthDiff / (1. - distanceMin);
|
|
|
const float3 coord = float3(shadowCoord.xy, indexOfCascade);
|
|
|
- const float occluder = m_directionalLightExponentialShadowmap.Sample(PassSrg::LinearSampler, coord).r;
|
|
|
+ const float occluder = m_directionalLightExponentialShadowmap.SampleLevel(PassSrg::LinearSampler, coord, 0).r;
|
|
|
const float exponent = -EsmExponentialShift * (distanceWithinCameraView - occluder);
|
|
|
const float ratio = exp(exponent);
|
|
|
|
|
@@ -335,21 +341,20 @@ float DirectionalShadowCalculator::GetVisibilityFromLightEsm()
|
|
|
|
|
|
float DirectionalShadowCalculator::GetVisibilityFromLightEsmPcf()
|
|
|
{
|
|
|
- const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
|
|
|
-
|
|
|
- for (uint indexOfCascade = m_cascadeIndex; indexOfCascade < cascadeCount; ++indexOfCascade)
|
|
|
+ for (int indexOfCascade = m_cascadeIndex; indexOfCascade < m_cascadeCount; ++indexOfCascade)
|
|
|
{
|
|
|
const float3 shadowCoord = m_shadowCoords[indexOfCascade];
|
|
|
- const float distanceMin = ViewSrg::m_esmsDirectional[indexOfCascade].m_lightDistanceOfCameraViewFrustum;
|
|
|
bool2 checkedShadowed = IsShadowed(shadowCoord, indexOfCascade);
|
|
|
+
|
|
|
+ const float distanceMin = ViewSrg::m_esmsDirectional[indexOfCascade].m_lightDistanceOfCameraViewFrustum;
|
|
|
const float depthDiff = shadowCoord.z - distanceMin;
|
|
|
-
|
|
|
+
|
|
|
[branch]
|
|
|
if (checkedShadowed.x && depthDiff >= 0)
|
|
|
{
|
|
|
const float distanceWithinCameraView = depthDiff / (1. - distanceMin);
|
|
|
const float3 coord = float3(shadowCoord.xy, indexOfCascade);
|
|
|
- const float occluder = m_directionalLightExponentialShadowmap.Sample(PassSrg::LinearSampler, coord).r;
|
|
|
+ const float occluder = m_directionalLightExponentialShadowmap.SampleLevel(PassSrg::LinearSampler, coord, 0).r;
|
|
|
const float exponent = -EsmExponentialShift * (distanceWithinCameraView - occluder);
|
|
|
float ratio = exp(exponent);
|
|
|
|
|
@@ -364,7 +369,6 @@ float DirectionalShadowCalculator::GetVisibilityFromLightEsmPcf()
|
|
|
return 1.;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
float DirectionalShadowCalculator::CalculateCascadeBlendAmount(const float3 texCoord)
|
|
|
{
|
|
|
const float CascadeBlendArea = 0.015f; // might be worth exposing this as a slider.
|
|
@@ -372,4 +376,4 @@ float DirectionalShadowCalculator::CalculateCascadeBlendAmount(const float3 texC
|
|
|
const float distanceToOneMin = min3(1.0f - texCoord);
|
|
|
const float currentPixelsBlendBandLocation = min(min(texCoord.x, texCoord.y), distanceToOneMin);
|
|
|
return currentPixelsBlendBandLocation / CascadeBlendArea;
|
|
|
-}
|
|
|
+}
|