Refactor DDGI probes to use variable ray count depending on the probe location relative to the view frustum

This commit is contained in:
Wojciech Figat
2022-12-15 17:33:44 +01:00
parent 3fcbcacd43
commit 1cf6c5233e
13 changed files with 106 additions and 89 deletions

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

View File

@@ -170,7 +170,7 @@ bool GlobalIlluminationFeature::Bind(MaterialShader::BindParameters& params, Spa
// Bind DDGI data // Bind DDGI data
data.DDGI = bindingDataDDGI.Constants; data.DDGI = bindingDataDDGI.Constants;
params.GPUContext->BindSR(srv + 0, bindingDataDDGI.ProbesState); params.GPUContext->BindSR(srv + 0, bindingDataDDGI.ProbesData);
params.GPUContext->BindSR(srv + 1, bindingDataDDGI.ProbesDistance); params.GPUContext->BindSR(srv + 1, bindingDataDDGI.ProbesDistance);
params.GPUContext->BindSR(srv + 2, bindingDataDDGI.ProbesIrradiance); params.GPUContext->BindSR(srv + 2, bindingDataDDGI.ProbesIrradiance);
} }

View File

@@ -87,7 +87,7 @@ public:
int32 ProbesCountTotal = 0; int32 ProbesCountTotal = 0;
Int3 ProbeCounts = Int3::Zero; Int3 ProbeCounts = Int3::Zero;
GPUTexture* ProbesTrace = nullptr; // Probes ray tracing: (RGB: hit radiance, A: hit distance) GPUTexture* ProbesTrace = nullptr; // Probes ray tracing: (RGB: hit radiance, A: hit distance)
GPUTexture* ProbesState = nullptr; // Probes state: (RGB: world-space offset, A: state) GPUTexture* ProbesData = nullptr; // Probes data: (RGB: world-space offset, A: state/data)
GPUTexture* ProbesIrradiance = nullptr; // Probes irradiance (RGB: sRGB color) GPUTexture* ProbesIrradiance = nullptr; // Probes irradiance (RGB: sRGB color)
GPUTexture* ProbesDistance = nullptr; // Probes distance (R: mean distance, G: mean distance^2) GPUTexture* ProbesDistance = nullptr; // Probes distance (R: mean distance, G: mean distance^2)
GPUBuffer* ActiveProbes = nullptr; // List with indices of the active probes (built during probes classification to use indirect dispatches for probes updating), counter at 0 GPUBuffer* ActiveProbes = nullptr; // List with indices of the active probes (built during probes classification to use indirect dispatches for probes updating), counter at 0
@@ -97,7 +97,7 @@ public:
FORCE_INLINE void Release() FORCE_INLINE void Release()
{ {
RenderTargetPool::Release(ProbesTrace); RenderTargetPool::Release(ProbesTrace);
RenderTargetPool::Release(ProbesState); RenderTargetPool::Release(ProbesData);
RenderTargetPool::Release(ProbesIrradiance); RenderTargetPool::Release(ProbesIrradiance);
RenderTargetPool::Release(ProbesDistance); RenderTargetPool::Release(ProbesDistance);
SAFE_DELETE_GPU_RESOURCE(ActiveProbes); SAFE_DELETE_GPU_RESOURCE(ActiveProbes);
@@ -373,7 +373,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
#define INIT_TEXTURE(texture, format, width, height) desc.Format = format; desc.Width = width; desc.Height = height; ddgiData.texture = RenderTargetPool::Get(desc); if (!ddgiData.texture) return true; memUsage += ddgiData.texture->GetMemoryUsage(); RENDER_TARGET_POOL_SET_NAME(ddgiData.texture, "DDGI." #texture) #define INIT_TEXTURE(texture, format, width, height) desc.Format = format; desc.Width = width; desc.Height = height; ddgiData.texture = RenderTargetPool::Get(desc); if (!ddgiData.texture) return true; memUsage += ddgiData.texture->GetMemoryUsage(); RENDER_TARGET_POOL_SET_NAME(ddgiData.texture, "DDGI." #texture)
desc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess; desc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess;
INIT_TEXTURE(ProbesTrace, PixelFormat::R16G16B16A16_Float, probeRaysCount, Math::Min(probesCountCascade, DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT)); INIT_TEXTURE(ProbesTrace, PixelFormat::R16G16B16A16_Float, probeRaysCount, Math::Min(probesCountCascade, DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT));
INIT_TEXTURE(ProbesState, PixelFormat::R8G8B8A8_SNorm, probesCountTotalX, probesCountTotalY); INIT_TEXTURE(ProbesData, PixelFormat::R8G8B8A8_SNorm, probesCountTotalX, probesCountTotalY);
INIT_TEXTURE(ProbesIrradiance, PixelFormat::R11G11B10_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2)); INIT_TEXTURE(ProbesIrradiance, PixelFormat::R11G11B10_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2));
INIT_TEXTURE(ProbesDistance, PixelFormat::R16G16_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2)); INIT_TEXTURE(ProbesDistance, PixelFormat::R16G16_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2));
#undef INIT_TEXTURE #undef INIT_TEXTURE
@@ -393,7 +393,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
{ {
// Clear probes // Clear probes
PROFILE_GPU("Clear"); PROFILE_GPU("Clear");
context->ClearUA(ddgiData.ProbesState, Float4::Zero); context->ClearUA(ddgiData.ProbesData, Float4::Zero);
context->ClearUA(ddgiData.ProbesIrradiance, Float4::Zero); context->ClearUA(ddgiData.ProbesIrradiance, Float4::Zero);
context->ClearUA(ddgiData.ProbesDistance, Float4::Zero); context->ClearUA(ddgiData.ProbesDistance, Float4::Zero);
} }
@@ -458,7 +458,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
ddgiData.Result.Constants.IrradianceGamma = 1.5f; ddgiData.Result.Constants.IrradianceGamma = 1.5f;
ddgiData.Result.Constants.IndirectLightingIntensity = indirectLightingIntensity; ddgiData.Result.Constants.IndirectLightingIntensity = indirectLightingIntensity;
ddgiData.Result.Constants.FallbackIrradiance = fallbackIrradiance.ToFloat3() * fallbackIrradiance.A; ddgiData.Result.Constants.FallbackIrradiance = fallbackIrradiance.ToFloat3() * fallbackIrradiance.A;
ddgiData.Result.ProbesState = ddgiData.ProbesState->View(); ddgiData.Result.ProbesData = ddgiData.ProbesData->View();
ddgiData.Result.ProbesDistance = ddgiData.ProbesDistance->View(); ddgiData.Result.ProbesDistance = ddgiData.ProbesDistance->View();
ddgiData.Result.ProbesIrradiance = ddgiData.ProbesIrradiance->View(); ddgiData.Result.ProbesIrradiance = ddgiData.ProbesIrradiance->View();
@@ -516,7 +516,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
threadGroupsX = Math::DivideAndRoundUp(probesCountCascade, DDGI_PROBE_CLASSIFY_GROUP_SIZE); threadGroupsX = Math::DivideAndRoundUp(probesCountCascade, DDGI_PROBE_CLASSIFY_GROUP_SIZE);
context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr);
context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr);
context->BindUA(0, ddgiData.Result.ProbesState); context->BindUA(0, ddgiData.Result.ProbesData);
context->BindUA(1, ddgiData.ActiveProbes->View()); context->BindUA(1, ddgiData.ActiveProbes->View());
Data1 data; Data1 data;
data.CascadeIndex = cascadeIndex; data.CascadeIndex = cascadeIndex;
@@ -558,7 +558,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
context->BindSR(4, bindingDataSurfaceAtlas.Objects ? bindingDataSurfaceAtlas.Objects->View() : nullptr); context->BindSR(4, bindingDataSurfaceAtlas.Objects ? bindingDataSurfaceAtlas.Objects->View() : nullptr);
context->BindSR(5, bindingDataSurfaceAtlas.AtlasDepth->View()); context->BindSR(5, bindingDataSurfaceAtlas.AtlasDepth->View());
context->BindSR(6, bindingDataSurfaceAtlas.AtlasLighting->View()); context->BindSR(6, bindingDataSurfaceAtlas.AtlasLighting->View());
context->BindSR(7, ddgiData.Result.ProbesState); context->BindSR(7, ddgiData.Result.ProbesData);
context->BindSR(8, skybox); context->BindSR(8, skybox);
context->BindSR(9, ddgiData.ActiveProbes->View()); context->BindSR(9, ddgiData.ActiveProbes->View());
context->BindUA(0, ddgiData.ProbesTrace->View()); context->BindUA(0, ddgiData.ProbesTrace->View());
@@ -570,7 +570,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
// Update probes irradiance and distance textures (one thread-group per probe) // Update probes irradiance and distance textures (one thread-group per probe)
{ {
PROFILE_GPU_CPU_NAMED("Update Probes"); PROFILE_GPU_CPU_NAMED("Update Probes");
context->BindSR(0, ddgiData.Result.ProbesState); context->BindSR(0, ddgiData.Result.ProbesData);
context->BindSR(1, ddgiData.ProbesTrace->View()); context->BindSR(1, ddgiData.ProbesTrace->View());
context->BindSR(2, ddgiData.ActiveProbes->View()); context->BindSR(2, ddgiData.ActiveProbes->View());
context->BindUA(0, ddgiData.Result.ProbesIrradiance); context->BindUA(0, ddgiData.Result.ProbesIrradiance);
@@ -682,7 +682,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
context->BindSR(1, renderContext.Buffers->GBuffer1->View()); context->BindSR(1, renderContext.Buffers->GBuffer1->View());
context->BindSR(2, renderContext.Buffers->GBuffer2->View()); context->BindSR(2, renderContext.Buffers->GBuffer2->View());
context->BindSR(3, renderContext.Buffers->DepthBuffer->View()); context->BindSR(3, renderContext.Buffers->DepthBuffer->View());
context->BindSR(4, ddgiData.Result.ProbesState); context->BindSR(4, ddgiData.Result.ProbesData);
context->BindSR(5, ddgiData.Result.ProbesDistance); context->BindSR(5, ddgiData.Result.ProbesDistance);
context->BindSR(6, ddgiData.Result.ProbesIrradiance); context->BindSR(6, ddgiData.Result.ProbesIrradiance);
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
@@ -724,7 +724,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
context->SetRenderTarget(*renderContext.Buffers->DepthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers))); context->SetRenderTarget(*renderContext.Buffers->DepthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
{ {
// Pass DDGI data to the material // Pass DDGI data to the material
_debugMaterial->SetParameterValue(TEXT("ProbesState"), Variant(ddgiData.ProbesState)); _debugMaterial->SetParameterValue(TEXT("ProbesData"), Variant(ddgiData.ProbesData));
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesIrradiance)); _debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesIrradiance));
_debugMaterial->SetParameterValue(TEXT("ProbesDistance"), Variant(ddgiData.ProbesDistance)); _debugMaterial->SetParameterValue(TEXT("ProbesDistance"), Variant(ddgiData.ProbesDistance));
auto cb = _debugMaterial->GetShader()->GetCB(3); auto cb = _debugMaterial->GetShader()->GetCB(3);

