From 2e06d1ce2a8606d100134dd919424ee3e465d861 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Fri, 17 Jun 2022 10:41:04 +0200 Subject: [PATCH] Refactor Global SDF to use a single texture for all cascades --- Content/Shaders/GI/DDGI.flax | 4 +- Content/Shaders/GI/GlobalSurfaceAtlas.flax | 4 +- Content/Shaders/GlobalSignDistanceField.flax | 4 +- Content/Shaders/VolumetricFog.flax | 4 +- .../Graphics/Materials/MaterialParams.cpp | 2 +- .../GI/DynamicDiffuseGlobalIllumination.cpp | 20 +-- .../Renderer/GI/GlobalSurfaceAtlasPass.cpp | 36 ++--- .../Renderer/GlobalSignDistanceFieldPass.cpp | 137 ++++++++++-------- .../Renderer/GlobalSignDistanceFieldPass.h | 10 +- .../Engine/Visject/ShaderGraphUtilities.cpp | 3 +- Source/Shaders/GI/DDGI.shader | 20 +-- Source/Shaders/GI/GlobalSurfaceAtlas.shader | 18 +-- Source/Shaders/GlobalSignDistanceField.hlsl | 97 +++++++------ Source/Shaders/GlobalSignDistanceField.shader | 39 +++-- 14 files changed, 204 insertions(+), 194 deletions(-) diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 06036722b..98fdcad04 100644 --- a/Content/Shaders/GI/DDGI.flax +++ b/Content/Shaders/GI/DDGI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48d4226baa1cf8751208681ad1c7d9855d5bbc5fc252acd381c24d293f8a789c -size 19190 +oid sha256:e57f5363c7e39b61123d748ea9134f16ae7d67208cb0c7aeb1e2bae3479a7e77 +size 19642 diff --git a/Content/Shaders/GI/GlobalSurfaceAtlas.flax b/Content/Shaders/GI/GlobalSurfaceAtlas.flax index ad3fe2b33..73cdb345d 100644 --- a/Content/Shaders/GI/GlobalSurfaceAtlas.flax +++ b/Content/Shaders/GI/GlobalSurfaceAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85611a5c4fefa268334bb564654c3631ac676354f2a61b388ca9fa09671d4686 -size 11551 +oid sha256:d102890fcf25252144222b53cd049d2534ce552a951daedd57afbe6ebe13398b +size 11847 diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index 45eb8f6a1..26dcc8847 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc0f1e9b52d89f6bfa8c590deeffb464988d7d666c612592968401da52ad8d2a -size 10668 +oid sha256:a3167ce312acd3367b1107eb442be0177ea2c788800fa6c13b11d0edbae8b559 +size 10992 diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index e82ee9eaa..f8e613b46 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:721f4fa6688bfc97592d1e8747feff21fd313e02fd8e5ee41cfa752fba42fa4d -size 14394 +oid sha256:b38c74ccf0bdeb69ea20b3a495df1e2b53e584982d85e5053a57c8573e65c2e6 +size 14237 diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index 762ed1bb9..790aa6588 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -502,7 +502,7 @@ void MaterialParameter::Bind(BindMeta& meta) const GlobalSignDistanceFieldPass::BindingData bindingData; if (GlobalSignDistanceFieldPass::Instance()->Get(meta.Buffers, bindingData)) Platform::MemoryClear(&bindingData, sizeof(bindingData)); - bindingData.BindCascades(meta.Context, _registerIndex); + meta.Context->BindSR(_registerIndex, bindingData.Texture ? bindingData.Texture->ViewVolume() : nullptr); *((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 76fed479b..e2598c315 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -486,8 +486,8 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, { PROFILE_GPU_CPU("Probes Classification"); uint32 threadGroups = Math::DivideAndRoundUp(probesCountCascade, DDGI_PROBE_CLASSIFY_GROUP_SIZE); - bindingDataSDF.BindCascades(context, 0); - bindingDataSDF.BindCascadeMips(context, 4); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); context->BindUA(0, ddgiData.Result.ProbesState); for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { @@ -529,14 +529,14 @@ 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); - 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()); - context->BindSR(11, bindingDataSurfaceAtlas.AtlasLighting->View()); - context->BindSR(12, ddgiData.Result.ProbesState); - context->BindSR(13, skybox); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); + context->BindSR(2, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr); + context->BindSR(3, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr); + context->BindSR(4, bindingDataSurfaceAtlas.AtlasDepth->View()); + context->BindSR(5, bindingDataSurfaceAtlas.AtlasLighting->View()); + context->BindSR(6, ddgiData.Result.ProbesState); + context->BindSR(7, skybox); context->BindUA(0, ddgiData.ProbesTrace->View()); context->Dispatch(_csTraceRays, probeRaysCount / DDGI_TRACE_RAYS_GROUP_SIZE_X, probesBatchSize, 1); context->ResetUA(); diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 42b984247..584211cf1 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -754,8 +754,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()); - bindingDataSDF.BindCascades(context, 5); - bindingDataSDF.BindCascadeMips(context, 9); + context->BindSR(5, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(6, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); context->BindCB(0, _cb0); Data0 data; data.ViewWorldPos = renderContext.View.Position; @@ -927,12 +927,12 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); } - 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()); - context->BindSR(12, skybox); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); + context->BindSR(2, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); + context->BindSR(3, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); + context->BindSR(4, bindingData.AtlasDepth->View()); + context->BindSR(6, skybox); context->SetState(_psDebug); { Float2 outputSizeThird = outputSize * 0.333f; @@ -943,7 +943,7 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->SetRenderTarget(tempBuffer->View()); // Full screen - direct light - context->BindSR(11, bindingData.AtlasLighting->View()); + context->BindSR(5, bindingData.AtlasLighting->View()); context->SetViewport(outputSize.X, outputSize.Y); context->SetScissor(Rectangle(0, 0, outputSizeTwoThird.X, outputSize.Y)); context->DrawFullscreenTriangle(); @@ -957,12 +957,12 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->ResetRenderTarget(); // Rebind resources - 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()); - context->BindSR(12, skybox); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); + context->BindSR(2, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); + context->BindSR(3, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); + context->BindSR(4, bindingData.AtlasDepth->View()); + context->BindSR(6, skybox); context->BindCB(0, _cb0); context->SetState(_psDebug); context->SetRenderTarget(output->View()); @@ -972,17 +972,17 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->UpdateCB(_cb0, &data); // Bottom left - diffuse - context->BindSR(11, bindingData.AtlasGBuffer0->View()); + context->BindSR(5, bindingData.AtlasGBuffer0->View()); context->SetViewportAndScissors(Viewport(outputSizeTwoThird.X, 0, outputSizeThird.X, outputSizeThird.Y)); context->DrawFullscreenTriangle(); // Bottom middle - normals - context->BindSR(11, bindingData.AtlasGBuffer1->View()); + context->BindSR(5, bindingData.AtlasGBuffer1->View()); context->SetViewportAndScissors(Viewport(outputSizeTwoThird.X, outputSizeThird.Y, outputSizeThird.X, outputSizeThird.Y)); context->DrawFullscreenTriangle(); // Bottom right - roughness/metalness/ao - context->BindSR(11, bindingData.AtlasGBuffer2->View()); + context->BindSR(5, bindingData.AtlasGBuffer2->View()); context->SetViewportAndScissors(Viewport(outputSizeTwoThird.X, outputSizeTwoThird.Y, outputSizeThird.X, outputSizeThird.Y)); context->DrawFullscreenTriangle(); } diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index 0cd7c977e..37e07140d 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -61,14 +61,18 @@ PACK_STRUCT(struct ModelsRasterizeData Int3 ChunkCoord; float MaxDistance; Float3 CascadeCoordToPosMul; - int ObjectsCount; + int32 ObjectsCount; Float3 CascadeCoordToPosAdd; int32 CascadeResolution; - float Padding0; + int32 CascadeIndex; float CascadeVoxelSize; int32 CascadeMipResolution; int32 CascadeMipFactor; uint32 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT]; + uint32 GenerateMipTexResolution; + uint32 GenerateMipCoordScale; + uint32 GenerateMipTexOffsetX; + uint32 GenerateMipMipOffsetX; }); struct RasterizeModel @@ -125,8 +129,6 @@ uint32 GetHash(const RasterizeChunkKey& key) struct CascadeData { - GPUTexture* Texture = nullptr; - GPUTexture* Mip = nullptr; Vector3 Position; float VoxelSize; BoundingBox Bounds; @@ -163,18 +165,14 @@ struct CascadeData } } } - - ~CascadeData() - { - RenderTargetPool::Release(Texture); - RenderTargetPool::Release(Mip); - } }; class GlobalSignDistanceFieldCustomBuffer : public RenderBuffers::CustomBuffer, public ISceneRenderingListener { public: int32 Resolution = 0; + GPUTexture* Texture = nullptr; + GPUTexture* TextureMip = nullptr; Array> Cascades; HashSet ObjectTypes; HashSet SDFTextures; @@ -187,6 +185,8 @@ public: e.Item->Deleted.Unbind(this); e.Item->ResidentMipsChanged.Unbind(this); } + RenderTargetPool::Release(Texture); + RenderTargetPool::Release(TextureMip); } void OnSDFTextureDeleted(ScriptingObject* object) @@ -300,8 +300,7 @@ bool GlobalSignDistanceFieldPass::setupResources() _csRasterizeModel1 = shader->GetCS("CS_RasterizeModel", 1); _csRasterizeHeightfield = shader->GetCS("CS_RasterizeHeightfield"); _csClearChunk = shader->GetCS("CS_ClearChunk"); - _csGenerateMip0 = shader->GetCS("CS_GenerateMip", 0); - _csGenerateMip1 = shader->GetCS("CS_GenerateMip", 1); + _csGenerateMip = shader->GetCS("CS_GenerateMip"); // Init buffer if (!_objectsBuffer) @@ -329,8 +328,7 @@ void GlobalSignDistanceFieldPass::OnShaderReloading(Asset* obj) _csRasterizeModel1 = nullptr; _csRasterizeHeightfield = nullptr; _csClearChunk = nullptr; - _csGenerateMip0 = nullptr; - _csGenerateMip1 = nullptr; + _csGenerateMip = nullptr; _cb0 = nullptr; _cb1 = nullptr; invalidateResources(); @@ -351,18 +349,6 @@ 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; @@ -428,14 +414,13 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex 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) + auto desc = GPUTextureDescription::New3D(resolution * cascadesCount, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); { - GPUTexture*& texture = cascade.Texture; + GPUTexture*& texture = sdfData.Texture; if (texture && texture->Width() != desc.Width) { RenderTargetPool::Release(texture); - texture = nullptr; + sdfData.Texture = nullptr; } if (!texture) { @@ -444,10 +429,11 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex return true; } } - desc.Width = desc.Height = desc.Depth = resolutionMip; + desc.Width = resolutionMip * cascadesCount; + desc.Height = desc.Depth = resolutionMip; for (auto& cascade : sdfData.Cascades) { - GPUTexture*& texture = cascade.Mip; + GPUTexture*& texture = sdfData.TextureMip; if (texture && texture->Width() != desc.Width) { RenderTargetPool::Release(texture); @@ -469,10 +455,12 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex { cascade.NonEmptyChunks.Clear(); cascade.StaticChunks.Clear(); - context->ClearUA(cascade.Texture, Float4::One); - context->ClearUA(cascade.Mip, Float4::One); } - LOG(Info, "Global SDF memory usage: {0} MB", (sdfData.Cascades[0].Texture->GetMemoryUsage() + sdfData.Cascades[0].Mip->GetMemoryUsage()) * ARRAY_COUNT(sdfData.Cascades) / 1024 / 1024); + uint64 memoryUsage = sdfData.Texture->GetMemoryUsage(); + context->ClearUA(sdfData.Texture, Float4::One); + memoryUsage += sdfData.TextureMip->GetMemoryUsage(); + context->ClearUA(sdfData.TextureMip, Float4::One); + LOG(Info, "Global SDF memory usage: {0} MB", memoryUsage / 1024 / 1024); } for (SceneRendering* scene : renderContext.List->Scenes) sdfData.ListenSceneRendering(scene); @@ -498,6 +486,8 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex bool anyDraw = false; const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 }; //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 }; + GPUTextureView* textureView = sdfData.Texture->ViewVolume(); + GPUTextureView* textureMipView = sdfData.TextureMip->ViewVolume(); for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { // Reduce frequency of the updates @@ -514,8 +504,6 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex BoundingBox cascadeBounds(center - cascadeDistance, center + cascadeDistance); // TODO: add scene detail scale factor to PostFx settings (eg. to increase or decrease scene details and quality) const float minObjectRadius = Math::Max(20.0f, cascadeVoxelSize * 0.5f); // Skip too small objects for this cascade - GPUTextureView* cascadeView = cascade.Texture->ViewVolume(); - GPUTextureView* cascadeMipView = cascade.Mip->ViewVolume(); // Clear cascade before rasterization { @@ -539,6 +527,8 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex _objectsBufferCount = 0; _voxelSize = cascadeVoxelSize; _cascadeBounds = cascadeBounds; + _cascadeBounds.Minimum += 0.1f; // Adjust to prevent overflowing chunk keys (cascade bounds are used for clamping object bounds) + _cascadeBounds.Maximum -= 0.1f; // Adjust to prevent overflowing chunk keys (cascade bounds are used for clamping object bounds) _cascadeIndex = cascadeIndex; _sdfData = &sdfData; { @@ -560,10 +550,6 @@ 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 = (Float3)cascadeBounds.GetSize() / (float)resolution; @@ -571,9 +557,10 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex data.MaxDistance = cascadeMaxDistance; data.CascadeResolution = resolution; data.CascadeMipResolution = resolutionMip; + data.CascadeIndex = cascadeIndex; data.CascadeMipFactor = GLOBAL_SDF_RASTERIZE_MIP_FACTOR; data.CascadeVoxelSize = cascadeVoxelSize; - context->BindUA(0, cascadeView); + context->BindUA(0, textureView); context->BindCB(1, _cb1); const int32 chunkDispatchGroups = GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / GLOBAL_SDF_RASTERIZE_GROUP_SIZE; bool anyChunkDispatch = false; @@ -738,25 +725,55 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex if (updated || anyChunkDispatch) { PROFILE_GPU_CPU("Generate Mip"); - context->UpdateCB(_cb1, &data); context->ResetUA(); - context->BindSR(0, cascadeView); - context->BindUA(0, cascadeMipView); const int32 mipDispatchGroups = Math::DivideAndRoundUp(resolutionMip, GLOBAL_SDF_MIP_GROUP_SIZE); + static_assert((GLOBAL_SDF_MIP_FLOODS % 2) == 1, "Invalid Global SDF mip flood iterations count."); int32 floodFillIterations = chunks.Count() == 0 ? 1 : GLOBAL_SDF_MIP_FLOODS; - context->Dispatch(_csGenerateMip0, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); - context->UnBindSR(0); + if (!tmpMip) + { + // Use temporary texture to flood fill mip + auto desc = GPUTextureDescription::New3D(resolutionMip, resolutionMip, resolutionMip, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); + tmpMip = RenderTargetPool::Get(desc); + if (!tmpMip) + return true; + } GPUTextureView* tmpMipView = tmpMip->ViewVolume(); + + // Tex -> Mip + // TODO: use push constants on DX12/Vulkan to provide those 4 uints to the shader + data.GenerateMipTexResolution = data.CascadeResolution; + data.GenerateMipCoordScale = data.CascadeMipFactor; + data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeResolution; + data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution; + context->UpdateCB(_cb1, &data); + context->BindSR(0, textureView); + context->BindUA(0, textureMipView); + context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); + + data.GenerateMipTexResolution = data.CascadeMipResolution; + data.GenerateMipCoordScale = 1; for (int32 i = 1; i < floodFillIterations; i++) { context->ResetUA(); - context->BindSR(0, cascadeMipView); - context->BindUA(0, tmpMipView); - context->Dispatch(_csGenerateMip1, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); - Swap(tmpMipView, cascadeMipView); + if ((i & 1) == 1) + { + // Mip -> Tmp + context->BindSR(0, textureMipView); + context->BindUA(0, tmpMipView); + data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeMipResolution; + data.GenerateMipMipOffsetX = 0; + } + else + { + // Tmp -> Mip + context->BindSR(0, tmpMipView); + context->BindUA(0, textureMipView); + data.GenerateMipTexOffsetX = 0; + data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution; + } + context->UpdateCB(_cb1, &data); + context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); } - if (floodFillIterations % 2 == 0) - Swap(tmpMipView, cascadeMipView); } } @@ -771,26 +788,22 @@ 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."); + result.Texture = sdfData.Texture; + result.TextureMip = sdfData.TextureMip; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { auto& cascade = sdfData.Cascades[cascadeIndex]; const float cascadeDistance = distanceExtent * cascadesDistanceScales[cascadeIndex]; const float cascadeMaxDistance = cascadeDistance * 2; - const float cascadeVoxelSize = cascadeMaxDistance / resolution; + const float cascadeVoxelSize = cascadeMaxDistance / (float)resolution; const Vector3 center = cascade.Position; result.Constants.CascadePosDistance[cascadeIndex] = Vector4(center, cascadeDistance); result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = cascadeVoxelSize; - 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; @@ -820,8 +833,8 @@ void GlobalSignDistanceFieldPass::RenderDebug(RenderContext& renderContext, GPUC context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); } - bindingData.BindCascades(context, 0); - bindingData.BindCascadeMips(context, 4); + context->BindSR(0, bindingData.Texture ? bindingData.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingData.TextureMip ? bindingData.TextureMip->ViewVolume() : nullptr); 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 38e225ac8..3fbbb2776 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h @@ -23,12 +23,9 @@ public: // Binding data for the GPU. struct BindingData { - GPUTexture* Cascades[4]; - GPUTexture* CascadeMips[4]; + GPUTexture* Texture; + GPUTexture* TextureMip; ConstantsData Constants; - - void BindCascades(GPUContext* context, int32 srvSlot); - void BindCascadeMips(GPUContext* context, int32 srvSlot); }; private: @@ -39,8 +36,7 @@ private: GPUShaderProgramCS* _csRasterizeModel1 = nullptr; GPUShaderProgramCS* _csRasterizeHeightfield = nullptr; GPUShaderProgramCS* _csClearChunk = nullptr; - GPUShaderProgramCS* _csGenerateMip0 = nullptr; - GPUShaderProgramCS* _csGenerateMip1 = nullptr; + GPUShaderProgramCS* _csGenerateMip = nullptr; GPUConstantBuffer* _cb0 = nullptr; GPUConstantBuffer* _cb1 = nullptr; diff --git a/Source/Engine/Visject/ShaderGraphUtilities.cpp b/Source/Engine/Visject/ShaderGraphUtilities.cpp index d229d7c03..0cf15010c 100644 --- a/Source/Engine/Visject/ShaderGraphUtilities.cpp +++ b/Source/Engine/Visject/ShaderGraphUtilities.cpp @@ -170,8 +170,7 @@ const Char* ShaderGraphUtilities::GenerateShaderResources(TextWriterUnicode& wri format = TEXT("Texture3D {0} : register(t{1});"); break; case MaterialParameterType::GlobalSDF: - format = TEXT("Texture3D {0}_Tex[4] : register(t{1});"); - registers = 4; + format = TEXT("Texture3D {0}_Tex : register(t{1});"); zeroOffset = false; break; } diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 4a22bba27..9484155c5 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -60,8 +60,8 @@ float3 GetProbeRayDirection(DDGIData data, uint rayIndex) RWTexture2D RWProbesState : register(u0); -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); // Compute shader for updating probes state between active and inactive. META_CS(true, FEATURE_LEVEL_SM5) @@ -138,14 +138,14 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) RWTexture2D RWProbesTrace : register(u0); -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); -ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t8); -Buffer GlobalSurfaceAtlasCulledObjects : register(t9); -Texture2D GlobalSurfaceAtlasDepth : register(t10); -Texture2D GlobalSurfaceAtlasTex : register(t11); -Texture2D ProbesState : register(t12); -TextureCube Skybox : register(t13); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); +ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2); +Buffer GlobalSurfaceAtlasCulledObjects : register(t3); +Texture2D GlobalSurfaceAtlasDepth : register(t4); +Texture2D GlobalSurfaceAtlasTex : register(t5); +Texture2D ProbesState : register(t6); +TextureCube Skybox : register(t7); // Compute shader for tracing rays for probes using Global SDF and Global Surface Atlas. META_CS(true, FEATURE_LEVEL_SM5) diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.shader b/Source/Shaders/GI/GlobalSurfaceAtlas.shader index 7ae54ed83..3993f995b 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.shader @@ -75,8 +75,8 @@ Texture2D ProbesState : register(t5); Texture2D ProbesDistance : register(t6); Texture2D ProbesIrradiance : register(t7); #else -Texture3D GlobalSDFTex[4] : register(t5); -Texture3D GlobalSDFMip[4] : register(t9); +Texture3D GlobalSDFTex : register(t5); +Texture3D GlobalSDFMip : register(t6); #endif // Pixel shader for Global Surface Atlas shading with direct light contribution @@ -265,13 +265,13 @@ void CS_CullObjects(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Disp #ifdef _PS_Debug -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); -ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t8); -Buffer GlobalSurfaceAtlasCulledObjects : register(t9); -Texture2D GlobalSurfaceAtlasDepth : register(t10); -Texture2D GlobalSurfaceAtlasTex : register(t11); -TextureCube Skybox : register(t12); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); +ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2); +Buffer GlobalSurfaceAtlasCulledObjects : register(t3); +Texture2D GlobalSurfaceAtlasDepth : register(t4); +Texture2D GlobalSurfaceAtlasTex : register(t5); +TextureCube Skybox : register(t6); // Pixel shader for Global Surface Atlas debug drawing META_PS(true, FEATURE_LEVEL_SM5) diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index da41b1854..fa4ee618d 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -59,20 +59,27 @@ struct GlobalSDFHit } }; +void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float cascadeMaxDistance, out float3 cascadeUV, out float3 textureUV) +{ + float4 cascadePosDistance = data.CascadePosDistance[cascade]; + float3 posInCascade = worldPosition - cascadePosDistance.xyz; + cascadeMaxDistance = cascadePosDistance.w * 2; + cascadeUV = saturate(posInCascade / cascadeMaxDistance + 0.5f); + textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // cascades are placed next to each other on X axis +} + // 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 tex[4], float3 worldPosition) +float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, float3 worldPosition) { float distance = data.CascadePosDistance[3].w * 2.0f; if (distance <= 0.0f) return GLOBAL_SDF_WORLD_SIZE; - UNROLL for (uint cascade = 0; cascade < data.CascadesCount; cascade++) { - float4 cascadePosDistance = data.CascadePosDistance[cascade]; - float cascadeMaxDistance = cascadePosDistance.w * 2; - float3 posInCascade = worldPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float cascadeDistance = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); + 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; @@ -83,29 +90,27 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 } // Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector. -float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition, out float distance) +float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex, float3 worldPosition, out float distance) { float3 gradient = float3(0, 0.00001f, 0); distance = GLOBAL_SDF_WORLD_SIZE; if (data.CascadePosDistance[3].w <= 0.0f) return gradient; - UNROLL for (uint cascade = 0; cascade < data.CascadesCount; cascade++) { - float4 cascadePosDistance = data.CascadePosDistance[cascade]; - float cascadeMaxDistance = cascadePosDistance.w * 2; - float3 posInCascade = worldPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float cascadeDistance = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); + float cascadeMaxDistance; + float3 cascadeUV, textureUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); + float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); if (cascadeDistance < 0.9f && !any(cascadeUV < 0) && !any(cascadeUV > 1)) { float texelOffset = 1.0f / data.Resolution; - float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x; - float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x; - float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x; - float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x; + float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; distance = cascadeDistance * cascadeMaxDistance; break; @@ -115,7 +120,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4] } // Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector. -float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], Texture3D mips[4], float3 worldPosition, out float distance) +float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex, Texture3D mip, float3 worldPosition, out float distance) { float3 gradient = float3(0, 0.00001f, 0); distance = GLOBAL_SDF_WORLD_SIZE; @@ -123,28 +128,26 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4] return gradient; float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1) float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1) - UNROLL for (uint cascade = 0; cascade < data.CascadesCount; cascade++) { - float4 cascadePosDistance = data.CascadePosDistance[cascade]; - float cascadeMaxDistance = cascadePosDistance.w * 2; - float3 posInCascade = worldPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float cascadeDistance = mips[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); + float cascadeMaxDistance; + float3 cascadeUV, textureUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); + float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); if (cascadeDistance < chunkSizeDistance && !any(cascadeUV < 0) && !any(cascadeUV > 1)) { - float cascadeDistanceTex = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); + float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); if (cascadeDistanceTex < chunkMarginDistance * 2) { cascadeDistance = cascadeDistanceTex; } float texelOffset = 1.0f / data.Resolution; - float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x; - float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x; - float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x; - float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x; + float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; distance = cascadeDistance * cascadeMaxDistance; break; @@ -154,7 +157,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4] } // Ray traces the Global SDF. -GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4], Texture3D mips[4], const GlobalSDFTrace trace) +GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex, Texture3D mip, const GlobalSDFTrace trace) { GlobalSDFHit hit = (GlobalSDFHit)0; hit.HitTime = -1.0f; @@ -163,7 +166,6 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4] float nextIntersectionStart = 0.0f; float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2); float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance; - UNROLL for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++) { float4 cascadePosDistance = data.CascadePosDistance[cascade]; @@ -195,13 +197,14 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4] { float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime; - // Sample SDF - float3 posInCascade = stepPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float stepDistance = mips[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); + // Sample SDF + float cascadeMaxDistance; + float3 cascadeUV, textureUV; + GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeMaxDistance, cascadeUV, textureUV); + float stepDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); if (stepDistance < chunkSizeDistance) { - float stepDistanceTex = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); + float stepDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); if (stepDistanceTex < chunkMarginDistance * 2) { stepDistance = stepDistanceTex; @@ -226,12 +229,12 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4] { // Calculate hit normal from SDF gradient float texelOffset = 1.0f / data.Resolution; - float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x; - float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x; - float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x; - float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x; + float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); } break; diff --git a/Source/Shaders/GlobalSignDistanceField.shader b/Source/Shaders/GlobalSignDistanceField.shader index 4c2ad666e..5f644dec8 100644 --- a/Source/Shaders/GlobalSignDistanceField.shader +++ b/Source/Shaders/GlobalSignDistanceField.shader @@ -37,11 +37,15 @@ float3 CascadeCoordToPosMul; int ObjectsCount; float3 CascadeCoordToPosAdd; int CascadeResolution; -float Padding0; +int CascadeIndex; float CascadeVoxelSize; int CascadeMipResolution; int CascadeMipFactor; uint4 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT / 4]; +uint GenerateMipTexResolution; +uint GenerateMipCoordScale; +uint GenerateMipTexOffsetX; +uint GenerateMipMipOffsetX; META_CB_END float CombineDistanceToSDF(float sdf, float distanceToSDF) @@ -97,10 +101,11 @@ META_CS(true, FEATURE_LEVEL_SM5) META_PERMUTATION_1(READ_SDF=0) META_PERMUTATION_1(READ_SDF=1) [numthreads(GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE)] -void CS_RasterizeModel(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_RasterizeModel(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoord = ChunkCoord + DispatchThreadId; float3 voxelWorldPos = voxelCoord * CascadeCoordToPosMul + CascadeCoordToPosAdd; + voxelCoord.x += CascadeIndex * CascadeResolution; float minDistance = MaxDistance; #if READ_SDF minDistance *= GlobalSDFTex[voxelCoord]; @@ -123,10 +128,11 @@ Texture2D ObjectsTextures[GLOBAL_SDF_RASTERIZE_HEIGHTFIELD_MAX_COUNT] : // Compute shader for rasterizing heightfield into Global SDF META_CS(true, FEATURE_LEVEL_SM5) [numthreads(GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE)] -void CS_RasterizeHeightfield(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_RasterizeHeightfield(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoord = ChunkCoord + DispatchThreadId; float3 voxelWorldPos = voxelCoord * CascadeCoordToPosMul + CascadeCoordToPosAdd; + voxelCoord.x += CascadeIndex * CascadeResolution; float minDistance = MaxDistance * GlobalSDFTex[voxelCoord]; float thickness = CascadeVoxelSize * -8; for (uint i = 0; i < ObjectsCount; i++) @@ -167,9 +173,10 @@ RWTexture3D GlobalSDFTex : register(u0); // Compute shader for clearing Global SDF chunk META_CS(true, FEATURE_LEVEL_SM5) [numthreads(GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE)] -void CS_ClearChunk(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_ClearChunk(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoord = ChunkCoord + DispatchThreadId; + voxelCoord.x += CascadeIndex * CascadeResolution; GlobalSDFTex[voxelCoord] = 1.0f; } @@ -182,21 +189,13 @@ Texture3D GlobalSDFTex : register(t0); float SampleSDF(uint3 voxelCoordMip, int3 offset) { -#if SAMPLE_MIP - // Sampling Global SDF Mip - float resolution = CascadeMipResolution; -#else - // Sampling Global SDF Tex - voxelCoordMip *= CascadeMipFactor; - float resolution = CascadeResolution; -#endif - // Sample SDF - voxelCoordMip = (uint3)clamp((int3)voxelCoordMip + offset, 0, resolution - 1); + voxelCoordMip = (uint3)clamp((int3)voxelCoordMip * GenerateMipCoordScale + offset, 0, GenerateMipTexResolution - 1); + voxelCoordMip.x += GenerateMipTexOffsetX; float result = GlobalSDFTex[voxelCoordMip].r; // Extend by distance to the sampled texel location - float distanceInWorldUnits = length(offset) * (MaxDistance / resolution); + float distanceInWorldUnits = length(offset) * (MaxDistance / (float)GenerateMipTexResolution); float distanceToVoxel = distanceInWorldUnits / MaxDistance; result = CombineDistanceToSDF(result, distanceToVoxel); @@ -205,10 +204,8 @@ float SampleSDF(uint3 voxelCoordMip, int3 offset) // Compute shader for generating mip for Global SDF (uses flood fill algorithm) META_CS(true, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(SAMPLE_MIP=0) -META_PERMUTATION_1(SAMPLE_MIP=1) [numthreads(GLOBAL_SDF_MIP_GROUP_SIZE, GLOBAL_SDF_MIP_GROUP_SIZE, GLOBAL_SDF_MIP_GROUP_SIZE)] -void CS_GenerateMip(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_GenerateMip(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoordMip = DispatchThreadId; float minDistance = SampleSDF(voxelCoordMip, int3(0, 0, 0)); @@ -221,6 +218,7 @@ void CS_GenerateMip(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Disp minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, -1, 0))); minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, 0, -1))); + voxelCoordMip.x += GenerateMipMipOffsetX; GlobalSDFMip[voxelCoordMip] = minDistance; } @@ -228,8 +226,8 @@ void CS_GenerateMip(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Disp #ifdef _PS_Debug -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); // Pixel shader for Global SDF debug drawing META_PS(true, FEATURE_LEVEL_SM5) @@ -241,6 +239,7 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target float mip = 0; uint cascade = 0; float distance01 = GlobalSDFTex[cascade].SampleLevel(SamplerLinearClamp, float3(input.TexCoord, zSlice), mip).x; + //float distance01 = GlobalSDFTex[cascade].SampleLevel(SamplerLinearClamp, float3((input.TexCoord.x + cascade) / (float)GlobalSDF.CascadesCount, input.TexCoord.y, zSlice), mip).x; //float distance01 = GlobalSDFMip[cascade].SampleLevel(SamplerLinearClamp, float3(input.TexCoord, zSlice), mip).x; float distance = distance01 * GlobalSDF.CascadePosDistance[cascade].w; if (abs(distance) < 1)