Improve DDGI probes relocation to search 64 nearby locations around the probe
This commit is contained in:
BIN
Content/Shaders/GI/DDGI.flax
(Stored with Git LFS)
BIN
Content/Shaders/GI/DDGI.flax
(Stored with Git LFS)
Binary file not shown.
@@ -77,7 +77,7 @@ uint2 GetDDGIProbeTexelCoords(DDGIData data, uint cascadeIndex, uint probeIndex)
|
|||||||
uint GetDDGIScrollingProbeIndex(DDGIData data, uint cascadeIndex, uint3 probeCoords)
|
uint GetDDGIScrollingProbeIndex(DDGIData data, uint cascadeIndex, uint3 probeCoords)
|
||||||
{
|
{
|
||||||
// Probes are scrolled on edges to stabilize GI when camera moves
|
// 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)
|
float3 GetDDGIProbeWorldPosition(DDGIData data, uint cascadeIndex, uint3 probeCoords)
|
||||||
|
|||||||
@@ -65,12 +65,19 @@ bool GetProbeState(float a, float b)
|
|||||||
|
|
||||||
#ifdef _CS_Classify
|
#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);
|
RWTexture2D<snorm float4> RWProbesState : register(u0);
|
||||||
RWByteAddressBuffer RWActiveProbes : register(u1);
|
RWByteAddressBuffer RWActiveProbes : register(u1);
|
||||||
|
|
||||||
Texture3D<float> GlobalSDFTex : register(t0);
|
Texture3D<float> GlobalSDFTex : register(t0);
|
||||||
Texture3D<float> GlobalSDFMip : register(t1);
|
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.
|
// Compute shader for updating probes state between active and inactive.
|
||||||
META_CS(true, FEATURE_LEVEL_SM5)
|
META_CS(true, FEATURE_LEVEL_SM5)
|
||||||
[numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)]
|
[numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)]
|
||||||
@@ -89,12 +96,15 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|||||||
float4 probeState = RWProbesState[probeDataCoords];
|
float4 probeState = RWProbesState[probeDataCoords];
|
||||||
probeState.xyz *= probesSpacing; // Probe offset is [-1;1] within probes spacing
|
probeState.xyz *= probesSpacing; // Probe offset is [-1;1] within probes spacing
|
||||||
float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords);
|
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;
|
float4 probeStateOld = probeState;
|
||||||
|
|
||||||
// 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
|
||||||
float sdf;
|
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 sdfDst = abs(sdf);
|
||||||
float threshold = GlobalSDF.CascadeVoxelSize[CascadeIndex];
|
float threshold = GlobalSDF.CascadeVoxelSize[CascadeIndex];
|
||||||
float distanceLimit = length(probesSpacing) * 1.5f;
|
float distanceLimit = length(probesSpacing) * 1.5f;
|
||||||
@@ -106,6 +116,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if DDGI_PROBE_RELOCATE_ITERATIVE
|
||||||
if (sdf < threshold) // Probe is inside geometry
|
if (sdf < threshold) // Probe is inside geometry
|
||||||
{
|
{
|
||||||
if (sdfDst < relocateLimit)
|
if (sdfDst < relocateLimit)
|
||||||
@@ -123,7 +134,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|||||||
probeState.xyz = float3(0, 0, 0);
|
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
|
// Reset relocation
|
||||||
probeState.xyz = float3(0, 0, 0);
|
probeState.xyz = float3(0, 0, 0);
|
||||||
@@ -136,6 +147,33 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|||||||
// Reset relocation
|
// Reset relocation
|
||||||
probeState.xyz = float3(0, 0, 0);
|
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
|
// Check if probe was scrolled
|
||||||
int3 probeScrollClears = ProbeScrollClears[CascadeIndex].xyz;
|
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
|
// 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 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;
|
probeState.w = wasInactive || wasScrolled || wasRelocated ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save probe state
|
||||||
probeState.xyz /= probesSpacing;
|
probeState.xyz /= probesSpacing;
|
||||||
RWProbesState[probeDataCoords] = probeState;
|
RWProbesState[probeDataCoords] = probeState;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
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.
|
// 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)
|
float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user