View File

@@ -34,7 +34,7 @@ public:
struct BindingData struct BindingData
{ {
ConstantsData Constants; ConstantsData Constants;
GPUTextureView* ProbesState; GPUTextureView* ProbesData;
GPUTextureView* ProbesDistance; GPUTextureView* ProbesDistance;
GPUTextureView* ProbesIrradiance; GPUTextureView* ProbesIrradiance;
}; };

View File

@@ -1056,7 +1056,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
PROFILE_GPU_CPU_NAMED("DDGI"); PROFILE_GPU_CPU_NAMED("DDGI");
data.DDGI = bindingDataDDGI.Constants; data.DDGI = bindingDataDDGI.Constants;
data.Light.Radius = giSettings.BounceIntensity / bindingDataDDGI.Constants.IndirectLightingIntensity; // Reuse for smaller CB data.Light.Radius = giSettings.BounceIntensity / bindingDataDDGI.Constants.IndirectLightingIntensity; // Reuse for smaller CB
context->BindSR(5, bindingDataDDGI.ProbesState); context->BindSR(5, bindingDataDDGI.ProbesData);
context->BindSR(6, bindingDataDDGI.ProbesDistance); context->BindSR(6, bindingDataDDGI.ProbesDistance);
context->BindSR(7, bindingDataDDGI.ProbesIrradiance); context->BindSR(7, bindingDataDDGI.ProbesIrradiance);
context->UpdateCB(_cb0, &data); context->UpdateCB(_cb0, &data);

