diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 3bb5e5186..bfc93300d 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:7662750c2a8aa5997afb0937158fa64784f73716d82785e5715cbf6bdc774e11 -size 23610 +oid sha256:dc50eaaab4396490e2fdb7a6de00909a6d44f726b905009e6395edb3a021947e +size 23637 diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index 81f031035..b0aef806a 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -403,7 +403,7 @@ void SceneRenderTask::OnBegin(GPUContext* context) void SceneRenderTask::OnRender(GPUContext* context) { - if (Buffers && Buffers->GetWidth() > 0) + if (!IsCustomRendering && Buffers && Buffers->GetWidth() > 0) Renderer::Render(this); RenderTask::OnRender(context); diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 43ae15f71..71c122ffd 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -228,6 +228,11 @@ public: /// bool IsCameraCut = true; + /// + /// True if the task is used for custom scene rendering and default scene drawing into output should be skipped. Enable it if you use Render event and draw scene manually. + /// + API_FIELD() bool IsCustomRendering = false; + /// /// Marks the next rendered frame as camera cut. Used to clear the temporal effects history and prevent visual artifacts blended from the previous frames. /// diff --git a/Source/Engine/Renderer/AtmospherePreCompute.cpp b/Source/Engine/Renderer/AtmospherePreCompute.cpp index 1ccd0e1ca..7fb60155f 100644 --- a/Source/Engine/Renderer/AtmospherePreCompute.cpp +++ b/Source/Engine/Renderer/AtmospherePreCompute.cpp @@ -88,7 +88,7 @@ namespace AtmospherePreComputeImpl GPUPipelineState* _psCopyInscatterNAdd = nullptr; GPUPipelineState* _psInscatterS = nullptr; GPUPipelineState* _psInscatterN = nullptr; - RenderTask* _task = nullptr; + SceneRenderTask* _task = nullptr; // GPUTexture* AtmosphereTransmittance = nullptr; @@ -166,12 +166,10 @@ bool init() return true; } auto shader = _shader->GetShader(); - - // Validate shader constant buffers sizes ASSERT(shader->GetCB(0) != nullptr); if (shader->GetCB(0)->GetSize() != sizeof(Data)) { - LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", _shader->ToString(), 0, shader->GetCB(0)->GetSize(), sizeof(Data)); + REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data); return true; } @@ -252,6 +250,7 @@ bool init() // Init rendering pipeline _task = New(); _task->Enabled = false; + _task->IsCustomRendering = true; _task->Render.Bind(onRender); // Init render targets diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index e5e14acd7..9082555b2 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -82,6 +82,7 @@ public: int32 CascadesCount = 0; int32 ProbeRaysCount = 0; + int32 ProbesCountTotal = 0; Int3 ProbeCounts = Int3::Zero; GPUTexture* ProbesTrace = nullptr; // Probes ray tracing: (RGB: hit radiance, A: hit distance) GPUTexture* ProbesState = nullptr; // Probes state: (RGB: world-space offset, A: state) @@ -194,7 +195,7 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources() { _psIndirectLighting = device->CreatePipelineState(); psDesc.PS = shader->GetPS("PS_IndirectLighting"); - psDesc.BlendMode = BlendingMode::Additive; + psDesc.BlendMode = BlendingMode::Add; if (_psIndirectLighting->Init(psDesc)) return true; } @@ -251,15 +252,8 @@ bool DynamicDiffuseGlobalIlluminationPass::Get(const RenderBuffers* buffers, Bin return true; } -bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer) +bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderContext, GPUContext* context, DDGICustomBuffer& ddgiData) { - // Skip if not supported - if (checkIfSkipPass()) - return true; - if (renderContext.List->Scenes.Count() == 0) - return true; - auto& ddgiData = *renderContext.Buffers->GetCustomBuffer(TEXT("DDGI")); - // Render Global SDF and Global Surface Atlas for software raytracing GlobalSignDistanceFieldPass::BindingData bindingDataSDF; if (GlobalSignDistanceFieldPass::Instance()->Render(renderContext, context, bindingDataSDF)) @@ -269,13 +263,6 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, return true; GPUTextureView* skybox = GBufferPass::Instance()->RenderSkybox(renderContext, context); - // Skip if already done in the current frame - const auto currentFrame = Engine::FrameCount; - if (ddgiData.LastFrameUsed == currentFrame) - return false; - ddgiData.LastFrameUsed = currentFrame; - PROFILE_GPU_CPU("Dynamic Diffuse Global Illumination"); - // Setup options auto& settings = renderContext.List->Settings.GlobalIllumination; auto* graphicsSettings = GraphicsSettings::Get(); @@ -299,7 +286,6 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, return true; } ASSERT_LOW_LAYER(probeRaysCount <= DDGI_TRACE_RAYS_LIMIT); - bool debugProbes = renderContext.View.Mode == ViewMode::GlobalIllumination; const float indirectLightingIntensity = settings.Intensity; const float probeHistoryWeight = Math::Clamp(settings.TemporalResponse, 0.0f, 0.98f); const float distance = settings.Distance; @@ -314,7 +300,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, idealDistance *= 2; cascadesCount++; } - + // Calculate the probes count based on the amount of cascades and the distance to cover const float cascadesDistanceScales[] = { 1.0f, 3.0f, 6.0f, 10.0f }; // Scales each cascade further away from the camera origin const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1]; @@ -369,6 +355,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, ddgiData.Release(); ddgiData.CascadesCount = cascadesCount; ddgiData.ProbeRaysCount = probeRaysCount; + ddgiData.ProbesCountTotal = probesCountTotal; ddgiData.ProbeCounts = probesCounts; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { @@ -408,6 +395,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, context->ClearUA(ddgiData.ProbesIrradiance, Float4::Zero); context->ClearUA(ddgiData.ProbesDistance, Float4::Zero); } + ddgiData.LastFrameUsed = Engine::FrameCount; // Calculate which cascades should be updated this frame const uint64 cascadeFrequencies[] = { 2, 3, 5, 7 }; @@ -416,7 +404,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, bool cascadeSkipUpdate[4]; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { - cascadeSkipUpdate[cascadeIndex] = !clear && (currentFrame % cascadeFrequencies[cascadeIndex]) != 0; + cascadeSkipUpdate[cascadeIndex] = !clear && (ddgiData.LastFrameUsed % cascadeFrequencies[cascadeIndex]) != 0; } // Compute scrolling (probes are placed around camera but are scrolling to increase stability during movement) @@ -622,6 +610,54 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, } } + return false; +} + +bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer) +{ + if (checkIfSkipPass()) + return true; + if (renderContext.List->Scenes.Count() == 0) + return true; + RenderBuffers* renderBuffers = renderContext.Buffers; + bool render = true; + if (renderContext.View.IsOfflinePass) + { + // During offline pass (eg. probes rendering) we can try reuse main game viewport or editor viewport DDGI probes + // TODO: apply it for transparency too (in DynamicDiffuseGlobalIlluminationPass::Get) + for (auto* task : RenderTask::Tasks) + { + if (auto* sceneTask = ScriptingObject::Cast(task)) + { + auto* sceneTaskDDGI = sceneTask->Buffers ? sceneTask->Buffers->FindCustomBuffer(TEXT("DDGI")) : nullptr; + if (sceneTaskDDGI && sceneTaskDDGI->LastFrameUsed + 1 >= Engine::FrameCount) + { + // Reuse DDGI from this task + renderBuffers = sceneTask->Buffers; + render = false; + break; + } + } + } + } + auto& ddgiData = *renderBuffers->GetCustomBuffer(TEXT("DDGI")); + if (render && ddgiData.LastFrameUsed == Engine::FrameCount) + render = false; + + PROFILE_GPU_CPU("Dynamic Diffuse Global Illumination"); + + if (render) + { + if (RenderInner(renderContext, context, ddgiData)) + { + context->ResetRenderTarget(); + context->ResetSR(); + context->ResetUA(); + context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); + return true; + } + } + // Render indirect lighting if (lightBuffer) { @@ -630,6 +666,15 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, // DDGI indirect lighting debug preview context->Clear(lightBuffer, Color::Transparent); #endif + if (!render) + { + Data0 data; + data.DDGI = ddgiData.Result.Constants; + data.TemporalTime = 0.0f; + GBufferPass::SetInputs(renderContext.View, data.GBuffer); + context->UpdateCB(_cb0, &data); + context->BindCB(0, _cb0); + } context->BindSR(0, renderContext.Buffers->GBuffer0->View()); context->BindSR(1, renderContext.Buffers->GBuffer1->View()); context->BindSR(2, renderContext.Buffers->GBuffer2->View()); @@ -645,7 +690,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, #if USE_EDITOR // Probes debug drawing - if (debugProbes && lightBuffer) + if (renderContext.View.Mode == ViewMode::GlobalIllumination && lightBuffer) { PROFILE_GPU_CPU("Debug Probes"); if (!_debugModel) @@ -661,7 +706,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, Matrix world; Matrix::Scaling(Float3(0.2f), world); const Mesh& debugMesh = _debugModel->LODs[0].Meshes[0]; - for (int32 probeIndex = 0; probeIndex < probesCountTotal; probeIndex++) + for (int32 probeIndex = 0; probeIndex < ddgiData.ProbesCountTotal; probeIndex++) debugMesh.Draw(debugRenderContext, _debugMaterial, world, StaticFlags::None, true, DrawPass::GBuffer, (float)probeIndex); debugRenderContext.List->SortDrawCalls(debugRenderContext, false, DrawCallsListType::GBuffer); context->SetViewportAndScissors(debugRenderContext.View.ScreenSize.X, debugRenderContext.View.ScreenSize.Y); @@ -688,14 +733,13 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, } debugRenderContext.List->ExecuteDrawCalls(debugRenderContext, DrawCallsListType::GBuffer); RenderList::ReturnToPool(debugRenderContext.List); - context->UnBindCB(3); - context->ResetRenderTarget(); } } #endif context->ResetRenderTarget(); context->ResetSR(); - + context->ResetUA(); + context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); return false; } diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index af3d89083..a6bdc9e9e 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -82,6 +82,7 @@ private: uint64 LastFrameShaderReload = 0; void OnShaderReloading(Asset* obj); #endif + bool RenderInner(RenderContext& renderContext, GPUContext* context, class DDGICustomBuffer& ddgiData); public: // [RendererPass] diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 716e6e8ee..f80673654 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -1043,14 +1043,14 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co } } } - - context->ResetSR(); - context->ResetRenderTarget(); } // TODO: explore atlas tiles optimization with feedback from renderer (eg. when tile is sampled by GI/Reflections mark it as used, then sort tiles by importance and prioritize updates for ones frequently used) #undef WRITE_TILE + context->ResetSR(); + context->ResetRenderTarget(); + context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); return notReady; } diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index 3c12f5986..26832871a 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -29,7 +29,6 @@ /// /// Custom task called after downloading probe texture data to save it. /// -/// class DownloadProbeTask : public ThreadPoolTask { private: @@ -38,31 +37,19 @@ private: ProbesRenderer::Entry _entry; public: - /// - /// Initializes a new instance of the class. - /// - /// The target. - /// The entry. DownloadProbeTask(GPUTexture* target, const ProbesRenderer::Entry& entry) : _texture(target) , _entry(entry) { } -public: - /// - /// Gets the texture data container. - /// FORCE_INLINE TextureData& GetData() { return _data; } -protected: - // [ThreadPoolTask] bool Run() override { - // Switch type if (_entry.Type == ProbesRenderer::EntryType::EnvProbe) { if (_entry.Actor) @@ -78,7 +65,6 @@ protected: return true; } - // Fire event ProbesRenderer::OnFinishBake(_entry); return false; @@ -239,13 +225,9 @@ bool ProbesRenderer::Init() if (!_shader->IsLoaded()) return false; const auto shader = _shader->GetShader(); - - LOG(Info, "Starting Probes Renderer service"); - - // Validate shader constant buffers sizes if (shader->GetCB(0)->GetSize() != sizeof(Data)) { - LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", _shader.ToString(), 0, shader->GetCB(0)->GetSize(), sizeof(Data)); + REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data); return true; } @@ -296,6 +278,7 @@ bool ProbesRenderer::Init() _task = New(); auto task = _task; task->Enabled = false; + task->IsCustomRendering = true; task->Output = _output; auto& view = task->View; view.Flags = @@ -310,6 +293,7 @@ bool ProbesRenderer::Init() ViewFlags::Fog; view.Mode = ViewMode::NoPostFx; view.IsOfflinePass = true; + view.IsSingleFrame = true; view.StaticFlagsMask = StaticFlags::ReflectionProbe; view.MaxShadowsQuality = Quality::Low; task->IsCameraCut = true; @@ -320,7 +304,7 @@ bool ProbesRenderer::Init() _probe = GPUDevice::Instance->CreateTexture(TEXT("ProbesUpdate.Probe")); if (_probe->Init(GPUTextureDescription::NewCube(probeResolution, _output->Format(), GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews, 0))) return true; - _tmpFace = GPUDevice::Instance->CreateTexture(TEXT("ProbesUpdate.TmpFae")); + _tmpFace = GPUDevice::Instance->CreateTexture(TEXT("ProbesUpdate.TmpFace")); if (_tmpFace->Init(GPUTextureDescription::New2D(probeResolution, probeResolution, 0, _output->Format(), GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews))) return true; @@ -335,8 +319,6 @@ void ProbesRenderer::Release() return; ASSERT(_updateFrameNumber == 0); - LOG(Info, "Disposing Probes Renderer service"); - // Release GPU data if (_output) _output->ReleaseGPU(); @@ -471,7 +453,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) return; } - bool setLowerHemisphereToBlack = false; auto shader = _shader->GetShader(); PROFILE_GPU("Render Probe"); @@ -487,7 +468,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) float nearPlane = Math::Max(0.1f, envProbe->CaptureNearPlane); // Fix far plane distance - // TODO: investigate performance of this action, maybe we could skip it? float farPlane = Math::Max(radius, nearPlane + 100.0f); Function f(&fixFarPlaneTreeExecute); SceneQuery::TreeExecute(f, position, farPlane); @@ -505,12 +485,11 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) float farPlane = Math::Max(nearPlane + 1000.0f, skyLight->SkyDistanceThreshold * 2.0f); customCullingNear = skyLight->SkyDistanceThreshold; - // TODO: use setLowerHemisphereToBlack feature for SkyLight? - // Setup view LargeWorlds::UpdateOrigin(_task->View.Origin, position); _task->View.SetUpCube(nearPlane, farPlane, position - _task->View.Origin); } + _task->CameraCut(); // Resize buffers bool resizeFailed = _output->Resize(probeResolution, probeResolution); @@ -527,7 +506,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) // Render scene for all faces for (int32 faceIndex = 0; faceIndex < 6; faceIndex++) { - // Set view _task->View.SetFace(faceIndex); // Handle custom frustum for the culling (used to skip objects near the camera) @@ -547,21 +525,7 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) PROFILE_GPU("Copy Face"); context->SetRenderTarget(_probe->View(faceIndex)); context->SetViewportAndScissors((float)probeResolution, (float)probeResolution); - auto probeFrame = _output->View(); - if (setLowerHemisphereToBlack && faceIndex != 2) - { - MISSING_CODE("set lower hemisphere of the probe to black"); - /* - ProbesFilter_Tmp.Set(_core.Rendering.Pool.RT2_FloatRGB.Handle); - ProbesFilter_CoefficientMask2.Set(f != 3 ? 1.0f : 0.0f); - ProbesFilter_Shader.Apply(5); - _device.DrawFullscreenTriangle(); - */ - } - else - { - context->Draw(probeFrame); - } + context->Draw(_output->View()); context->ResetRenderTarget(); } } @@ -577,16 +541,9 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) auto cb = shader->GetCB(0); for (int32 mipIndex = 1; mipIndex < mipLevels; mipIndex++) { - // Cache data - int32 srcMipIndex = mipIndex - 1; int32 mipSize = 1 << (mipLevels - mipIndex - 1); - data.SourceMipIndex = srcMipIndex; - - // Set viewport - float mipSizeFloat = (float)mipSize; - context->SetViewportAndScissors(mipSizeFloat, mipSizeFloat); - - // Filter all faces + data.SourceMipIndex = mipIndex - 1; + context->SetViewportAndScissors((float)mipSize, (float)mipSize); for (int32 faceIndex = 0; faceIndex < 6; faceIndex++) { context->ResetSR(); @@ -600,12 +557,14 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) context->SetRenderTarget(_tmpFace->View(0, mipIndex)); context->SetState(_psFilterFace); context->DrawFullscreenTriangle(); - context->ResetSR(); context->ResetRenderTarget(); // Copy face back to the cubemap - copyTmpToFace(context, mipIndex, faceIndex); + context->SetRenderTarget(_probe->View(faceIndex, mipIndex)); + context->BindSR(1, _tmpFace->View(0, mipIndex)); + context->SetState(_psCopyFace); + context->DrawFullscreenTriangle(); } } } @@ -618,17 +577,4 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) _task->Enabled = false; } -void ProbesRenderer::copyTmpToFace(GPUContext* context, int32 mipIndex, int32 faceIndex) -{ - // Set destination render target - context->SetRenderTarget(_probe->View(faceIndex, mipIndex)); - - // Setup shader constants - context->BindSR(1, _tmpFace->View(0, mipIndex)); - - // Copy pixels - context->SetState(_psCopyFace); - context->DrawFullscreenTriangle(); -} - #endif diff --git a/Source/Engine/Renderer/ProbesRenderer.h b/Source/Engine/Renderer/ProbesRenderer.h index e9fa24b97..3de952db4 100644 --- a/Source/Engine/Renderer/ProbesRenderer.h +++ b/Source/Engine/Renderer/ProbesRenderer.h @@ -105,7 +105,6 @@ public: private: static void onRender(RenderTask* task, GPUContext* context); - static void copyTmpToFace(GPUContext* context, int32 mipIndex, int32 faceIndex); }; #endif diff --git a/Source/Engine/Renderer/RendererPass.h b/Source/Engine/Renderer/RendererPass.h index 3a4146ed1..80e465473 100644 --- a/Source/Engine/Renderer/RendererPass.h +++ b/Source/Engine/Renderer/RendererPass.h @@ -112,4 +112,4 @@ class RendererPass : public Singleton, public RendererPassBase { }; -#define REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, index, dataType) LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", ToString(), index, shader->GetCB(index)->GetSize(), sizeof(dataType)); +#define REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, index, dataType) LOG(Fatal, "Shader {0} has incorrect constant buffer {1} size: {2} bytes. Expected: {3} bytes", shader->ToString(), index, shader->GetCB(index)->GetSize(), sizeof(dataType)); diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 7d1177797..466a85b06 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -569,7 +569,7 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) // Calculate lighting float3 diffuseColor = GetDiffuseColor(gBuffer); float3 diffuse = Diffuse_Lambert(diffuseColor); - output = float4(diffuse * irradiance * gBuffer.AO, 1); + output = float4(diffuse * irradiance * gBuffer.AO, saturate(length(irradiance))); } #endif