Improve DDGI probes relocation to search 64 nearby locations around the probe

This commit is contained in:
Wojciech Figat
2022-07-01 12:41:33 +02:00
parent b40a890d31
commit e2319ceca3
4 changed files with 73 additions and 7 deletions

BIN
Content/Shaders/GI/DDGI.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -77,7 +77,7 @@ uint2 GetDDGIProbeTexelCoords(DDGIData data, uint cascadeIndex, uint probeIndex)
uint GetDDGIScrollingProbeIndex(DDGIData data, uint cascadeIndex, uint3 probeCoords)
{
// Probes are scrolled on edges to stabilize GI when camera moves
return GetDDGIProbeIndex(data, ((int3)probeCoords + data.ProbesScrollOffsets[cascadeIndex].xyz + (int3)data.ProbesCounts) % (int3)data.ProbesCounts);
return GetDDGIProbeIndex(data, (probeCoords + data.ProbesCounts + data.ProbesScrollOffsets[cascadeIndex].xyz) % data.ProbesCounts);
}
float3 GetDDGIProbeWorldPosition(DDGIData data, uint cascadeIndex, uint3 probeCoords)

View File

@@ -65,12 +65,19 @@ bool GetProbeState(float a, float b)
#ifdef _CS_Classify
#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
RWTexture2D<snorm float4> RWProbesState : register(u0);
RWByteAddressBuffer RWActiveProbes : register(u1);
Texture3D<float> GlobalSDFTex : register(t0);
Texture3D<float> GlobalSDFMip : register(t1);
float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3 toMax)
{
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
}
// Compute shader for updating probes state between active and inactive.
META_CS(true, FEATURE_LEVEL_SM5)
[numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)]
@@ -89,12 +96,15 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
float4 probeState = RWProbesState[probeDataCoords];
probeState.xyz *= probesSpacing; // Probe offset is [-1;1] within probes spacing
float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords);
float3 probePosition = probeBasePosition + probeState.xyz;
float3 probePosition = probeBasePosition;
#if DDGI_PROBE_RELOCATE_ITERATIVE
probePosition += probeState.xyz;
#endif
float4 probeStateOld = probeState;
// Use Global SDF to quickly get distance and direction to the scene geometry
float sdf;
float3 sdfNormal = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, GlobalSDFMip, probePosition.xyz, sdf));
float3 sdfNormal = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, GlobalSDFMip, probePosition, sdf));
float sdfDst = abs(sdf);
float threshold = GlobalSDF.CascadeVoxelSize[CascadeIndex];
float distanceLimit = length(probesSpacing) * 1.5f;
@@ -106,6 +116,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
}
else
{
#if DDGI_PROBE_RELOCATE_ITERATIVE
if (sdf < threshold) // Probe is inside geometry
{
if (sdfDst < relocateLimit)
@@ -123,7 +134,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
probeState.xyz = float3(0, 0, 0);
}
}
else if (sdf > threshold * 4.0f) // Probe is far enough any geometry
else if (sdf > threshold * 4.0f) // Probe is far enough from any geometry
{
// Reset relocation
probeState.xyz = float3(0, 0, 0);
@@ -136,6 +147,33 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
// Reset relocation
probeState.xyz = 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);
probeState.xyz = bestOffset.xyz;
#endif
// Check if probe was scrolled
int3 probeScrollClears = ProbeScrollClears[CascadeIndex].xyz;
@@ -153,10 +191,11 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
// If probe was in different location or was inactive last frame then mark it as activated
bool wasInactive = probeStateOld.w == DDGI_PROBE_STATE_INACTIVE;
bool wasRelocated = distance(probeState.xyz, probeStateOld.xyz) > 1.0f;
bool wasRelocated = distance(probeState.xyz, probeStateOld.xyz) > 2.0f;
probeState.w = wasInactive || wasScrolled || wasRelocated ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE;
}
// Save probe state
probeState.xyz /= probesSpacing;
RWProbesState[probeDataCoords] = probeState;

View File

@@ -68,6 +68,33 @@ void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldP
textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // cascades are placed next to each other on X axis
}
// Gets the Global SDF cascade index for the given world location.
uint GetGlobalSDFCascade(const GlobalSDFData data, float3 worldPosition)
{
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
if (all(cascadeUV > 0) && all(cascadeUV < 1))
return cascade;
}
return 0;
}
// Samples the Global SDF cascade and returns the distance to the closest surface (in world units) at the given world location.
float SampleGlobalSDFCascade(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition, uint cascade)
{
float distance = GLOBAL_SDF_WORLD_SIZE;
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < 1.0f && !any(cascadeUV < 0) && !any(cascadeUV > 1))
distance = cascadeDistance * cascadeMaxDistance;
return distance;
}
// Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location.
float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition)
{