View File

@@ -671,7 +671,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
int32 csIndex; int32 csIndex;
if (useDDGI) if (useDDGI)
{ {
context->BindSR(5, bindingDataDDGI.ProbesState); context->BindSR(5, bindingDataDDGI.ProbesData);
context->BindSR(6, bindingDataDDGI.ProbesDistance); context->BindSR(6, bindingDataDDGI.ProbesDistance);
context->BindSR(7, bindingDataDDGI.ProbesIrradiance); context->BindSR(7, bindingDataDDGI.ProbesIrradiance);
csIndex = 1; csIndex = 1;

View File

@@ -13,9 +13,9 @@
#include "./Flax/Math.hlsl" #include "./Flax/Math.hlsl"
#include "./Flax/Octahedral.hlsl" #include "./Flax/Octahedral.hlsl"
#define DDGI_PROBE_STATE_INACTIVE 0.0f #define DDGI_PROBE_STATE_INACTIVE 0
#define DDGI_PROBE_STATE_ACTIVATED 0.2f #define DDGI_PROBE_STATE_ACTIVATED 1
#define DDGI_PROBE_STATE_ACTIVE 1.0f #define DDGI_PROBE_STATE_ACTIVE 2
#define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side)
#define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side)
#define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space #define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space
@@ -89,22 +89,32 @@ float3 GetDDGIProbeWorldPosition(DDGIData data, uint cascadeIndex, uint3 probeCo
return probesOrigin + probePosition - probeGridOffset + (data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing); return probesOrigin + probePosition - probeGridOffset + (data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing);
} }
// Loads probe probe state // Loads probe probe data (encoded)
float LoadDDGIProbeState(DDGIData data, Texture2D<snorm float4> probesState, uint cascadeIndex, uint probeIndex) float4 LoadDDGIProbeData(DDGIData data, Texture2D<snorm float4> probesData, uint cascadeIndex, uint probeIndex)
{ {
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex); int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
float4 probeState = probesState.Load(int3(probeDataCoords, 0)); return probesData.Load(int3(probeDataCoords, 0));
return probeState.w;
} }
// Loads probe world-space position (XYZ) and probe state (W) // Encodes probe probe data
float4 LoadDDGIProbePositionAndState(DDGIData data, Texture2D<snorm float4> probesState, uint cascadeIndex, uint probeIndex, uint3 probeCoords) float4 EncodeDDGIProbeData(float3 probeOffset, uint probeState)
{ {
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex); return float4(probeOffset, (float)probeState * (1.0f / 8.0f));
float4 probeState = probesState.Load(int3(probeDataCoords, 0)); }
probeState.xyz *= data.ProbesOriginAndSpacing[cascadeIndex].w; // Probe offset is [-1;1] within probes spacing
probeState.xyz += GetDDGIProbeWorldPosition(data, cascadeIndex, probeCoords); // Place probe on a grid // Decodes probe state from the encoded state
return probeState; uint DecodeDDGIProbeState(float4 probeData)
{
return (uint)(probeData.w * 8.0f);
}
// Decodes probe world-space position (XYZ) from the encoded state
float3 DecodeDDGIProbePosition(DDGIData data, float4 probeData, uint cascadeIndex, uint probeIndex, uint3 probeCoords)
{
float3 probePosition = probeData.xyz;
probePosition *= data.ProbesOriginAndSpacing[cascadeIndex].w; // Probe offset is [-1;1] within probes spacing
probePosition += GetDDGIProbeWorldPosition(data, cascadeIndex, probeCoords); // Place probe on a grid
return probePosition;
} }
// Calculates texture UVs for sampling probes atlas texture (irradiance or distance) // Calculates texture UVs for sampling probes atlas texture (irradiance or distance)
@@ -122,11 +132,11 @@ float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2
// Samples DDGI probes volume at the given world-space position and returns the irradiance. // Samples DDGI probes volume at the given world-space position and returns the irradiance.
// bias - scales the bias vector to the initial sample point to reduce self-shading artifacts // bias - scales the bias vector to the initial sample point to reduce self-shading artifacts
// dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending // dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending
float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesState, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = 0.2f, float dither = 0.0f) float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = 0.2f, float dither = 0.0f)
{ {
// Select the highest cascade that contains the sample location // Select the highest cascade that contains the sample location
uint cascadeIndex = 0; uint cascadeIndex = 0;
float4 probeStates[8]; float4 probesDatas[8];
for (; cascadeIndex < data.CascadesCount; cascadeIndex++) for (; cascadeIndex < data.CascadesCount; cascadeIndex++)
{ {
float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w; float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
@@ -145,9 +155,10 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesState,
uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1; 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), data.ProbesCounts - uint3(1, 1, 1));
uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords); uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords);
float4 probeState = probesState.Load(int3(GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex), 0)); float4 probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex);
probeStates[i] = probeState; probesDatas[i] = probeData;
if (probeState.w != DDGI_PROBE_STATE_INACTIVE) uint probeState = DecodeDDGIProbeState(probeData);
if (probeState != DDGI_PROBE_STATE_INACTIVE)
activeCount++; activeCount++;
} }
@@ -182,12 +193,12 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesState,
uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords); uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords);
// Load probe position and state // Load probe position and state
float4 probeState = probeStates[i]; float4 probeData = probesDatas[i];
if (probeState.w == DDGI_PROBE_STATE_INACTIVE) uint probeState = DecodeDDGIProbeState(probeData);
if (probeState == DDGI_PROBE_STATE_INACTIVE)
continue; continue;
probeState.xyz *= probesSpacing; // Probe offset is [-1;1] within probes spacing
float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing); float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing);
float3 probePosition = probeBasePosition + probeState.xyz; float3 probePosition = probeBasePosition + probeData.xyz * probesSpacing; // Probe offset is [-1;1] within probes spacing
// Calculate the distance and direction from the (biased and non-biased) shading point and the probe // Calculate the distance and direction from the (biased and non-biased) shading point and the probe
float3 worldPosToProbe = normalize(probePosition - worldPosition); float3 worldPosToProbe = normalize(probePosition - worldPosition);
@@ -233,7 +244,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesState,
#endif #endif
// Debug probe offset visualization // Debug probe offset visualization
//probeIrradiance = float3(max(frac(probeState.xyz) * 2, 0.1f)); //probeIrradiance = float3(max(frac(probeData.xyz) * 2, 0.1f));
// Accumulate weighted irradiance // Accumulate weighted irradiance
irradiance += float4(probeIrradiance * weight, weight); irradiance += float4(probeIrradiance * weight, weight);

