From 1a2d86826d517c995e936eb0aa418afe929bd37f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 26 Jun 2026 14:51:59 +0200 Subject: [PATCH] Minor DDGI changes --- Source/Engine/Foliage/Foliage.cpp | 2 +- .../GI/DynamicDiffuseGlobalIllumination.cpp | 1 - Source/Shaders/GI/DDGI.shader | 75 ++++++++++++------- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index b862651e6..468b05538 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -1638,7 +1638,7 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie { const DeserializeStream& items = foliageInstancesMember->value; int32 chunksCount = (int32)items.Size() - 1; - if (chunksCount <= 0) + if (chunksCount <= 0 || !items[0].IsInt()) return; int32 foliageInstancesCount = items[0].GetInt(); Instances.Resize(foliageInstancesCount); diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index c03e78759..ca0558f88 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -41,7 +41,6 @@ #define DDGI_TRACE_RAYS_LIMIT 256 // Limit of rays per-probe (runtime value can be smaller) #define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) -#define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 #define DDGI_PROBE_EMPTY_AREA_DENSITY 8 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency #define DDGI_DEBUG_STATS 0 // Enables additional GPU-driven stats for probe/rays count diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 91c7de00b..9f80ee4aa 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -577,12 +577,12 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) #if defined(_CS_UpdateProbes) -#if DDGI_PROBE_UPDATE_MODE == 0 +#if DDGI_PROBE_UPDATE_IRRADIANCE // Update irradiance #define DDGI_PROBE_RESOLUTION DDGI_PROBE_RESOLUTION_IRRADIANCE groupshared float4 CachedProbesTraceRadiance[DDGI_TRACE_RAYS_LIMIT]; groupshared float OutputInstability[DDGI_PROBE_RESOLUTION * DDGI_PROBE_RESOLUTION]; -#else +#elif DDGI_PROBE_UPDATE_DEPTH // Update distance #define DDGI_PROBE_RESOLUTION DDGI_PROBE_RESOLUTION_DISTANCE groupshared float CachedProbesTraceDistance[DDGI_TRACE_RAYS_LIMIT]; @@ -693,29 +693,27 @@ static const uint4 BorderOffsets[BorderOffsetsSize] = { uint4(1, 1, 15, 15) }; #else -#error "Unsupported probe size for border values copy." +#undef BorderOffsetsSize #endif groupshared float3 CachedProbesTraceDirection[DDGI_TRACE_RAYS_LIMIT]; RWTexture2D RWOutput : register(u0); -#if DDGI_PROBE_UPDATE_MODE == 0 +#if DDGI_PROBE_UPDATE_IRRADIANCE RWTexture2D RWProbesData : register(u1); #if DDGI_DEBUG_INSTABILITY RWTexture2D RWOutputInstability : register(u2); #endif -#else +#elif DDGI_PROBE_UPDATE_DEPTH Texture2D ProbesData : register(t0); #endif Texture2D ProbesTrace : register(t1); ByteAddressBuffer ActiveProbes : register(t2); // Compute shader for updating probes irradiance or distance texture. -#define DDGI_PROBE_UPDATE_IRRADIANCE 0 -#define DDGI_PROBE_UPDATE_DISTANCE 1 META_CS(true, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(DDGI_PROBE_UPDATE_MODE=0) // Irradiance -META_PERMUTATION_1(DDGI_PROBE_UPDATE_MODE=1) // Distance +META_PERMUTATION_1(DDGI_PROBE_UPDATE_IRRADIANCE=1) +META_PERMUTATION_1(DDGI_PROBE_UPDATE_DEPTH=1) [numthreads(DDGI_PROBE_RESOLUTION, DDGI_PROBE_RESOLUTION, 1)] void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) { @@ -727,20 +725,20 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords); // Load probe data -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE int2 probeDataCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex); float4 probeData = RWProbesData[probeDataCoords]; -#else +#elif DDGI_PROBE_UPDATE_DEPTH float4 probeData = LoadDDGIProbeData(DDGI, ProbesData, CascadeIndex, probeIndex); #endif float probeAttention = DecodeDDGIProbeAttention(probeData); uint probeState = DecodeDDGIProbeState(probeData); uint probeRaysCount = GetProbeRaysCount(DDGI, probeAttention); -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE uint backfacesCount = 0; uint backfacesLimit = uint(probeRaysCount * 0.1f); -#else +#elif DDGI_PROBE_UPDATE_DEPTH float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; float distanceLimit = probesSpacing * 1.5f; #endif @@ -752,9 +750,9 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ for (uint i = 0; i < raysCount; i++) { uint rayIndex = raysStart + i; -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE CachedProbesTraceRadiance[rayIndex] = ProbesTrace[uint2(rayIndex, GroupId.x)]; -#else +#elif DDGI_PROBE_UPDATE_DEPTH float rayDistance = ProbesTrace[uint2(rayIndex, GroupId.x)].w; CachedProbesTraceDistance[rayIndex] = min(abs(rayDistance), distanceLimit); #endif @@ -775,7 +773,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ float3 rayDirection = CachedProbesTraceDirection[rayIndex]; float rayWeight = max(dot(octahedralDirection, rayDirection), 0.0f); -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE float4 rayRadiance = CachedProbesTraceRadiance[rayIndex]; #if DDGI_TRACE_NEGATIVE if (rayRadiance.w < 0.0f) @@ -795,7 +793,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // Add radiance (RGB) and weight (A) result += float4(rayRadiance.rgb * rayWeight, rayWeight); -#else +#elif DDGI_PROBE_UPDATE_DEPTH // Increase reaction speed for depth discontinuities rayWeight = pow(rayWeight, 10.0f); @@ -807,9 +805,9 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // Normalize results float epsilon = (float)probeRaysCount * 1e-9f; -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE result.rgb *= 1.0f / (2.0f * max(result.a, epsilon)); -#else +#elif DDGI_PROBE_UPDATE_DEPTH result.rgb *= 1.0f / max(result.a, epsilon); #endif @@ -820,7 +818,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ if (wasActivated) previous = result.rgb; -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE // Calculate instability of the irradiance float previousLuma = Luminance(previous.rgb); float resultLuma = Luminance(result.rgb); @@ -840,7 +838,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // Blend current value with the previous probe data float historyWeightFast = DDGI.ProbeHistoryWeight; float historyWeightSlow = 0.97f; -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE float3 irradianceDelta = result.rgb - previous; float irradianceDeltaMax = Max3(abs(irradianceDelta)); float irradianceDeltaLen = length(irradianceDelta); @@ -853,7 +851,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ float historyWeight = lerp(historyWeightSlow, historyWeightFast, probeAttention * probeAttention * probeAttention); //historyWeight = 1.0f; // Debug full-blend //historyWeight = 0.0f; // Debug no-blend -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_DISTANCE +#if DDGI_PROBE_UPDATE_DEPTH // Reduce blending when the distance changes too much to avoid ghosting artifacts float instability = abs(previous.x - result.x) / previous.x; if (instability > 0.5f) historyWeight *= 0.5f; @@ -861,7 +859,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ if (wasActivated) historyWeight = 0.0f; result.rgb = max(result.rgb, 0); -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE result *= DDGI.IndirectLightingIntensity; #if DDGI_SRGB_BLENDING == 1 result.rgb = pow(result.rgb, 0.5f / DDGI_SRGB_BLENDING_GAMMA); @@ -876,16 +874,18 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // Apply quantization error to reduce yellowish artifacts due to R11G11B10 format float noise = InterleavedGradientNoise(octahedralCoords * 10, FrameIndexMod8); result.rgb = QuantizeColor(result.rgb, noise, QuantizationError); -#else +#elif DDGI_PROBE_UPDATE_DEPTH result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f); #endif RWOutput[outputCoords] = result; +#ifdef BorderOffsetsSize GroupMemoryBarrierWithGroupSync(); +#endif uint2 baseCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex) * (DDGI_PROBE_RESOLUTION + 2); -#if DDGI_PROBE_UPDATE_MODE == DDGI_PROBE_UPDATE_IRRADIANCE +#if DDGI_PROBE_UPDATE_IRRADIANCE // The first thread updates the probe attention based on the instability of all texels BRANCH if (GroupIndex == 0 && probeState != DDGI_PROBE_STATE_INACTIVE) @@ -912,7 +912,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ RWProbesData[probeDataCoords] = EncodeDDGIProbeData(probeData.xyz, probeState, probeAttention); } -#if DDGI_DEBUG_INSTABILITY +#if DDGI_DEBUG_INSTABILITY && defined(BorderOffsetsSize) // Copy border pixels for (uint borderIndex = GroupIndex; borderIndex < BorderOffsetsSize; borderIndex += DDGI_PROBE_RESOLUTION * DDGI_PROBE_RESOLUTION) { @@ -923,11 +923,34 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ #endif // Copy border pixels +#ifdef BorderOffsetsSize for (uint borderIndex = GroupIndex; borderIndex < BorderOffsetsSize; borderIndex += DDGI_PROBE_RESOLUTION * DDGI_PROBE_RESOLUTION) { uint4 borderOffsets = BorderOffsets[borderIndex]; RWOutput[baseCoords + borderOffsets.zw] = RWOutput[baseCoords + borderOffsets.xy]; } +#else + bool isEdgeX = GroupThreadId.x == 0 || GroupThreadId.x == DDGI_PROBE_RESOLUTION - 1; + bool isEdgeY = GroupThreadId.y == 0 || GroupThreadId.y == DDGI_PROBE_RESOLUTION - 1; + if (isEdgeX) + { + outputCoords.x = baseCoords.x + (GroupThreadId.x == 0 ? 0 : (DDGI_PROBE_RESOLUTION + 1)); + outputCoords.y = baseCoords.y + DDGI_PROBE_RESOLUTION - GroupThreadId.y; + RWOutput[outputCoords] = result; + } + if (isEdgeY) + { + outputCoords.x = baseCoords.x + DDGI_PROBE_RESOLUTION - GroupThreadId.x; + outputCoords.y = baseCoords.y + (GroupThreadId.y == 0 ? 0 : (DDGI_PROBE_RESOLUTION + 1)); + RWOutput[outputCoords] = result; + } + if (isEdgeX && isEdgeY) + { + outputCoords.x = baseCoords.x + (GroupThreadId.x == 0 ? DDGI_PROBE_RESOLUTION + 1 : 0); + outputCoords.y = baseCoords.y + (GroupThreadId.y == 0 ? DDGI_PROBE_RESOLUTION + 1 : 0); + RWOutput[outputCoords] = result; + } +#endif } #endif