From bf345f13ce573cc8a7aaffe30aa8fae222d1e19a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 3 Jul 2025 13:54:22 +0200 Subject: [PATCH] Fix reflection probes capture seams on cube face edges due to volumetric fog #3252 --- Source/Engine/Graphics/RenderTask.cpp | 9 ++++++++- Source/Engine/Renderer/ProbesRenderer.cpp | 13 ++++++++++--- Source/Engine/Renderer/RenderSetup.h | 1 + Source/Engine/Renderer/Renderer.cpp | 1 + Source/Engine/Renderer/VolumetricFogPass.cpp | 5 ++--- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index ecdcd572c..ac969ad2d 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -200,12 +200,19 @@ void SceneRenderTask::RemoveGlobalCustomPostFx(PostProcessEffect* fx) void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext) { + PROFILE_CPU(); + // Cache WorldPosition used for PostFx volumes blending (RenderView caches it later on) renderContext.View.WorldPosition = renderContext.View.Origin + renderContext.View.Position; if (EnumHasAllFlags(ActorsSource, ActorsSources::Scenes)) { - Level::CollectPostFxVolumes(renderContext); + //ScopeLock lock(Level::ScenesLock); + for (Scene* scene : Level::Scenes) + { + if (scene->IsActiveInHierarchy()) + scene->Rendering.CollectPostFxVolumes(renderContext); + } } if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomActors)) { diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index 9eb5f3937..eaf7a53ca 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -2,6 +2,7 @@ #include "ProbesRenderer.h" #include "Renderer.h" +#include "RenderList.h" #include "ReflectionsPass.h" #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/Time.h" @@ -17,7 +18,6 @@ #include "Engine/Content/Content.h" #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/AssetReference.h" -#include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/PixelFormat.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/Textures/GPUTexture.h" @@ -115,7 +115,6 @@ private: GPUTexture* _output = nullptr; GPUTexture* _probe = nullptr; GPUTexture* _tmpFace = nullptr; - GPUTexture* _skySHIrradianceMap = nullptr; uint64 _updateFrameNumber = 0; public: @@ -132,6 +131,7 @@ public: private: void OnRender(RenderTask* task, GPUContext* context); + void OnSetupRender(RenderContext& renderContext); #if COMPILE_WITH_DEV_ENV bool _initShader = false; void OnShaderReloading(Asset* obj) @@ -234,6 +234,7 @@ bool ProbesRendererService::LazyInit() task->Order = -100; // Run before main view rendering (realtime probes will get smaller latency) task->Enabled = false; task->IsCustomRendering = true; + task->ActorsSource = ActorsSources::ScenesAndCustomActors; task->Output = _output; auto& view = task->View; view.Flags = @@ -254,6 +255,7 @@ bool ProbesRendererService::LazyInit() task->IsCameraCut = true; task->Resize(probeResolution, probeResolution); task->Render.Bind(this); + task->SetupRender.Bind(this); // Init render targets _probe = GPUDevice::Instance->CreateTexture(TEXT("ProbesRenderer.Probe")); @@ -362,7 +364,6 @@ void ProbesRendererService::Dispose() SAFE_DELETE_GPU_RESOURCE(_output); SAFE_DELETE_GPU_RESOURCE(_probe); SAFE_DELETE_GPU_RESOURCE(_tmpFace); - SAFE_DELETE_GPU_RESOURCE(_skySHIrradianceMap); SAFE_DELETE(_task); _shader = nullptr; _initDone = false; @@ -589,3 +590,9 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) _current.Type = ProbeEntry::Types::Invalid; } } + +void ProbesRendererService::OnSetupRender(RenderContext& renderContext) +{ + // Disable Volumetric Fog in reflection as it causes seams on cubemap face edges + renderContext.List->Setup.UseVolumetricFog = false; +} diff --git a/Source/Engine/Renderer/RenderSetup.h b/Source/Engine/Renderer/RenderSetup.h index 10377e023..3444f0838 100644 --- a/Source/Engine/Renderer/RenderSetup.h +++ b/Source/Engine/Renderer/RenderSetup.h @@ -14,4 +14,5 @@ struct FLAXENGINE_API RenderSetup bool UseTemporalAAJitter = false; bool UseGlobalSDF = false; bool UseGlobalSurfaceAtlas = false; + bool UseVolumetricFog = false; }; diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 87d1d94f1..fd02b133f 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -379,6 +379,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont setup.UseGlobalSDF = (graphicsSettings->EnableGlobalSDF && EnumHasAnyFlags(view.Flags, ViewFlags::GlobalSDF)) || renderContext.View.Mode == ViewMode::GlobalSDF || setup.UseGlobalSurfaceAtlas; + setup.UseVolumetricFog = (view.Flags & ViewFlags::Fog) != ViewFlags::None; // Disable TAA jitter in debug modes switch (renderContext.View.Mode) diff --git a/Source/Engine/Renderer/VolumetricFogPass.cpp b/Source/Engine/Renderer/VolumetricFogPass.cpp index b7e57c2bb..6029b399d 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.cpp +++ b/Source/Engine/Renderer/VolumetricFogPass.cpp @@ -99,7 +99,6 @@ float ComputeZSliceFromDepth(float sceneDepth, const VolumetricFogOptions& optio bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) { - auto& view = renderContext.View; const auto fog = renderContext.List->Fog; // Check if already prepared for this frame @@ -111,7 +110,7 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context, } // Check if skip rendering - if (fog == nullptr || (view.Flags & ViewFlags::Fog) == ViewFlags::None || !_isSupported || checkIfSkipPass()) + if (fog == nullptr || !renderContext.List->Setup.UseVolumetricFog || !_isSupported || checkIfSkipPass()) { RenderTargetPool::Release(renderContext.Buffers->VolumetricFog); renderContext.Buffers->VolumetricFog = nullptr; @@ -184,7 +183,7 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context, _cache.Data.PhaseG = options.ScatteringDistribution; _cache.Data.VolumetricFogMaxDistance = options.Distance; _cache.Data.MissedHistorySamplesCount = Math::Clamp(_cache.MissedHistorySamplesCount, 1, (int32)ARRAY_COUNT(_cache.Data.FrameJitterOffsets)); - Matrix::Transpose(view.PrevViewProjection, _cache.Data.PrevWorldToClip); + Matrix::Transpose(renderContext.View.PrevViewProjection, _cache.Data.PrevWorldToClip); _cache.Data.SkyLight.VolumetricScatteringIntensity = 0; // Fill frame jitter history