View File

@@ -58,17 +58,18 @@ float3 GetProbeRayDirection(DDGIData data, uint rayIndex)
return normalize(QuaternionRotate(data.RaysRotation, direction)); return normalize(QuaternionRotate(data.RaysRotation, direction));
} }
// Checks if the probe states are equal // Calculates amount of rays to allocate for a probe
bool GetProbeState(float a, float b) uint GetProbeRaysCount(DDGIData data, uint probeState)
{ {
return abs(a - b) < 0.05f; // TODO: implement variable ray count based on probe location relative to the view frustum (use probe state for storage)
return data.RaysCount;
} }
#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 #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> RWProbesData : register(u0);
RWByteAddressBuffer RWActiveProbes : register(u1); RWByteAddressBuffer RWActiveProbes : register(u1);
Texture3D<float> GlobalSDFTex : register(t0); Texture3D<float> GlobalSDFTex : register(t0);
@@ -79,7 +80,7 @@ float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin; 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 and performing probes relocation.
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)]
void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
@@ -94,14 +95,15 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w;
// Load probe state and position // Load probe state and position
float4 probeState = RWProbesState[probeDataCoords]; float4 probeData = RWProbesData[probeDataCoords];
probeState.xyz *= probesSpacing; // Probe offset is [-1;1] within probes spacing uint probeState = DecodeDDGIProbeState(probeData);
float3 probeOffset = probeData.xyz * probesSpacing; // Probe offset is [-1;1] within probes spacing
float3 probeOffsetOld = probeOffset;
float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords); float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords);
float3 probePosition = probeBasePosition; float3 probePosition = probeBasePosition;
#if DDGI_PROBE_RELOCATE_ITERATIVE #if DDGI_PROBE_RELOCATE_ITERATIVE
probePosition += probeState.xyz; probePosition += probeOffset;
#endif #endif
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
#if DDGI_PROBE_RELOCATE_ITERATIVE #if DDGI_PROBE_RELOCATE_ITERATIVE
@@ -117,7 +119,8 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
if (sdfDst > distanceLimit) // Probe is too far from geometry if (sdfDst > distanceLimit) // Probe is too far from geometry
{ {
// Disable it // Disable it
probeState = float4(0, 0, 0, DDGI_PROBE_STATE_INACTIVE); probeOffset = float3(0, 0, 0);
probeState = DDGI_PROBE_STATE_INACTIVE;
} }
else else
{ {
@@ -127,22 +130,22 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
if (sdfDst < relocateLimit) if (sdfDst < relocateLimit)
{ {
float3 offsetToAdd = sdfNormal * (sdf + threshold); float3 offsetToAdd = sdfNormal * (sdf + threshold);
if (distance(probeState.xyz, offsetToAdd) < relocateLimit) if (distance(probeOffset, offsetToAdd) < relocateLimit)
{ {
// Relocate it // Relocate it
probeState.xyz += offsetToAdd; probeOffset += offsetToAdd;
} }
} }
else else
{ {
// Reset relocation // Reset relocation
probeState.xyz = float3(0, 0, 0); probeOffset = float3(0, 0, 0);
} }
} }
else if (sdf > threshold * 4.0f) // Probe is far enough from 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); probeOffset = float3(0, 0, 0);
} }
// Check if probe is relocated but the base location is fine // Check if probe is relocated but the base location is fine
@@ -150,7 +153,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
if (sdf > threshold) if (sdf > threshold)
{ {
// Reset relocation // Reset relocation
probeState.xyz = float3(0, 0, 0); probeOffset = float3(0, 0, 0);
} }
#else #else
// Sample Global SDF around the probe location // Sample Global SDF around the probe location
@@ -177,7 +180,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
// Relocate the probe to the best found location (or zero if nothing good found) // Relocate the probe to the best found location (or zero if nothing good found)
if (bestOffset.w <= threshold) if (bestOffset.w <= threshold)
bestOffset.xyz = float3(0, 0, 0); bestOffset.xyz = float3(0, 0, 0);
probeState.xyz = bestOffset.xyz; probeOffset = bestOffset.xyz;
#endif #endif
// Check if probe was scrolled // Check if probe was scrolled
@@ -189,23 +192,21 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
int probeCount = (int)DDGI.ProbesCounts[planeIndex]; int probeCount = (int)DDGI.ProbesCounts[planeIndex];
int newCord = (int)probeCoords[planeIndex] + probeScrollClears[planeIndex]; int newCord = (int)probeCoords[planeIndex] + probeScrollClears[planeIndex];
if (newCord < 0 || newCord >= probeCount) if (newCord < 0 || newCord >= probeCount)
{
wasScrolled = true; wasScrolled = true;
}
} }
// 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 = probeState == DDGI_PROBE_STATE_INACTIVE;
bool wasRelocated = distance(probeState.xyz, probeStateOld.xyz) > 2.0f; bool wasRelocated = distance(probeOffset, probeOffsetOld) > 2.0f;
probeState.w = wasInactive || wasScrolled || wasRelocated ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE; probeState = wasInactive || wasScrolled || wasRelocated ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE;
} }
// Save probe state // Save probe state
probeState.xyz /= probesSpacing; probeOffset /= probesSpacing; // Move offset back to [-1;1] space
RWProbesState[probeDataCoords] = probeState; RWProbesData[probeDataCoords] = EncodeDDGIProbeData(probeOffset, probeState);
// Collect active probes // Collect active probes
if (probeState.w != DDGI_PROBE_STATE_INACTIVE) if (probeState != DDGI_PROBE_STATE_INACTIVE)
{ {
uint activeProbeIndex; uint activeProbeIndex;
RWActiveProbes.InterlockedAdd(0, 1, activeProbeIndex); // Counter at 0 RWActiveProbes.InterlockedAdd(0, 1, activeProbeIndex); // Counter at 0
@@ -250,7 +251,7 @@ ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t3);
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4); Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
Texture2D GlobalSurfaceAtlasDepth : register(t5); Texture2D GlobalSurfaceAtlasDepth : register(t5);
Texture2D GlobalSurfaceAtlasTex : register(t6); Texture2D GlobalSurfaceAtlasTex : register(t6);
Texture2D<snorm float4> ProbesState : register(t7); Texture2D<snorm float4> ProbesData : register(t7);
TextureCube Skybox : register(t8); TextureCube Skybox : register(t8);
ByteAddressBuffer ActiveProbes : register(t9); ByteAddressBuffer ActiveProbes : register(t9);
@@ -269,14 +270,17 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID)
probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords); probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords);
// Load current probe state and position // Load current probe state and position
float4 probePositionAndState = LoadDDGIProbePositionAndState(DDGI, ProbesState, CascadeIndex, probeIndex, probeCoords); float4 probeData = LoadDDGIProbeData(DDGI, ProbesData, CascadeIndex, probeIndex);
if (probePositionAndState.w == DDGI_PROBE_STATE_INACTIVE) uint probeState = DecodeDDGIProbeState(probeData);
return; // Skip disabled probes uint probeRaysCount = GetProbeRaysCount(DDGI, probeState);
if (probeState == DDGI_PROBE_STATE_INACTIVE || probeRaysCount < rayIndex)
return; // Skip disabled probes or if current thread's ray is unused
float3 probePosition = DecodeDDGIProbePosition(DDGI, probeData, CascadeIndex, probeIndex, probeCoords);
float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex); float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex);
// Trace ray with Global SDF // Trace ray with Global SDF
GlobalSDFTrace trace; GlobalSDFTrace trace;
trace.Init(probePositionAndState.xyz, probeRayDirection, 0.0f, DDGI.RayMaxDistance); trace.Init(probePosition, probeRayDirection, 0.0f, DDGI.RayMaxDistance);
GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace);
// Calculate radiance and distance // Calculate radiance and distance
@@ -328,7 +332,7 @@ groupshared float CachedProbesTraceDistance[DDGI_TRACE_RAYS_LIMIT];
groupshared float3 CachedProbesTraceDirection[DDGI_TRACE_RAYS_LIMIT]; groupshared float3 CachedProbesTraceDirection[DDGI_TRACE_RAYS_LIMIT];
RWTexture2D<float4> RWOutput : register(u0); RWTexture2D<float4> RWOutput : register(u0);
Texture2D<snorm float4> ProbesState : register(t0); Texture2D<snorm float4> ProbesData : register(t0);
Texture2D<float4> ProbesTrace : register(t1); Texture2D<float4> ProbesTrace : register(t1);
ByteAddressBuffer ActiveProbes : register(t2); ByteAddressBuffer ActiveProbes : register(t2);
@@ -348,13 +352,15 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
// Skip disabled probes // Skip disabled probes
bool skip = false; bool skip = false;
float probeState = LoadDDGIProbeState(DDGI, ProbesState, CascadeIndex, probeIndex); float4 probeData = LoadDDGIProbeData(DDGI, ProbesData, CascadeIndex, probeIndex);
uint probeState = DecodeDDGIProbeState(probeData);
uint probeRaysCount = GetProbeRaysCount(DDGI, probeState);
if (probeState == DDGI_PROBE_STATE_INACTIVE) if (probeState == DDGI_PROBE_STATE_INACTIVE)
skip = true; skip = true;
#if DDGI_PROBE_UPDATE_MODE == 0 #if DDGI_PROBE_UPDATE_MODE == 0
uint backfacesCount = 0; uint backfacesCount = 0;
uint backfacesLimit = uint(DDGI.RaysCount * 0.1f); uint backfacesLimit = uint(probeRaysCount * 0.1f);
#else #else
float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w;
float distanceLimit = length(probesSpacing) * 1.5f; float distanceLimit = length(probesSpacing) * 1.5f;
@@ -364,9 +370,9 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
if (!skip) if (!skip)
{ {
// Load trace rays results into shared memory to reuse across whole thread group (raysCount per thread) // Load trace rays results into shared memory to reuse across whole thread group (raysCount per thread)
uint raysCount = (uint)(ceil((float)DDGI.RaysCount / (float)(DDGI_PROBE_RESOLUTION * DDGI_PROBE_RESOLUTION))); uint raysCount = (uint)(ceil((float)probeRaysCount / (float)(DDGI_PROBE_RESOLUTION * DDGI_PROBE_RESOLUTION)));
uint raysStart = GroupIndex * raysCount; uint raysStart = GroupIndex * raysCount;
raysCount = max(min(raysStart + raysCount, DDGI.RaysCount), raysStart) - raysStart; raysCount = max(min(raysStart + raysCount, probeRaysCount), raysStart) - raysStart;
for (uint i = 0; i < raysCount; i++) for (uint i = 0; i < raysCount; i++)
{ {
uint rayIndex = raysStart + i; uint rayIndex = raysStart + i;
@@ -392,7 +398,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
// Loop over rays // Loop over rays
float4 result = float4(0, 0, 0, 0); float4 result = float4(0, 0, 0, 0);
LOOP LOOP
for (uint rayIndex = 0; rayIndex < DDGI.RaysCount; rayIndex++) for (uint rayIndex = 0; rayIndex < probeRaysCount; rayIndex++)
{ {
float3 rayDirection = CachedProbesTraceDirection[rayIndex]; float3 rayDirection = CachedProbesTraceDirection[rayIndex];
float rayWeight = max(dot(octahedralDirection, rayDirection), 0.0f); float rayWeight = max(dot(octahedralDirection, rayDirection), 0.0f);
@@ -426,12 +432,12 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
} }
// Normalize results // Normalize results
float epsilon = (float)DDGI.RaysCount * 1e-9f; float epsilon = (float)probeRaysCount * 1e-9f;
result.rgb *= 1.0f / (2.0f * max(result.a, epsilon)); result.rgb *= 1.0f / (2.0f * max(result.a, epsilon));
// Load current probe value // Load current probe value
float3 previous = RWOutput[outputCoords].rgb; float3 previous = RWOutput[outputCoords].rgb;
bool wasActivated = GetProbeState(probeState, DDGI_PROBE_STATE_ACTIVATED); bool wasActivated = probeState == DDGI_PROBE_STATE_ACTIVATED;
if (ResetBlend || wasActivated) if (ResetBlend || wasActivated)
previous = float3(0, 0, 0); previous = float3(0, 0, 0);
@@ -541,7 +547,7 @@ void CS_UpdateBorders(uint3 DispatchThreadId : SV_DispatchThreadID)
#include "./Flax/Random.hlsl" #include "./Flax/Random.hlsl"
#include "./Flax/LightingCommon.hlsl" #include "./Flax/LightingCommon.hlsl"
Texture2D<snorm float4> ProbesState : register(t4); Texture2D<snorm float4> ProbesData : register(t4);
Texture2D<float4> ProbesDistance : register(t5); Texture2D<float4> ProbesDistance : register(t5);
Texture2D<float4> ProbesIrradiance : register(t6); Texture2D<float4> ProbesIrradiance : register(t6);
@@ -565,7 +571,7 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0)
// Sample irradiance // Sample irradiance
float bias = 0.2f; float bias = 0.2f;
float dither = RandN2(input.TexCoord + TemporalTime).x; float dither = RandN2(input.TexCoord + TemporalTime).x;
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias, dither); float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias, dither);
// Calculate lighting // Calculate lighting
float3 diffuseColor = GetDiffuseColor(gBuffer); float3 diffuseColor = GetDiffuseColor(gBuffer);

View File

@@ -87,7 +87,7 @@ float4 PS_ClearLighting(AtlasVertexOutput input) : SV_Target
// GBuffer+Depth at 0-3 slots // GBuffer+Depth at 0-3 slots
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4); Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
#if INDIRECT_LIGHT #if INDIRECT_LIGHT
Texture2D<snorm float4> ProbesState : register(t5); Texture2D<snorm float4> ProbesData : register(t5);
Texture2D<float4> ProbesDistance : register(t6); Texture2D<float4> ProbesDistance : register(t6);
Texture2D<float4> ProbesIrradiance : register(t7); Texture2D<float4> ProbesIrradiance : register(t7);
#else #else
@@ -134,7 +134,7 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target
#if INDIRECT_LIGHT #if INDIRECT_LIGHT
// Sample irradiance // Sample irradiance
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal); float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal);
irradiance *= Light.Radius; // Cached BounceIntensity / IndirectLightingIntensity irradiance *= Light.Radius; // Cached BounceIntensity / IndirectLightingIntensity
// Calculate lighting // Calculate lighting

View File

@@ -286,7 +286,7 @@ Texture3D<float4> LightScatteringHistory : register(t2);
Texture3D<float4> LocalShadowedLightScattering : register(t3); Texture3D<float4> LocalShadowedLightScattering : register(t3);
Texture2DArray ShadowMapCSM : register(t4); Texture2DArray ShadowMapCSM : register(t4);
#if USE_DDGI #if USE_DDGI
Texture2D<snorm float4> ProbesState : register(t5); Texture2D<snorm float4> ProbesData : register(t5);
Texture2D<float4> ProbesDistance : register(t6); Texture2D<float4> ProbesDistance : register(t6);
Texture2D<float4> ProbesIrradiance : register(t7); Texture2D<float4> ProbesIrradiance : register(t7);
#else #else
@@ -337,7 +337,7 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_
#if USE_DDGI #if USE_DDGI
// Dynamic Diffuse Global Illumination // Dynamic Diffuse Global Illumination
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, positionWS, cameraVectorNormalized, 0.0f, cellOffset.x); float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, positionWS, cameraVectorNormalized, 0.0f, cellOffset.x);
lightScattering += float4(irradiance, 1); lightScattering += float4(irradiance, 1);
#else #else
// Sky light // Sky light