Add CascadeBlendSize to Directional Light and fix sun shadow fade at distance

This commit is contained in:
2026-05-04 23:41:00 +02:00
parent 4c5035e433
commit aac399c6a1
6 changed files with 24 additions and 10 deletions
@@ -48,6 +48,7 @@ void DirectionalLight::Draw(RenderContext& renderContext)
data.Cascade2Spacing = Cascade2Spacing;
data.Cascade3Spacing = Cascade3Spacing;
data.Cascade4Spacing = Cascade4Spacing;
data.CascadeBlendSize = CascadeBlendSize;
data.PartitionMode = PartitionMode;
data.ContactShadowsLength = ContactShadowsLength;
data.StaticFlags = GetStaticFlags();
@@ -69,6 +70,7 @@ void DirectionalLight::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE(Cascade2Spacing);
SERIALIZE(Cascade3Spacing);
SERIALIZE(Cascade4Spacing);
SERIALIZE(CascadeBlendSize);
SERIALIZE(PartitionMode);
}
@@ -82,6 +84,7 @@ void DirectionalLight::Deserialize(DeserializeStream& stream, ISerializeModifier
DESERIALIZE(Cascade2Spacing);
DESERIALIZE(Cascade3Spacing);
DESERIALIZE(Cascade4Spacing);
DESERIALIZE(CascadeBlendSize);
DESERIALIZE(PartitionMode);
}
@@ -48,6 +48,12 @@ public:
API_FIELD(Attributes="EditorOrder(69), DefaultValue(1.0f), VisibleIf(nameof(ShowCascade4)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")")
float Cascade4Spacing = 1.0f;
/// <summary>
/// Percentage of the cascade distance over which cascades will blend together. This helps to hide the transition between cascades.
/// </summary>
API_FIELD(Attributes = "EditorOrder(70), EditorDisplay(\"Shadow\", \"Cascade Blend Distance\"), Limit(0.0f, 1.0f)")
float CascadeBlendSize = 0.1f;
public:
// [LightWithShadow]
void Draw(RenderContext& renderContext) override;
+1
View File
@@ -79,6 +79,7 @@ struct RenderDirectionalLightData : RenderLightData
PartitionMode PartitionMode;
int32 CascadeCount;
float CascadeBlendSize;
RenderDirectionalLightData()
{
+5 -4
View File
@@ -195,7 +195,7 @@ struct ShadowAtlasLight
bool BlendCSM;
mutable StaticStates StaticState;
BoundingSphere Bounds;
float Sharpness, Fade, NormalOffsetScale, Bias, FadeDistance, Distance, TileBorder;
float Sharpness, Fade, NormalOffsetScale, Bias, FadeDistance, Distance, TileBorder, CascadeBlendSize;
Float4 CascadeSplits;
ShadowAtlasLightTile Tiles[SHADOWS_MAX_TILES];
ShadowAtlasLightCache Cache;
@@ -846,6 +846,7 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
cascadeSplits[i] = (cascadeSplits[i] - renderContext.View.Near) / viewRange;
}
atlasLight.CascadeSplits = renderContext.View.Near + Float4(cascadeSplits) * viewRange;
atlasLight.CascadeBlendSize = Math::Saturate(light.CascadeBlendSize);
// Update cached state (invalidate it if the light changed)
atlasLight.ValidateCache(renderContext.View, light);
@@ -917,10 +918,10 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
// Calculate cascade split frustum corners in view space
Float3 cascadeCornersVs[8];
float csmOverlap = atlasLight.BlendCSM ? 0.2f : 0.1f;
float blendDistance = atlasLight.CascadeBlendSize;
for (int32 j = 0; j < 4; j++)
{
float overlapWithPrevSplit = csmOverlap * (splitMinRatio - oldSplitMinRatio);
float overlapWithPrevSplit = blendDistance * (splitMinRatio - oldSplitMinRatio);
const Float3 frustumRangeVS = frustumCornersVs[j + 4] - frustumCornersVs[j];
cascadeCornersVs[j] = frustumCornersVs[j] + frustumRangeVS * (splitMinRatio - overlapWithPrevSplit);
cascadeCornersVs[j + 4] = frustumCornersVs[j] + frustumRangeVS * splitMaxRatio;
@@ -1430,7 +1431,7 @@ RETRY_ATLAS_SETUP:
{
// Shadow info
auto* packed = shadows.ShadowsBuffer.WriteReserve<Float4>(2);
Color32 packed0x((byte)(atlasLight.Sharpness * (255.0f / 10.0f)), (byte)(atlasLight.Fade * 255.0f), (byte)atlasLight.TilesCount, 0);
Color32 packed0x((byte)(atlasLight.Sharpness * (255.0f / 10.0f)), (byte)(atlasLight.Fade * 255.0f), (byte)atlasLight.TilesCount, (byte)(atlasLight.CascadeBlendSize * 255.0f));
packed[0] = Float4(*(const float*)&packed0x, atlasLight.FadeDistance, atlasLight.NormalOffsetScale, atlasLight.Bias);
packed[1] = atlasLight.CascadeSplits;
}
+2
View File
@@ -21,6 +21,7 @@ struct ShadowData
float FadeDistance;
float NormalOffsetScale;
float Bias;
float CascadeBlendSize;
uint TilesCount;
float4 CascadeSplits;
};
@@ -43,6 +44,7 @@ ShadowData LoadShadowsBuffer(Buffer<float4> shadowsBuffer, uint shadowsBufferAdd
shadow.Sharpness = (packed0x & 0x000000ff) * (10.0f / 255.0f);
shadow.Fade = ((packed0x & 0x0000ff00) >> 8) * (1.0f / 255.0f);
shadow.TilesCount = ((packed0x & 0x00ff0000) >> 16);
shadow.CascadeBlendSize = ((packed0x & 0xff000000) >> 24) * (1.0f / 255.0f);
shadow.FadeDistance = vector0.y;
shadow.NormalOffsetScale = vector0.z;
shadow.Bias = vector0.w;
+7 -6
View File
@@ -293,11 +293,10 @@ ShadowSample SampleDirectionalLightShadow(LightData light, Buffer<float4> shadow
float splitDist = (nextSplit - viewDepth) / splitSize;
#endif
#if SHADOWS_CSM_DITHERING && !SHADOWS_CSM_BLENDING
const float BlendThreshold = 0.05f;
if (splitDist <= BlendThreshold && cascadeIndex != shadow.TilesCount - 1)
if (splitDist <= shadow.CascadeBlendSize && cascadeIndex != shadow.TilesCount - 1)
{
// Dither with the next cascade but with screen-space dithering (gets cleaned out by TAA)
float lerpAmount = 1 - splitDist / BlendThreshold;
float lerpAmount = 1 - splitDist / shadow.CascadeBlendSize;
if (step(RandN2(gBuffer.ViewPos.xy + dither).x, lerpAmount))
cascadeIndex++;
}
@@ -312,17 +311,19 @@ ShadowSample SampleDirectionalLightShadow(LightData light, Buffer<float4> shadow
result = SampleDirectionalLightShadowCascade(light, shadowsBuffer, shadowMap, gBuffer, shadow, samplePosition, cascadeIndex);
#if SHADOWS_CSM_BLENDING
const float BlendThreshold = 0.1f;
if (splitDist <= BlendThreshold && cascadeIndex != shadow.TilesCount - 1)
if (splitDist <= shadow.CascadeBlendSize && cascadeIndex != shadow.TilesCount - 1)
{
// Sample the next cascade, and blend between the two results to smooth the transition
ShadowSample nextResult = SampleDirectionalLightShadowCascade(light, shadowsBuffer, shadowMap, gBuffer, shadow, samplePosition, cascadeIndex + 1);
float blendAmount = splitDist / BlendThreshold;
float blendAmount = splitDist / shadow.CascadeBlendSize;
result.SurfaceShadow = lerp(nextResult.SurfaceShadow, result.SurfaceShadow, blendAmount);
result.TransmissionShadow = lerp(nextResult.TransmissionShadow, result.TransmissionShadow, blendAmount);
}
#endif
result.SurfaceShadow = lerp(result.SurfaceShadow, 1, fade);
result.TransmissionShadow = lerp(result.TransmissionShadow, 1, fade);
return result;
}