Refactor DDGI probe relocation algorithm to result in smoother and more precise placement

#754 #1614
This commit is contained in:
Wojtek Figat
2024-06-13 17:03:23 +02:00
parent 66f9374477
commit 5c5fad6bb4
3 changed files with 78 additions and 67 deletions

View File

@@ -208,7 +208,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
// Adjust weight curve to inject a small portion of light // Adjust weight curve to inject a small portion of light
const float minWeightThreshold = 0.2f; const float minWeightThreshold = 0.2f;
if (weight < minWeightThreshold) if (weight < minWeightThreshold)
weight *= Square(weight) * (1.0f / (minWeightThreshold * minWeightThreshold)); weight *= weight * weight * (1.0f / (minWeightThreshold * minWeightThreshold));
// Calculate trilinear weights based on the distance to each probe to smoothly transition between grid of 8 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); float3 trilinear = lerp(1.0f - biasAlpha, biasAlpha, (float3)probeCoordsOffset);
@@ -244,7 +244,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
if (irradiance.a > 0.0f) if (irradiance.a > 0.0f)
{ {
// Normalize irradiance // Normalize irradiance
irradiance.rgb *= 1.f / irradiance.a; irradiance.rgb *= 1.0f / irradiance.a;
#if DDGI_SRGB_BLENDING #if DDGI_SRGB_BLENDING
irradiance.rgb *= irradiance.rgb; irradiance.rgb *= irradiance.rgb;
#endif #endif

View File

@@ -21,7 +21,8 @@
#define DDGI_TRACE_RAYS_LIMIT 256 // Limit of rays per-probe (runtime value can be smaller) #define DDGI_TRACE_RAYS_LIMIT 256 // Limit of rays per-probe (runtime value can be smaller)
#define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 #define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8
#define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32
#define DDGI_PROBE_RELOCATE_ITERATIVE 0 // If true, probes relocation algorithm tries to move them in additive way, otherwise all nearby locations are checked to find the best position #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
#define DDGI_PROBE_RELOCATE_FIND_BEST 1 // If true, probes relocation algorithm tries to move to the best matching location within nearby area
META_CB_BEGIN(0, Data0) META_CB_BEGIN(0, Data0)
DDGIData DDGI; DDGIData DDGI;
@@ -113,12 +114,10 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
// Load probe state and position // Load probe state and position
float4 probeData = RWProbesData[probeDataCoords]; float4 probeData = RWProbesData[probeDataCoords];
uint probeState = DecodeDDGIProbeState(probeData); uint probeState = DecodeDDGIProbeState(probeData);
uint probeStateOld = probeState;
float3 probeOffset = probeData.xyz * probesSpacing; // Probe offset is [-1;1] within probes spacing float3 probeOffset = probeData.xyz * probesSpacing; // Probe offset is [-1;1] within probes spacing
float3 probeOffsetOld = probeOffset; float3 probeOffsetOld = probeOffset;
float3 probePosition = probeBasePosition; float3 probePosition = probeBasePosition + probeOffset;
#if DDGI_PROBE_RELOCATE_ITERATIVE
probePosition += probeOffset;
#endif
// Use Global SDF to quickly get distance and direction to the scene geometry // Use Global SDF to quickly get distance and direction to the scene geometry
#if DDGI_PROBE_RELOCATE_ITERATIVE #if DDGI_PROBE_RELOCATE_ITERATIVE
@@ -128,10 +127,12 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
float sdf = SampleGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, probePosition); float sdf = SampleGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, probePosition);
#endif #endif
float sdfDst = abs(sdf); float sdfDst = abs(sdf);
float threshold = GlobalSDF.CascadeVoxelSize[CascadeIndex]; const float ProbesDistanceLimits[4] = { 1.1f, 2.3f, 2.5f, 2.5f };
float distanceLimit = length(probesSpacing) * ProbesDistanceLimit; const float ProbesRelocateLimits[4] = { 0.4f, 0.5f, 0.6f, 0.7f };
float relocateLimit = length(probesSpacing) * 0.6f; float voxelLimit = GlobalSDF.CascadeVoxelSize[CascadeIndex];
if (sdfDst > distanceLimit) // Probe is too far from geometry float distanceLimit = length(probesSpacing) * ProbesDistanceLimits[CascadeIndex];
float relocateLimit = length(probesSpacing) * ProbesRelocateLimits[CascadeIndex];
if (sdfDst > distanceLimit + length(probeOffset)) // Probe is too far from geometry (or deep inside)
{ {
// Disable it // Disable it
probeOffset = float3(0, 0, 0); probeOffset = float3(0, 0, 0);
@@ -139,64 +140,72 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
} }
else else
{ {
#if DDGI_PROBE_RELOCATE_ITERATIVE // Relocate only if probe location is not good enough
if (sdf < threshold) // Probe is inside geometry probeState = DDGI_PROBE_STATE_ACTIVE;
if (sdf <= voxelLimit)
{ {
if (sdfDst < relocateLimit) #if DDGI_PROBE_RELOCATE_ITERATIVE
{ {
float3 offsetToAdd = sdfNormal * (sdf + threshold); // Use SDF gradient to relocate probe away the surface
if (distance(probeOffset, offsetToAdd) < relocateLimit) float iterativeRelocateSpeed = probeStateOld != DDGI_PROBE_STATE_ACTIVE ? 1.0f : 0.3f;
float3 offsetToSet = probeOffset + sdfNormal * ((sdf + voxelLimit) * iterativeRelocateSpeed);
if (length(offsetToSet) < relocateLimit)
{ {
// Relocate it // Relocate it
probeOffset += offsetToAdd; probeOffset = offsetToSet;
} }
else
{
// Reset offset
probeOffset = float3(0, 0, 0);
}
// Read SDF at the new position for additional check
probePosition = probeBasePosition + probeOffset;
sdf = SampleGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, probePosition);
sdfDst = abs(sdf);
} }
else if (sdf <= voxelLimit * 1.1f) // Add some safe-bias to reduce artifacts
{
// Reset relocation
probeOffset = float3(0, 0, 0);
}
}
else if (sdf > threshold * 4.0f) // Probe is far enough from any geometry
{
// Reset relocation
probeOffset = float3(0, 0, 0);
}
// Check if probe is relocated but the base location is fine
sdf = SampleGlobalSDF(GlobalSDF, GlobalSDFTex, probeBasePosition.xyz);
if (sdf > threshold)
{
// Reset relocation
probeOffset = float3(0, 0, 0);
}
#else
// Sample Global SDF around the probe location
uint sdfCascade = GetGlobalSDFCascade(GlobalSDF, probePosition);
float4 CachedProbeOffsets[64];
// TODO: test performance diff when using shared memory and larger thread group (is it worth it?)
for (uint x = 0; x < 4; x++)
for (uint y = 0; y < 4; y++)
for (uint z = 0; z < 4; z++)
{
float3 offset = Remap(float3(x, y, z), 0, 3, -0.5f, 0.5f) * relocateLimit;
float offsetSdf = SampleGlobalSDFCascade(GlobalSDF, GlobalSDFTex, probeBasePosition + offset, sdfCascade);
CachedProbeOffsets[x * 16 + y * 4 + z] = float4(offset, offsetSdf);
}
// Select the best probe location around the base position
float4 bestOffset = CachedProbeOffsets[0];
for (uint i = 1; i < 64; i++)
{
if (CachedProbeOffsets[i].w > bestOffset.w)
bestOffset = CachedProbeOffsets[i];
}
// Relocate the probe to the best found location (or zero if nothing good found)
if (bestOffset.w <= threshold)
bestOffset.xyz = float3(0, 0, 0);
probeOffset = bestOffset.xyz;
#endif #endif
{
#if DDGI_PROBE_RELOCATE_FIND_BEST
// Sample Global SDF around the probe base location
uint sdfCascade = GetGlobalSDFCascade(GlobalSDF, probeBasePosition);
float4 CachedProbeOffsets[64];
for (uint x = 0; x < 4; x++)
for (uint y = 0; y < 4; y++)
for (uint z = 0; z < 4; z++)
{
float3 offset = Remap(float3(x, y, z), 0, 3, -0.707f, 0.707f) * relocateLimit;
float offsetSdf = SampleGlobalSDFCascade(GlobalSDF, GlobalSDFTex, probeBasePosition + offset, sdfCascade);
CachedProbeOffsets[x * 16 + y * 4 + z] = float4(offset, offsetSdf);
}
// Select the best probe location around the base position
float4 bestOffset = CachedProbeOffsets[0];
for (uint i = 1; i < 64; i++)
{
if (CachedProbeOffsets[i].w > bestOffset.w)
bestOffset = CachedProbeOffsets[i];
}
if (bestOffset.w <= voxelLimit)
{
// Disable probe that is too close to the geometry
probeOffset = float3(0, 0, 0);
probeState = DDGI_PROBE_STATE_INACTIVE;
}
else
{
// Relocate the probe to the best found location
probeOffset = bestOffset.xyz;
}
#elif DDGI_PROBE_RELOCATE_ITERATIVE
// Disable probe
probeOffset = float3(0, 0, 0);
probeState = DDGI_PROBE_STATE_INACTIVE;
#endif
}
}
// Check if probe was scrolled // Check if probe was scrolled
int3 probeScrollClears = ProbeScrollClears[CascadeIndex].xyz; int3 probeScrollClears = ProbeScrollClears[CascadeIndex].xyz;
@@ -210,10 +219,11 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
wasScrolled = true; wasScrolled = true;
} }
// If probe was in different location or was inactive last frame then mark it as activated // If probe was in a different location or was activated now then mark it as activated
bool wasInactive = probeState == DDGI_PROBE_STATE_INACTIVE; bool wasActivated = probeStateOld == DDGI_PROBE_STATE_INACTIVE;
bool wasRelocated = distance(probeOffset, probeOffsetOld) > 2.0f; bool wasRelocated = distance(probeOffset, probeOffsetOld) > 2.0f;
probeState = wasInactive || wasScrolled || wasRelocated ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE; if ((wasActivated || wasScrolled || wasRelocated) && probeState == DDGI_PROBE_STATE_ACTIVE)
probeState = DDGI_PROBE_STATE_ACTIVATED;
} }
// Save probe state // Save probe state
@@ -302,12 +312,12 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID)
float4 radiance; float4 radiance;
if (hit.IsHit()) if (hit.IsHit())
{ {
if (hit.HitSDF <= 0.0f && hit.HitTime <= GlobalSDF.CascadeVoxelSize[0]) /*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) // 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); radiance = float4(0, 0, 0, hit.HitTime * -0.25f);
} }
else else*/
{ {
// Sample Global Surface Atlas to get the lighting at the hit location // Sample Global Surface Atlas to get the lighting at the hit location
float3 hitPosition = hit.GetHitPosition(trace); float3 hitPosition = hit.GetHitPosition(trace);

View File

@@ -200,6 +200,7 @@ void CS_RasterizeHeightfield(uint3 DispatchThreadId : SV_DispatchThreadID)
// Calculate distance from voxel center to the heightfield // Calculate distance from voxel center to the heightfield
float objectDistance = dot(heightfieldNormal, voxelWorldPos - heightfieldPosition); float objectDistance = dot(heightfieldNormal, voxelWorldPos - heightfieldPosition);
//objectDistance += (1.0f - saturate(dot(heightfieldNormal, float3(0, 1, 0)))) * -50.0f;
if (objectDistance < thickness * 0.5f) if (objectDistance < thickness * 0.5f)
objectDistance = thickness - objectDistance; objectDistance = thickness - objectDistance;
minDistance = CombineSDF(minDistance, objectDistance); minDistance = CombineSDF(minDistance, objectDistance);