Refactor DDGI probe relocation algorithm to result in smoother and more precise placement
#754 #1614
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user