From 37df16a3e4f94434d3228abd5af28d286e3d5562 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 31 Dec 2025 00:19:51 +0100 Subject: [PATCH] Refactor DDGI irradiance filtering for smoother and more accurate lighting --- Source/Shaders/GI/DDGI.hlsl | 21 +++++++++++++-------- Source/Shaders/GI/DDGI.shader | 7 ++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index 9d8f5509c..eb3995ada 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -25,6 +25,7 @@ #define DDGI_CASCADE_BLEND_SMOOTH 0 // Enables smooth cascade blending, otherwise dithering will be used #endif #define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space +#define DDGI_DEFAULT_BIAS 0.2f // Default value for DDGI sampling bias // DDGI data for a constant buffer struct DDGIData @@ -181,13 +182,14 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes if (probeState == DDGI_PROBE_STATE_INACTIVE) { // Search nearby probes to find any nearby GI sample + LOOP for (int searchDistance = 1; searchDistance < 3 && probeState == DDGI_PROBE_STATE_INACTIVE; searchDistance++) { for (uint searchAxis = 0; searchAxis < 3; searchAxis++) { int searchAxisSign = probeCoordsOffset[searchAxis] ? 1 : -1; int3 searchCoordsOffset = SearchAxes[searchAxis] * (searchAxisSign * searchDistance); - uint3 searchCoords = clamp((int3)probeCoords + searchCoordsOffset, int3(0, 0, 0), (int3)probeCoordsEnd); + uint3 searchCoords = clamp((uint3)((int3)probeCoords + searchCoordsOffset), uint3(0, 0, 0), probeCoordsEnd); uint searchIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, searchCoords); float4 searchData = LoadDDGIProbeData(data, probesData, cascadeIndex, searchIndex); uint searchState = DecodeDDGIProbeState(searchData); @@ -205,8 +207,9 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes if (probeState == DDGI_PROBE_STATE_INACTIVE) continue; } - float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing); - float3 probePosition = probeBasePosition + probeData.xyz * probesSpacing; // Probe offset is [-1;1] within probes spacing + + // Calculate probe position + float3 probePosition = baseProbeWorldPosition + (((float3)probeCoords - (float3)baseProbeCoords) * probesSpacing) + probeData.xyz * probesSpacing; // Calculate the distance and direction from the (biased and non-biased) shading point and the probe float3 worldPosToProbe = normalize(probePosition - worldPosition); @@ -215,6 +218,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Smooth backface test float weight = Square(dot(worldPosToProbe, worldNormal) * 0.5f + 0.5f); + weight = max(weight, 0.1f); // Sample distance texture float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe); @@ -226,7 +230,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes { float variance = abs(Square(probeDistance.x) - probeDistance.y); float visibilityWeight = variance / (variance + Square(biasedPosToProbeDist - probeDistance.x)); - weight *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.05f); + weight *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.0f); } // Avoid a weight of zero @@ -238,7 +242,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Calculate trilinear weights based on the distance to each probe to smoothly transition between grid of 8 probes float3 trilinear = lerp(1.0f - biasAlpha, biasAlpha, (float3)probeCoordsOffset); - weight *= max(trilinear.x * trilinear.y * trilinear.z, 0.001f); + weight *= saturate(trilinear.x * trilinear.y * trilinear.z * 2.0f); // Sample irradiance texture octahedralCoords = GetOctahedralCoords(worldNormal); @@ -270,7 +274,8 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes if (irradiance.a > 0.0f) { // Normalize irradiance - irradiance.rgb /= irradiance.a; + //irradiance.rgb /= irradiance.a; + irradiance.rgb /= lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f)); #if DDGI_SRGB_BLENDING irradiance.rgb *= irradiance.rgb; #endif @@ -282,13 +287,13 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes float3 GetDDGISurfaceBias(float3 viewDir, float probesSpacing, float3 worldNormal, float bias) { // Bias the world-space position to reduce artifacts - return (worldNormal * 0.2f + viewDir * 0.8f) * (0.75f * probesSpacing * bias); + return (worldNormal * 0.2f + viewDir * 0.8f) * (0.6f * probesSpacing * bias); } // Samples DDGI probes volume at the given world-space position and returns the irradiance. // bias - scales the bias vector to the initial sample point to reduce self-shading artifacts // dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending -float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = 0.2f, float dither = 0.0f) +float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = DDGI_DEFAULT_BIAS, float dither = 0.0f) { // Select the highest cascade that contains the sample location uint cascadeIndex = 0; diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index bcd1f59fb..fd82531ac 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -395,6 +395,8 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) // Add some bias to prevent self occlusion artifacts in Chebyshev due to Global SDF being very incorrect in small scale radiance.w = max(radiance.w + GlobalSDF.CascadeVoxelSize[hit.HitCascade] * 0.5f, 0); + float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; + radiance.w += probesSpacing * 0.05f; } } else @@ -793,10 +795,9 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) } // Sample irradiance - float bias = 0.2f; float dither = RandN2(input.TexCoord + TemporalTime).x; - float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias, dither); - + float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither); + // Calculate lighting float3 diffuseColor = GetDiffuseColor(gBuffer); float3 diffuse = Diffuse_Lambert(diffuseColor);