Add nearby probes search to fill cells with missing GI data

#1614
This commit is contained in:
Wojtek Figat
2024-06-17 18:25:11 +02:00
parent e3f0991805
commit 6e0dd2064a
3 changed files with 44 additions and 21 deletions

View File

@@ -426,7 +426,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
// Shift the volume origin based on scroll offsets for each axis once it overflows
for (int32 axis = 0; axis < 3; axis++)
{
// different volume scroll that preserves the scroll offset delta relative to the probe count
const int32 probeCount = ddgiData.ProbeCounts.Raw[axis];
int32& scrollOffset = cascade.ProbeScrollOffsets.Raw[axis];
while (scrollOffset >= probeCount)

View File

@@ -148,13 +148,12 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
float fadeDistance = probesSpacing * 0.5f;
float cascadeWeight = saturate(Min3(probesExtent - abs(worldPosition - probesOrigin)) / fadeDistance);
if (cascadeWeight > dither) // Use dither to make transition smoother
{
break;
}
}
if (cascadeIndex == data.CascadesCount)
return data.FallbackIrradiance;
uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), uint3(0, 0, 0), data.ProbesCounts - uint3(1, 1, 1));
uint3 probeCoordsEnd = data.ProbesCounts - uint3(1, 1, 1);
uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), uint3(0, 0, 0), probeCoordsEnd);
// Bias the world-space position to reduce artifacts
float3 viewDir = normalize(data.ViewPos - worldPosition);
@@ -167,17 +166,40 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
// Loop over the closest probes to accumulate their contributions
float4 irradiance = float4(0, 0, 0, 0);
const int3 SearchAxisMasks[3] = { int3(1, 0, 0), int3(0, 1, 0), int3(0, 0, 1) };
for (uint i = 0; i < 8; i++)
{
uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1;
uint3 probeCoords = clamp(baseProbeCoords + probeCoordsOffset, uint3(0, 0, 0), data.ProbesCounts - uint3(1, 1, 1));
uint3 probeCoords = clamp(baseProbeCoords + probeCoordsOffset, uint3(0, 0, 0), probeCoordsEnd);
uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords);
// Load probe position and state
float4 probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex);
uint probeState = DecodeDDGIProbeState(probeData);
if (probeState == DDGI_PROBE_STATE_INACTIVE)
continue;
{
// Search nearby probes to find any nearby GI sample
for (int searchDistance = 1; searchDistance < 3 && probeState == DDGI_PROBE_STATE_INACTIVE; searchDistance++)
for (uint searchAxis = 0; searchAxis < 3; searchAxis++)
{
int searchAxisDir = probeCoordsOffset[searchAxis] ? 1 : -1;
int3 searchCoordsOffset = SearchAxisMasks[searchAxis] * searchAxisDir * searchDistance;
uint3 searchCoords = clamp((int3)probeCoords + searchCoordsOffset, int3(0, 0, 0), (int3)probeCoordsEnd);
uint searchIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, searchCoords);
float4 searchData = LoadDDGIProbeData(data, probesData, cascadeIndex, searchIndex);
uint searchState = DecodeDDGIProbeState(searchData);
if (searchState != DDGI_PROBE_STATE_INACTIVE)
{
// Use nearby probe as a fallback (visibility test might ignore it but with smooth gradient)
probeCoords = searchCoords;
probeIndex = searchIndex;
probeData = searchData;
probeState = searchState;
break;
}
}
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
@@ -193,15 +215,13 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe);
float2 uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE);
float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg * 2.0f;
float probeDistanceMean = probeDistance.x;
// Visibility weight (Chebyshev)
if (biasedPosToProbeDist > probeDistanceMean)
if (biasedPosToProbeDist > probeDistance.x)
{
float probeDistanceMean2 = probeDistance.y;
float probeDistanceVariance = abs(Square(probeDistanceMean) - probeDistanceMean2);
float chebyshevWeight = probeDistanceVariance / (probeDistanceVariance + Square(biasedPosToProbeDist - probeDistanceMean));
weight *= max(chebyshevWeight * chebyshevWeight * chebyshevWeight, 0.05f);
float variance = abs(Square(probeDistance.x) - probeDistance.y);
float visibilityWeight = variance / (variance + Square(biasedPosToProbeDist - probeDistance.x));
weight *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.05f);
}
// Avoid a weight of zero
@@ -209,8 +229,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
// Adjust weight curve to inject a small portion of light
const float minWeightThreshold = 0.2f;
if (weight < minWeightThreshold)
weight *= weight * weight * (1.0f / (minWeightThreshold * minWeightThreshold));
if (weight < minWeightThreshold) weight *= Square(weight) / Square(minWeightThreshold);
// 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);
@@ -246,7 +265,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
if (irradiance.a > 0.0f)
{
// Normalize irradiance
irradiance.rgb *= 1.0f / irradiance.a;
irradiance.rgb /= irradiance.a;
#if DDGI_SRGB_BLENDING
irradiance.rgb *= irradiance.rgb;
#endif

View File

@@ -19,6 +19,7 @@
// This must match C++
#define DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT 4096 // Maximum amount of probes to update at once during rays tracing and blending
#define DDGI_TRACE_RAYS_LIMIT 256 // Limit of rays per-probe (runtime value can be smaller)
#define DDGI_TRACE_NEGATIVE 0 // If true, rays that start inside geometry will use negative distance to indicate backface hit
#define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8
#define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32
#define DDGI_PROBE_RELOCATE_ITERATIVE 1 // If true, probes relocation algorithm tries to move them in additive way, otherwise all nearby locations are checked to find the best position
@@ -146,9 +147,9 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
float sdfDst = abs(sdf);
const float ProbesDistanceLimits[4] = { 1.1f, 2.3f, 2.5f, 2.5f };
const float ProbesRelocateLimits[4] = { 0.4f, 0.5f, 0.6f, 0.7f };
float voxelLimit = GlobalSDF.CascadeVoxelSize[CascadeIndex];
float distanceLimit = length(probesSpacing) * ProbesDistanceLimits[CascadeIndex];
float relocateLimit = length(probesSpacing) * ProbesRelocateLimits[CascadeIndex];
float voxelLimit = GlobalSDF.CascadeVoxelSize[CascadeIndex] * 0.8f;
float distanceLimit = probesSpacing * ProbesDistanceLimits[CascadeIndex];
float relocateLimit = probesSpacing * ProbesRelocateLimits[CascadeIndex];
if (sdfDst > distanceLimit + length(probeOffset)) // Probe is too far from geometry (or deep inside)
{
// Disable it
@@ -317,12 +318,14 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID)
float4 radiance;
if (hit.IsHit())
{
/*if (hit.HitSDF <= 0.0f && hit.HitTime <= GlobalSDF.CascadeVoxelSize[0])
#if DDGI_TRACE_NEGATIVE
if (hit.HitSDF <= 0.0f && hit.HitTime <= GlobalSDF.CascadeVoxelSize[0])
{
// Ray starts inside geometry (mark as negative distance and reduce it's influence during irradiance blending)
radiance = float4(0, 0, 0, hit.HitTime * -0.25f);
}
else*/
else
#endif
{
// Sample Global Surface Atlas to get the lighting at the hit location
float3 hitPosition = hit.GetHitPosition(trace);
@@ -393,7 +396,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
uint backfacesLimit = uint(probeRaysCount * 0.1f);
#else
float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w;
float distanceLimit = length(probesSpacing) * 1.5f;
float distanceLimit = probesSpacing * 1.5f;
#endif
BRANCH
@@ -435,6 +438,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
#if DDGI_PROBE_UPDATE_MODE == 0
float4 rayRadiance = CachedProbesTraceRadiance[rayIndex];
#if DDGI_TRACE_NEGATIVE
if (rayRadiance.w < 0.0f)
{
// Count backface hits
@@ -448,6 +452,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
}
continue;
}
#endif
// Add radiance (RGB) and weight (A)
result += float4(rayRadiance.rgb * rayWeight, rayWeight);