Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,9 @@ namespace VolumetricShadows
{
Texture2D<float2> SharedShadowMap : register(t18);

struct ShadowData
{
float4 VPOSOffset;
float4 ShadowSampleParam; // fPoissonRadiusScale / iShadowMapResolution in z and w
float4 EndSplitDistances; // cascade end distances int xyz, cascade count int z
float4 StartSplitDistances; // cascade start distances int xyz, 4 int z
float4 FocusShadowFadeParam;
float4 DebugColor;
float4 PropertyColor;
float4 AlphaTestRef;
float4 ShadowLightParam; // Falloff in x, ShadowDistance squared in z
float4x3 FocusShadowMapProj[4];
// Since ShadowData is passed between c++ and hlsl, can't have different defines due to strong typing
float4x3 ShadowMapProj[2][3];
float4x4 CameraViewProjInverse[2];
};

StructuredBuffer<ShadowData> SharedShadowData : register(t19);

static const float VSM_MIN_VARIANCE = 0.00001;
static const float VSM_BLEEDING_REDUCTION = 0.2;

float GetShadowDepth(float3 positionWS, uint eyeIndex)
{
float4 positionCS = mul(FrameBuffer::CameraViewProj[eyeIndex], float4(positionWS, 1));
return positionCS.z / positionCS.w;
}

// Chebyshev upper bound on P(X >= t)
// moments.x = mean(z), moments.y = mean(z^2)
float ComputeVSM(float2 moments, float depth)
Expand Down Expand Up @@ -84,35 +59,39 @@ namespace VolumetricShadows

float GetVSMShadow3D(float3 startPosition, float3 endPosition, float noise, uint baseSampleCount, uint eyeIndex, out float surfaceShadow)
{
ShadowData sD = SharedShadowData[0];
DirectionalShadowLightData directionalShadowLightData = DirectionalShadowLights[0];

// View-space z — matches the linear cascade split distances from BSShadowDirectionalLight.
float3 midPosition = (startPosition + endPosition) * 0.5;
float shadowMapDepth = GetShadowDepth(midPosition, eyeIndex);
float shadowMapDepth = SharedData::GetScreenDepth(FrameBuffer::GetShadowDepth(midPosition, eyeIndex));

// Cascade projections are world-space; positions come in camera-relative.
startPosition += FrameBuffer::CameraPosAdjust[eyeIndex].xyz;
endPosition += FrameBuffer::CameraPosAdjust[eyeIndex].xyz;

// Early out beyond cascade range
if (shadowMapDepth >= sD.EndSplitDistances.w) {
if (shadowMapDepth >= directionalShadowLightData.EndSplitDistances.y) {
surfaceShadow = 1.0;
return 1.0;
}

// Reduce over distance
float distSq = dot(midPosition, midPosition);
float fade = saturate(distSq / sD.ShadowLightParam.z);
float fade = saturate(shadowMapDepth / directionalShadowLightData.EndSplitDistances.y);

uint sampleCount = max(1, ceil(float(baseSampleCount) * (1.0 - fade)));
float rcpSampleCount = rcp(sampleCount);

// Compute cascade blend factor with smoothstep
float cascadeSelect = saturate((shadowMapDepth - sD.StartSplitDistances.y) / (sD.EndSplitDistances.x - sD.StartSplitDistances.y));
float cascadeSelect = saturate((shadowMapDepth - directionalShadowLightData.StartSplitDistances.y) / (directionalShadowLightData.EndSplitDistances.x - directionalShadowLightData.StartSplitDistances.y));

// Determine which cascade(s) to sample
uint primaryCascade = uint(cascadeSelect);
bool needsBlending = (cascadeSelect > 0.0) && (cascadeSelect < 1.0);

// Transform ray to light space for primary cascade
float4x3 shadowProj = sD.ShadowMapProj[eyeIndex][primaryCascade];
float3 startLS = mul(transpose(shadowProj), float4(startPosition, 1));
float3 endLS = mul(transpose(shadowProj), float4(endPosition, 1));
float4x4 shadowProj = directionalShadowLightData.ShadowProj[primaryCascade];
float3 startLS = mul(shadowProj, float4(startPosition, 1)).xyz;
float3 endLS = mul(shadowProj, float4(endPosition, 1)).xyz;
startLS.xy = saturate(startLS.xy);
endLS.xy = saturate(endLS.xy);

Expand All @@ -126,9 +105,9 @@ namespace VolumetricShadows
{
uint secondaryCascade = 1 - primaryCascade;

shadowProj = sD.ShadowMapProj[eyeIndex][secondaryCascade];
startLS = mul(transpose(shadowProj), float4(startPosition, 1));
endLS = mul(transpose(shadowProj), float4(endPosition, 1));
shadowProj = directionalShadowLightData.ShadowProj[secondaryCascade];
startLS = mul(shadowProj, float4(startPosition, 1)).xyz;
endLS = mul(shadowProj, float4(endPosition, 1)).xyz;
startLS.xy = saturate(startLS.xy);
endLS.xy = saturate(endLS.xy);

Expand All @@ -139,7 +118,7 @@ namespace VolumetricShadows
}

// Apply distance fade
float fadeFactor = 1.0 - pow(fade, 8);
float fadeFactor = 1.0 - pow(fade * fade, 8);
surfaceShadow = lerp(1.0, surfaceShadow, fadeFactor);
return lerp(1.0, shadow, fadeFactor);
}
Expand All @@ -153,30 +132,31 @@ namespace VolumetricShadows

float GetVSMShadow2D(float3 position, uint eyeIndex, out float detailedShadow)
{
ShadowData sD = SharedShadowData[0];
DirectionalShadowLightData directionalShadowLightData = DirectionalShadowLights[0];

float shadowMapDepth = GetShadowDepth(position, eyeIndex);
float shadowMapDepth = SharedData::GetScreenDepth(FrameBuffer::GetShadowDepth(position, eyeIndex));

// Early out beyond cascade range
if (shadowMapDepth >= sD.EndSplitDistances.w) {
if (shadowMapDepth >= directionalShadowLightData.EndSplitDistances.y) {
detailedShadow = 1.0;
return 1.0;
}

// Reduce over distance
float distSq = dot(position, position);
float fade = saturate(distSq / sD.ShadowLightParam.z);
float fade = saturate(shadowMapDepth / directionalShadowLightData.EndSplitDistances.y);

// Cascade projections are world-space; position comes in camera-relative.
float3 positionWS = position + FrameBuffer::CameraPosAdjust[eyeIndex].xyz;

// Compute cascade blend factor with smoothstep
float cascadeSelect = saturate((shadowMapDepth - sD.StartSplitDistances.y) / (sD.EndSplitDistances.x - sD.StartSplitDistances.y));
float cascadeSelect = saturate((shadowMapDepth - directionalShadowLightData.StartSplitDistances.y) / (directionalShadowLightData.EndSplitDistances.x - directionalShadowLightData.StartSplitDistances.y));

// Determine which cascade(s) to sample
uint primaryCascade = uint(cascadeSelect);
bool needsBlending = (cascadeSelect > 0.0) && (cascadeSelect < 1.0);

// Transform ray to light space for primary cascade
float4x3 shadowProj = sD.ShadowMapProj[eyeIndex][primaryCascade];
float3 positionLS = mul(transpose(shadowProj), float4(position, 1));
// Transform position to light space for primary cascade
float3 positionLS = mul(directionalShadowLightData.ShadowProj[primaryCascade], float4(positionWS, 1)).xyz;
positionLS.xy = saturate(positionLS.xy);

// Sample primary cascade
Expand All @@ -187,16 +167,15 @@ namespace VolumetricShadows
{
uint secondaryCascade = 1 - primaryCascade;

shadowProj = sD.ShadowMapProj[eyeIndex][secondaryCascade];
positionLS = mul(transpose(shadowProj), float4(position, 1));
positionLS = mul(directionalShadowLightData.ShadowProj[secondaryCascade], float4(positionWS, 1)).xyz;
positionLS.xy = saturate(positionLS.xy);

float shadowBlend = SampleVSMCascade2D(secondaryCascade, positionLS);
shadow = lerp(shadow, shadowBlend, cascadeSelect);
}

// Apply distance fade
float fadeFactor = 1.0 - pow(fade, 8);
float fadeFactor = 1.0 - pow(fade * fade, 8);
detailedShadow = lerp(1.0, ReduceBleeding(shadow, VSM_BLEEDING_REDUCTION), fadeFactor);
return lerp(1.0, shadow, fadeFactor);
}
Expand Down
9 changes: 9 additions & 0 deletions package/Shaders/Common/FrameBuffer.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ namespace FrameBuffer
return clamp(screenPositionDR, minValue, maxValue);
}

// Projects a world-space (camera-relative) point into NDC using the eye's CameraViewProj
// and returns the post-perspective z (NDC depth). Combine with SharedData::GetScreenDepth
// to get a linear view-space distance suitable for cascade-split comparisons.
float GetShadowDepth(float3 positionWS, uint eyeIndex)
{
float4 positionCS = mul(FrameBuffer::CameraViewProj[eyeIndex], float4(positionWS, 1));
return positionCS.z / positionCS.w;
}

/**
* @brief Converts normalized screen UVs to dynamic-resolution UVs and clamps them.
*
Expand Down
13 changes: 13 additions & 0 deletions package/Shaders/Common/ShadowSampling.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@
# include "Common/Spherical Harmonics/SphericalHarmonics.hlsli"
#endif

// Populated once per frame by Deferred::CopyShadowLightData from BSShadowDirectionalLight.
// Column-major float4x4 projections so HLSL `mul(proj, float4(pos, 1))` matches the
// XMMATRIX layout written by XMStoreFloat4x4 on the C++ side.
struct DirectionalShadowLightData
{
column_major float4x4 ShadowProj[2];
column_major float4x4 InvShadowProj[2];
float2 EndSplitDistances;
float2 StartSplitDistances;
};

StructuredBuffer<DirectionalShadowLightData> DirectionalShadowLights : register(t98);

#if defined(VOLUMETRIC_SHADOWS)
# include "VolumetricShadows/VolumetricShadows.hlsli"
#endif
Expand Down
Loading
Loading