diff --git a/Source/Editor/Windows/GraphicsQualityWindow.cs b/Source/Editor/Windows/GraphicsQualityWindow.cs index 9ece021c5..3442a4e7c 100644 --- a/Source/Editor/Windows/GraphicsQualityWindow.cs +++ b/Source/Editor/Windows/GraphicsQualityWindow.cs @@ -64,6 +64,14 @@ namespace FlaxEditor.Windows set => Graphics.VolumetricFogQuality = value; } + [DefaultValue(Quality.High)] + [EditorOrder(1280), EditorDisplay("Quality"), Tooltip("The Global SDF quality. Controls the volume texture resolution and amount of cascades to use.")] + public Quality GlobalSDFQuality + { + get => Graphics.GlobalSDFQuality; + set => Graphics.GlobalSDFQuality = value; + } + [DefaultValue(Quality.Medium)] [EditorOrder(1300), EditorDisplay("Quality", "Shadows Quality"), Tooltip("The shadows quality.")] public Quality ShadowsQuality diff --git a/Source/Engine/Core/Config/GraphicsSettings.cpp b/Source/Engine/Core/Config/GraphicsSettings.cpp index c411fe969..f3e08b63b 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.cpp +++ b/Source/Engine/Core/Config/GraphicsSettings.cpp @@ -13,4 +13,5 @@ void GraphicsSettings::Apply() Graphics::ShadowsQuality = ShadowsQuality; Graphics::ShadowMapsQuality = ShadowMapsQuality; Graphics::AllowCSMBlending = AllowCSMBlending; + Graphics::GlobalSDFQuality = GlobalSDFQuality; } diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 53bf89a61..0ec2b26b1 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -68,6 +68,12 @@ public: API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Global SDF\")") bool EnableGlobalSDF = false; + /// + /// The Global SDF quality. Controls the volume texture resolution and amount of cascades to use. + /// + API_FIELD(Attributes="EditorOrder(2005), DefaultValue(Quality.High), EditorDisplay(\"Quality\")") + Quality GlobalSDFQuality = Quality::High; + #if USE_EDITOR /// /// If checked, the 'Generate SDF' option will be checked on model import options by default. Use it if your project uses Global SDF (eg. for Global Illumination or particles). diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index a1004064a..4cdbf9eb2 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -14,6 +14,7 @@ Quality Graphics::VolumetricFogQuality = Quality::High; Quality Graphics::ShadowsQuality = Quality::Medium; Quality Graphics::ShadowMapsQuality = Quality::Medium; bool Graphics::AllowCSMBlending = false; +Quality Graphics::GlobalSDFQuality = Quality::High; #if GRAPHICS_API_NULL extern GPUDevice* CreateGPUDeviceNull(); diff --git a/Source/Engine/Graphics/Graphics.h b/Source/Engine/Graphics/Graphics.h index 986623b1f..a39c89fd9 100644 --- a/Source/Engine/Graphics/Graphics.h +++ b/Source/Engine/Graphics/Graphics.h @@ -53,6 +53,11 @@ public: /// API_FIELD() static bool AllowCSMBlending; + /// + /// The Global SDF quality. Controls the volume texture resolution and amount of cascades to use. + /// + API_FIELD() static Quality GlobalSDFQuality; + public: /// diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index 87d4193ef..a118526cf 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -488,8 +488,7 @@ void MaterialParameter::Bind(BindMeta& meta) const GlobalSignDistanceFieldPass::BindingData bindingData; if (GlobalSignDistanceFieldPass::Instance()->Get(meta.Buffers, bindingData)) Platform::MemoryClear(&bindingData, sizeof(bindingData)); - for (int32 i = 0; i < 4; i++) - meta.Context->BindSR(_registerIndex + i, bindingData.Cascades[i] ? bindingData.Cascades[i]->ViewVolume() : nullptr); + bindingData.BindCascades(meta.Context, _registerIndex); *((GlobalSignDistanceFieldPass::ConstantsData*)(meta.Constants.Get() + _offset)) = bindingData.Constants; break; } diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index f471cb89a..0e2a9ce7a 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -456,10 +456,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, { PROFILE_GPU_CPU("Probes Classification"); uint32 threadGroups = Math::DivideAndRoundUp(probesCountCascade, DDGI_PROBE_CLASSIFY_GROUP_SIZE); - for (int32 i = 0; i < 4; i++) - { - context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume()); - } + bindingDataSDF.BindCascades(context, 0); context->BindUA(0, ddgiData.Result.ProbesState); for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { @@ -501,11 +498,8 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, // Global SDF with Global Surface Atlas software raytracing (thread X - per probe ray, thread Y - per probe) ASSERT_LOW_LAYER((probeRaysCount % DDGI_TRACE_RAYS_GROUP_SIZE_X) == 0); - for (int32 i = 0; i < 4; i++) - { - context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume()); - context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume()); - } + bindingDataSDF.BindCascades(context, 0); + bindingDataSDF.BindCascadeMips(context, 4); context->BindSR(8, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr); context->BindSR(9, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr); context->BindSR(10, bindingDataSurfaceAtlas.AtlasDepth->View()); diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index eb89f0372..446981076 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -753,11 +753,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co context->BindSR(2, surfaceAtlasData.AtlasGBuffer2->View()); context->BindSR(3, surfaceAtlasData.AtlasDepth->View()); context->BindSR(4, _objectsBuffer->GetBuffer()->View()); - for (int32 i = 0; i < 4; i++) - { - context->BindSR(i + 5, bindingDataSDF.Cascades[i]->ViewVolume()); - context->BindSR(i + 9, bindingDataSDF.CascadeMips[i]->ViewVolume()); - } + bindingDataSDF.BindCascades(context, 5); + bindingDataSDF.BindCascadeMips(context, 9); context->BindCB(0, _cb0); Data0 data; data.ViewWorldPos = renderContext.View.Position; @@ -921,11 +918,8 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); } - for (int32 i = 0; i < 4; i++) - { - context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume()); - context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume()); - } + bindingDataSDF.BindCascades(context, 0); + bindingDataSDF.BindCascadeMips(context, 4); context->BindSR(8, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); context->BindSR(9, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); context->BindSR(10, bindingData.AtlasDepth->View()); @@ -954,11 +948,8 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->ResetRenderTarget(); // Rebind resources - for (int32 i = 0; i < 4; i++) - { - context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume()); - context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume()); - } + bindingDataSDF.BindCascades(context, 0); + bindingDataSDF.BindCascadeMips(context, 4); context->BindSR(8, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); context->BindSR(9, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); context->BindSR(10, bindingData.AtlasDepth->View()); diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index d9682867b..a9346ca2c 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -7,6 +7,7 @@ #include "Engine/Engine/Engine.h" #include "Engine/Content/Content.h" #include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderTargetPool.h" @@ -173,7 +174,8 @@ struct CascadeData class GlobalSignDistanceFieldCustomBuffer : public RenderBuffers::CustomBuffer, public ISceneRenderingListener { public: - CascadeData Cascades[4]; + int32 Resolution = 0; + Array> Cascades; HashSet ObjectTypes; HashSet SDFTextures; GlobalSignDistanceFieldPass::BindingData Result; @@ -349,6 +351,18 @@ void GlobalSignDistanceFieldPass::Dispose() ChunksCache.SetCapacity(0); } +void GlobalSignDistanceFieldPass::BindingData::BindCascades(GPUContext* context, int32 srvSlot) +{ + for (int32 i = 0; i < 4; i++) + context->BindSR(srvSlot + i, Cascades[i] ? Cascades[i]->ViewVolume() : nullptr); +} + +void GlobalSignDistanceFieldPass::BindingData::BindCascadeMips(GPUContext* context, int32 srvSlot) +{ + for (int32 i = 0; i < 4; i++) + context->BindSR(srvSlot + i, CascadeMips[i] ? CascadeMips[i]->ViewVolume() : nullptr); +} + bool GlobalSignDistanceFieldPass::Get(const RenderBuffers* buffers, BindingData& result) { auto* sdfData = buffers ? buffers->FindCustomBuffer(TEXT("GlobalSignDistanceField")) : nullptr; @@ -379,49 +393,71 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex sdfData.LastFrameUsed = currentFrame; PROFILE_GPU_CPU("Global SDF"); - // TODO: configurable via graphics settings - const int32 resolution = 256; + // Setup options + int32 resolution, cascadesCount; + switch (Graphics::GlobalSDFQuality) + { + case Quality::Low: + resolution = 128; + cascadesCount = 2; + break; + case Quality::Medium: + resolution = 128; + cascadesCount = 3; + break; + case Quality::High: + resolution = 192; + cascadesCount = 4; + break; + case Quality::Ultra: + default: + resolution = 256; + cascadesCount = 4; + break; + } const int32 resolutionMip = Math::DivideAndRoundUp(resolution, GLOBAL_SDF_RASTERIZE_MIP_FACTOR); - // TODO: configurable via postFx settings - const int32 cascadesCount = 4; // in range 1-4 const float distance = true ? 20000.0f : 16000.0f; // TODO: switch based if using GI, then use GI range - const float cascadesDistanceScales[] = { 1.0f, 2.0f, 4.0f, 8.0f }; + const float cascadesDistanceScales[] = { 1.0f, 2.5f, 5.0f, 10.0f }; const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1]; // Initialize buffers - auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); bool updated = false; - for (auto& cascade : sdfData.Cascades) + if (sdfData.Cascades.Count() != cascadesCount || sdfData.Resolution != resolution) { - GPUTexture*& texture = cascade.Texture; - if (texture && texture->Width() != desc.Width) + sdfData.Cascades.Resize(cascadesCount); + sdfData.Resolution = resolution; + updated = true; + auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); + for (auto& cascade : sdfData.Cascades) { - RenderTargetPool::Release(texture); - texture = nullptr; - } - if (!texture) - { - texture = RenderTargetPool::Get(desc); + GPUTexture*& texture = cascade.Texture; + if (texture && texture->Width() != desc.Width) + { + RenderTargetPool::Release(texture); + texture = nullptr; + } if (!texture) - return true; - updated = true; + { + texture = RenderTargetPool::Get(desc); + if (!texture) + return true; + } } - } - desc = GPUTextureDescription::New3D(resolutionMip, resolutionMip, resolutionMip, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); - for (auto& cascade : sdfData.Cascades) - { - GPUTexture*& texture = cascade.Mip; - if (texture && texture->Width() != desc.Width) + desc.Width = desc.Height = desc.Depth = resolutionMip; + for (auto& cascade : sdfData.Cascades) { - RenderTargetPool::Release(texture); - texture = nullptr; - } - if (!texture) - { - texture = RenderTargetPool::Get(desc); + GPUTexture*& texture = cascade.Mip; + if (texture && texture->Width() != desc.Width) + { + RenderTargetPool::Release(texture); + texture = nullptr; + } if (!texture) - return true; - updated = true; + { + texture = RenderTargetPool::Get(desc); + if (!texture) + return true; + } } } GPUTexture* tmpMip = nullptr; @@ -451,7 +487,7 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex bool anyDraw = false; const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 }; //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 }; - for (int32 cascadeIndex = 0; cascadeIndex < 4; cascadeIndex++) + for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { // Reduce frequency of the updates if (useCache && (Engine::FrameCount % cascadeFrequencies[cascadeIndex]) != 0) @@ -513,12 +549,13 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex { anyDraw = true; context->ResetSR(); + auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); tmpMip = RenderTargetPool::Get(desc); if (!tmpMip) return true; } ModelsRasterizeData data; - data.CascadeCoordToPosMul = cascadeBounds.GetSize() / resolution; + data.CascadeCoordToPosMul = cascadeBounds.GetSize() / (float)resolution; data.CascadeCoordToPosAdd = cascadeBounds.Minimum + cascadeVoxelSize * 0.5f; data.MaxDistance = cascadeMaxDistance; data.CascadeResolution = resolution; @@ -725,8 +762,7 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex // Copy results static_assert(ARRAY_COUNT(result.Cascades) == ARRAY_COUNT(sdfData.Cascades), "Invalid cascades count."); static_assert(ARRAY_COUNT(result.CascadeMips) == ARRAY_COUNT(sdfData.Cascades), "Invalid cascades count."); - static_assert(ARRAY_COUNT(sdfData.Cascades) == 4, "Invalid cascades count."); - for (int32 cascadeIndex = 0; cascadeIndex < 4; cascadeIndex++) + for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { auto& cascade = sdfData.Cascades[cascadeIndex]; const float cascadeDistance = distanceExtent * cascadesDistanceScales[cascadeIndex]; @@ -738,7 +774,15 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex result.Cascades[cascadeIndex] = cascade.Texture; result.CascadeMips[cascadeIndex] = cascade.Mip; } + for (int32 cascadeIndex = cascadesCount; cascadeIndex < 4; cascadeIndex++) + { + result.Constants.CascadePosDistance[cascadeIndex] = result.Constants.CascadePosDistance[cascadesCount - 1]; + result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = result.Constants.CascadeVoxelSize.Raw[cascadesCount - 1]; + result.Cascades[cascadeIndex] = nullptr; + result.CascadeMips[cascadeIndex] = nullptr; + } result.Constants.Resolution = (float)resolution; + result.Constants.CascadesCount = cascadesCount; sdfData.Result = result; return false; } @@ -765,11 +809,8 @@ void GlobalSignDistanceFieldPass::RenderDebug(RenderContext& renderContext, GPUC context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); } - for (int32 i = 0; i < 4; i++) - { - context->BindSR(i, bindingData.Cascades[i]->ViewVolume()); - context->BindSR(i + 4, bindingData.CascadeMips[i]->ViewVolume()); - } + bindingData.BindCascades(context, 0); + bindingData.BindCascadeMips(context, 4); context->SetState(_psDebug); context->SetRenderTarget(output->View()); context->SetViewportAndScissors(outputSize.X, outputSize.Y); diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h index 5e1f0461a..df3f730cc 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h @@ -15,7 +15,8 @@ public: { Vector4 CascadePosDistance[4]; Vector4 CascadeVoxelSize; - Vector3 Padding; + Vector2 Padding; + uint32 CascadesCount; float Resolution; }); @@ -25,6 +26,9 @@ public: GPUTexture* Cascades[4]; GPUTexture* CascadeMips[4]; ConstantsData Constants; + + void BindCascades(GPUContext* context, int32 srvSlot); + void BindCascadeMips(GPUContext* context, int32 srvSlot); }; private: diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 08a3525e1..70cb92834 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -181,7 +181,7 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) { // Sample Global Surface Atlas to get the lighting at the hit location float3 hitPosition = hit.GetHitPosition(trace); - float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(hit); + float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, hit); float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -probeRayDirection, surfaceThreshold); radiance = float4(surfaceColor.rgb, hit.HitTime); diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.shader b/Source/Shaders/GI/GlobalSurfaceAtlas.shader index 194375636..30f455675 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.shader @@ -293,7 +293,7 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target if (hit.IsHit()) { // Sample Global Surface Atlas at the hit location - float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(hit); + float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, hit); color = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay, surfaceThreshold).rgb; //color = hit.HitNormal * 0.5f + 0.5f; } diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 6ee0efc75..e7b4c99c1 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -13,7 +13,8 @@ struct GlobalSDFData { float4 CascadePosDistance[4]; float4 CascadeVoxelSize; - float3 Padding; + float2 Padding; + uint CascadesCount; float Resolution; }; @@ -65,7 +66,7 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 if (distance <= 0.0f) return GLOBAL_SDF_WORLD_SIZE; UNROLL - for (uint cascade = 0; cascade < 4; cascade++) + for (uint cascade = 0; cascade < data.CascadesCount; cascade++) { float4 cascadePosDistance = data.CascadePosDistance[cascade]; float cascadeMaxDistance = cascadePosDistance.w * 2; @@ -89,7 +90,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4] if (data.CascadePosDistance[3].w <= 0.0f) return gradient; UNROLL - for (uint cascade = 0; cascade < 4; cascade++) + for (uint cascade = 0; cascade < data.CascadesCount; cascade++) { float4 cascadePosDistance = data.CascadePosDistance[cascade]; float cascadeMaxDistance = cascadePosDistance.w * 2; @@ -124,7 +125,7 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4] float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2); float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance; UNROLL - for (uint cascade = 0; cascade < 4 && hit.HitTime < 0.0f; cascade++) + for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++) { float4 cascadePosDistance = data.CascadePosDistance[cascade]; float cascadeMaxDistance = cascadePosDistance.w * 2; @@ -206,8 +207,8 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4] } // Calculates the surface threshold for Global Surface Atlas sampling which matches the Global SDF trace to reduce artifacts -float GetGlobalSurfaceAtlasThreshold(GlobalSDFHit hit) +float GetGlobalSurfaceAtlasThreshold(const GlobalSDFData data, const GlobalSDFHit hit) { - // Scale the threshold based on the hit cascade (less precision) - return hit.HitCascade * 20.0f + 25.0f; + // Scale the threshold based on the hit cascade (less precision) + return data.CascadeVoxelSize[hit.HitCascade] * 1.1f; }