Adding cascade blending for pcf (#6181)
* Adding cascade blending for pcf Signed-off-by: mrieggeramzn <mriegger@amazon.com> * tabs to spaces Signed-off-by: mrieggeramzn <mriegger@amazon.com> * tabs to spaces Signed-off-by: mrieggeramzn <mriegger@amazon.com> * tabs 2 spaces Signed-off-by: mrieggeramzn <mriegger@amazon.com> * Feedback using min3 Signed-off-by: mrieggeramzn <mriegger@amazon.com> * Only enable flag if > 1 cascade Signed-off-by: mrieggeramzn <mriegger@amazon.com> * no blending if last cascade Signed-off-by: mrieggeramzn <mriegger@amazon.com> * Remove unused Signed-off-by: mrieggeramzn <mriegger@amazon.com>
This commit is contained in:
+49
-3
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
#include <Atom/RPI/Math.azsli>
|
||||
#include "Shadow.azsli"
|
||||
#include "ShadowmapAtlasLib.azsli"
|
||||
#include "BicubicPcfFilters.azsli"
|
||||
@@ -25,6 +26,10 @@
|
||||
enum class ShadowFilterMethod {None, Pcf, Esm, EsmPcf};
|
||||
option ShadowFilterMethod o_directional_shadow_filtering_method = ShadowFilterMethod::None;
|
||||
option bool o_directional_shadow_receiver_plane_bias_enable = true;
|
||||
option bool o_blend_between_cascades_enable = false;
|
||||
|
||||
static const float CascadeBlendArea = 0.015f; // might be worth exposing this as a slider.
|
||||
|
||||
|
||||
// DirectionalLightShadow calculates lit ratio for a directional light.
|
||||
class DirectionalLightShadow
|
||||
@@ -98,6 +103,8 @@ class DirectionalLightShadow
|
||||
|
||||
float SamplePcfBicubic(float3 shadowCoord, uint indexOfCascade);
|
||||
|
||||
float CalculateCascadeBlendAmount(const float3 texCoord);
|
||||
|
||||
uint m_lightIndex;
|
||||
float3 m_shadowCoords[ViewSrg::MaxCascadeCount];
|
||||
float m_slopeBias[ViewSrg::MaxCascadeCount];
|
||||
@@ -174,6 +181,7 @@ bool2 DirectionalLightShadow::IsShadowed(float3 shadowCoord, uint indexOfCascade
|
||||
const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
|
||||
Texture2DArray<float> shadowmap = PassSrg::m_directionalLightShadowmap;
|
||||
|
||||
[branch]
|
||||
if (shadowCoord.x >= 0. && shadowCoord.x * size < size - PixelMargin &&
|
||||
shadowCoord.y >= 0. && shadowCoord.y * size < size - PixelMargin)
|
||||
{
|
||||
@@ -213,20 +221,46 @@ float DirectionalLightShadow::GetVisibilityFromLightPcf()
|
||||
static const float PixelMargin = 1.5; // avoiding artifact between cascade levels.
|
||||
static const float DepthMargin = 1e-8; // avoiding artifact when near depth bounds.
|
||||
|
||||
bool cascadeFound = false;
|
||||
int currentCascadeIndex = 0;
|
||||
|
||||
const uint size = ViewSrg::m_directionalLightShadows[m_lightIndex].m_shadowmapSize;
|
||||
const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
|
||||
for (uint indexOfCascade = 0; indexOfCascade < cascadeCount; ++indexOfCascade)
|
||||
{
|
||||
const float3 shadowCoord = m_shadowCoords[indexOfCascade];
|
||||
|
||||
|
||||
if (shadowCoord.x >= 0. && shadowCoord.x * size < size - PixelMargin &&
|
||||
shadowCoord.y >= 0. && shadowCoord.y * size < size - PixelMargin &&
|
||||
shadowCoord.z < 1. - DepthMargin)
|
||||
{
|
||||
m_debugInfo.m_cascadeIndex = indexOfCascade;
|
||||
return SamplePcfBicubic(shadowCoord, indexOfCascade);
|
||||
currentCascadeIndex = m_debugInfo.m_cascadeIndex = indexOfCascade;
|
||||
cascadeFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[branch]
|
||||
if (cascadeFound)
|
||||
{
|
||||
float lit = SamplePcfBicubic(m_shadowCoords[currentCascadeIndex], currentCascadeIndex);
|
||||
|
||||
if(o_blend_between_cascades_enable)
|
||||
{
|
||||
const float blendBetweenCascadesAmount = CalculateCascadeBlendAmount(m_shadowCoords[currentCascadeIndex].xyz);
|
||||
|
||||
const int nextCascadeIndex = currentCascadeIndex + 1;
|
||||
[branch]
|
||||
if (blendBetweenCascadesAmount < 1.0f && nextCascadeIndex < cascadeCount)
|
||||
{
|
||||
const float nextLit = SamplePcfBicubic(m_shadowCoords[nextCascadeIndex], nextCascadeIndex);
|
||||
lit = lerp(nextLit, lit, blendBetweenCascadesAmount);
|
||||
}
|
||||
}
|
||||
|
||||
return lit;
|
||||
}
|
||||
|
||||
m_debugInfo.m_cascadeIndex = cascadeCount;
|
||||
return 1.;
|
||||
}
|
||||
@@ -244,6 +278,8 @@ float DirectionalLightShadow::GetVisibilityFromLightEsm()
|
||||
const float distanceMin = ViewSrg::m_esmsDirectional[indexOfCascade].m_lightDistanceOfCameraViewFrustum;
|
||||
bool2 checkedShadowed = IsShadowed(shadowCoord, indexOfCascade);
|
||||
const float depthDiff = shadowCoord.z - distanceMin;
|
||||
|
||||
[branch]
|
||||
if (checkedShadowed.x && depthDiff >= 0)
|
||||
{
|
||||
const float distanceWithinCameraView = depthDiff / (1. - distanceMin);
|
||||
@@ -274,6 +310,8 @@ float DirectionalLightShadow::GetVisibilityFromLightEsmPcf()
|
||||
const float distanceMin = ViewSrg::m_esmsDirectional[indexOfCascade].m_lightDistanceOfCameraViewFrustum;
|
||||
bool2 checkedShadowed = IsShadowed(shadowCoord, indexOfCascade);
|
||||
const float depthDiff = shadowCoord.z - distanceMin;
|
||||
|
||||
[branch]
|
||||
if (checkedShadowed.x && depthDiff >= 0)
|
||||
{
|
||||
const float distanceWithinCameraView = depthDiff / (1. - distanceMin);
|
||||
@@ -319,6 +357,7 @@ float DirectionalLightShadow::SamplePcfBicubic(float3 shadowCoord, uint indexOfC
|
||||
param.samplerState = SceneSrg::m_hwPcfSampler;
|
||||
param.receiverPlaneDepthBias = o_directional_shadow_receiver_plane_bias_enable ? ComputeReceiverPlaneDepthBias(m_shadowPosDX[indexOfCascade], m_shadowPosDY[indexOfCascade]) : 0;
|
||||
|
||||
[branch]
|
||||
if (filteringSampleCount <= 4)
|
||||
{
|
||||
return SampleShadowMapBicubic_4Tap(param);
|
||||
@@ -420,3 +459,10 @@ float3 DirectionalLightShadow::AddDebugColoring(
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
float DirectionalLightShadow::CalculateCascadeBlendAmount(const float3 texCoord)
|
||||
{
|
||||
const float distanceToOneMin = min3(1.0f - texCoord);
|
||||
const float currentPixelsBlendBandLocation = min(min(texCoord.x, texCoord.y), distanceToOneMin);
|
||||
return currentPixelsBlendBandLocation / CascadeBlendArea;
|
||||
}
|
||||
|
||||
+3
@@ -163,6 +163,9 @@ namespace AZ
|
||||
|
||||
//! Reduces acne by biasing the shadowmap lookup along the geometric normal.
|
||||
virtual void SetNormalShadowBias(LightHandle handle, float normalShadowBias) = 0;
|
||||
|
||||
//! Sets whether or not blending between shadow map cascades is enabled.
|
||||
virtual void SetCascadeBlendingEnabled(LightHandle handle, bool enable) = 0;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
+10
-3
@@ -198,13 +198,15 @@ namespace AZ
|
||||
|
||||
if (m_shadowingLightHandle.IsValid())
|
||||
{
|
||||
uint32_t shadowFilterMethod = m_shadowData.at(nullptr).GetData(m_shadowingLightHandle.GetIndex()).m_shadowFilterMethod;
|
||||
const uint32_t shadowFilterMethod = m_shadowData.at(nullptr).GetData(m_shadowingLightHandle.GetIndex()).m_shadowFilterMethod;
|
||||
RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(m_directionalShadowFilteringMethodName, AZ::RPI::ShaderOptionValue{shadowFilterMethod});
|
||||
RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(m_directionalShadowReceiverPlaneBiasEnableName, AZ::RPI::ShaderOptionValue{ m_shadowProperties.GetData(m_shadowingLightHandle.GetIndex()).m_isReceiverPlaneBiasEnabled });
|
||||
RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(m_directionalShadowReceiverPlaneBiasEnableName, AZ::RPI::ShaderOptionValue{ m_shadowProperties.GetData(m_shadowingLightHandle.GetIndex()).m_isReceiverPlaneBiasEnabled });
|
||||
|
||||
const uint32_t cascadeCount = m_shadowData.at(nullptr).GetData(m_shadowingLightHandle.GetIndex()).m_cascadeCount;
|
||||
ShadowProperty& property = m_shadowProperties.GetData(m_shadowingLightHandle.GetIndex());
|
||||
|
||||
RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(m_BlendBetweenCascadesEnableName, AZ::RPI::ShaderOptionValue{cascadeCount > 1 && m_shadowProperties.GetData(m_shadowingLightHandle.GetIndex()).m_blendBetwenCascades });
|
||||
|
||||
ShadowProperty& property = m_shadowProperties.GetData(m_shadowingLightHandle.GetIndex());
|
||||
bool segmentsNeedUpdate = property.m_segments.empty();
|
||||
for (const auto& passIt : m_cascadedShadowmapsPasses)
|
||||
{
|
||||
@@ -577,6 +579,11 @@ namespace AZ
|
||||
m_shadowProperties.GetData(handle.GetIndex()).m_isReceiverPlaneBiasEnabled = enable;
|
||||
}
|
||||
|
||||
void DirectionalLightFeatureProcessor::SetCascadeBlendingEnabled(LightHandle handle, bool enable)
|
||||
{
|
||||
m_shadowProperties.GetData(handle.GetIndex()).m_blendBetwenCascades = enable;
|
||||
}
|
||||
|
||||
void DirectionalLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias)
|
||||
{
|
||||
for (auto& it : m_shadowData)
|
||||
|
||||
@@ -176,6 +176,8 @@ namespace AZ
|
||||
// If true, this will reduce the shadow acne introduced by large pcf kernels by estimating the angle of the triangle being shaded
|
||||
// with the ddx/ddy functions.
|
||||
bool m_isReceiverPlaneBiasEnabled = true;
|
||||
|
||||
bool m_blendBetwenCascades = false;
|
||||
};
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
@@ -215,6 +217,7 @@ namespace AZ
|
||||
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
|
||||
void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;
|
||||
void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) override;
|
||||
void SetCascadeBlendingEnabled(LightHandle handle, bool enable) override;
|
||||
void SetShadowBias(LightHandle handle, float bias) override;
|
||||
void SetNormalShadowBias(LightHandle handle, float normalShadowBias) override;
|
||||
|
||||
@@ -367,6 +370,7 @@ namespace AZ
|
||||
Name m_lightTypeName = Name("directional");
|
||||
Name m_directionalShadowFilteringMethodName = Name("o_directional_shadow_filtering_method");
|
||||
Name m_directionalShadowReceiverPlaneBiasEnableName = Name("o_directional_shadow_receiver_plane_bias_enable");
|
||||
Name m_BlendBetweenCascadesEnableName = Name("o_blend_between_cascades_enable");
|
||||
static constexpr const char* FeatureProcessorName = "DirectionalLightFeatureProcessor";
|
||||
};
|
||||
} // namespace Render
|
||||
|
||||
+8
@@ -184,6 +184,14 @@ namespace AZ
|
||||
//! Reduces acne by biasing the shadowmap lookup along the geometric normal.
|
||||
//! @param normalShadowBias Sets the amount of normal shadow bias to apply.
|
||||
virtual void SetNormalShadowBias(float normalShadowBias) = 0;
|
||||
|
||||
//! Gets whether the directional shadow map has cascade blending enabled.
|
||||
//! This smooths out the border between cascades at the cost of some performance in the blend area.
|
||||
virtual bool GetCascadeBlendingEnabled() const = 0;
|
||||
|
||||
//! Sets whether the directional shadow map has cascade blending enabled.
|
||||
//! @param enable flag specifying whether to enable cascade blending.
|
||||
virtual void SetCascadeBlendingEnabled(bool enable) = 0;
|
||||
};
|
||||
using DirectionalLightRequestBus = EBus<DirectionalLightRequests>;
|
||||
|
||||
|
||||
+3
@@ -115,6 +115,9 @@ namespace AZ
|
||||
//! Reduces shadow acne by applying a small amount of offset along shadow-space z.
|
||||
float m_shadowBias = 0.0f;
|
||||
|
||||
// If true, sample between two adjacent shadow map cascades in a small boundary area to smooth out the transition.
|
||||
bool m_cascadeBlendingEnabled = false;
|
||||
|
||||
bool IsSplitManual() const;
|
||||
bool IsSplitAutomatic() const;
|
||||
bool IsCascadeCorrectionDisabled() const;
|
||||
|
||||
+3
-3
@@ -40,7 +40,8 @@ namespace AZ
|
||||
->Field("PcfFilteringSampleCount", &DirectionalLightComponentConfig::m_filteringSampleCount)
|
||||
->Field("ShadowReceiverPlaneBiasEnabled", &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled)
|
||||
->Field("Shadow Bias", &DirectionalLightComponentConfig::m_shadowBias)
|
||||
->Field("Normal Shadow Bias", &DirectionalLightComponentConfig::m_normalShadowBias);
|
||||
->Field("Normal Shadow Bias", &DirectionalLightComponentConfig::m_normalShadowBias)
|
||||
->Field("CascadeBlendingEnabled", &DirectionalLightComponentConfig::m_cascadeBlendingEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,8 +114,7 @@ namespace AZ
|
||||
|
||||
bool DirectionalLightComponentConfig::IsShadowPcfDisabled() const
|
||||
{
|
||||
return !(m_shadowFilterMethod == ShadowFilterMethod::Pcf ||
|
||||
m_shadowFilterMethod == ShadowFilterMethod::EsmPcf);
|
||||
return !(m_shadowFilterMethod == ShadowFilterMethod::Pcf);
|
||||
}
|
||||
|
||||
bool DirectionalLightComponentConfig::IsEsmDisabled() const
|
||||
|
||||
+16
-1
@@ -88,6 +88,8 @@ namespace AZ
|
||||
->Event("SetShadowBias", &DirectionalLightRequestBus::Events::SetShadowBias)
|
||||
->Event("GetNormalShadowBias", &DirectionalLightRequestBus::Events::GetNormalShadowBias)
|
||||
->Event("SetNormalShadowBias", &DirectionalLightRequestBus::Events::SetNormalShadowBias)
|
||||
->Event("GetCascadeBlendingEnabled", &DirectionalLightRequestBus::Events::GetCascadeBlendingEnabled)
|
||||
->Event("SetCascadeBlendingEnabled", &DirectionalLightRequestBus::Events::SetCascadeBlendingEnabled)
|
||||
->VirtualProperty("Color", "GetColor", "SetColor")
|
||||
->VirtualProperty("Intensity", "GetIntensity", "SetIntensity")
|
||||
->VirtualProperty("AngularDiameter", "GetAngularDiameter", "SetAngularDiameter")
|
||||
@@ -104,7 +106,8 @@ namespace AZ
|
||||
->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount")
|
||||
->VirtualProperty("ShadowReceiverPlaneBiasEnabled", "GetShadowReceiverPlaneBiasEnabled", "SetShadowReceiverPlaneBiasEnabled")
|
||||
->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias")
|
||||
->VirtualProperty("NormalShadowBias", "GetNormalShadowBias", "SetNormalShadowBias");
|
||||
->VirtualProperty("NormalShadowBias", "GetNormalShadowBias", "SetNormalShadowBias")
|
||||
->VirtualProperty("BlendBetweenCascadesEnabled", "GetCascadeBlendingEnabled", "SetCascadeBlendingEnabled");
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -537,6 +540,7 @@ namespace AZ
|
||||
SetNormalShadowBias(m_configuration.m_normalShadowBias);
|
||||
SetFilteringSampleCount(m_configuration.m_filteringSampleCount);
|
||||
SetShadowReceiverPlaneBiasEnabled(m_configuration.m_receiverPlaneBiasEnabled);
|
||||
SetCascadeBlendingEnabled(m_configuration.m_cascadeBlendingEnabled);
|
||||
|
||||
// [GFX TODO][ATOM-1726] share config for multiple light (e.g., light ID).
|
||||
// [GFX TODO][ATOM-2416] adapt to multiple viewports.
|
||||
@@ -636,5 +640,16 @@ namespace AZ
|
||||
m_featureProcessor->SetShadowReceiverPlaneBiasEnabled(m_lightHandle, enable);
|
||||
}
|
||||
|
||||
bool DirectionalLightComponentController::GetCascadeBlendingEnabled() const
|
||||
{
|
||||
return m_configuration.m_cascadeBlendingEnabled;
|
||||
}
|
||||
|
||||
void DirectionalLightComponentController::SetCascadeBlendingEnabled(bool enable)
|
||||
{
|
||||
m_configuration.m_cascadeBlendingEnabled = enable;
|
||||
m_featureProcessor->SetCascadeBlendingEnabled(m_lightHandle, enable);
|
||||
}
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
+3
-1
@@ -83,7 +83,9 @@ namespace AZ
|
||||
float GetShadowBias() const override;
|
||||
void SetShadowBias(float bias) override;
|
||||
float GetNormalShadowBias() const override;
|
||||
void SetNormalShadowBias(float bias) override;
|
||||
void SetNormalShadowBias(float bias) override;
|
||||
bool GetCascadeBlendingEnabled() const override;
|
||||
void SetCascadeBlendingEnabled(bool enable) override;
|
||||
|
||||
private:
|
||||
friend class EditorDirectionalLightComponent;
|
||||
|
||||
+5
-1
@@ -161,7 +161,11 @@ namespace AZ
|
||||
->Attribute(Edit::Attributes::Min, 0.f)
|
||||
->Attribute(Edit::Attributes::Max, 10.0f)
|
||||
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
|
||||
;
|
||||
->DataElement(
|
||||
Edit::UIHandlers::CheckBox, &DirectionalLightComponentConfig::m_cascadeBlendingEnabled,
|
||||
"Blend between cascades\n", "Enables smooth blending between shadow map cascades.")
|
||||
->Attribute(Edit::Attributes::ReadOnly, &DirectionalLightComponentConfig::IsShadowPcfDisabled)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user