diff --git a/Source/Engine/Graphics/PostProcessSettings.cpp b/Source/Engine/Graphics/PostProcessSettings.cpp index 51281dba0..6205fdce7 100644 --- a/Source/Engine/Graphics/PostProcessSettings.cpp +++ b/Source/Engine/Graphics/PostProcessSettings.cpp @@ -35,6 +35,7 @@ void GlobalIlluminationSettings::BlendWith(GlobalIlluminationSettings& other, fl BLEND_FLOAT(Distance); BLEND_FLOAT(IndirectShadowsStrength); BLEND_COL(FallbackIrradiance); + BLEND_ENUM(IndirectResolution); } void BloomSettings::BlendWith(BloomSettings& other, float weight) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index f9ae1a53b..1a8ecf989 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -339,10 +339,15 @@ API_ENUM(Attributes="Flags") enum class GlobalIlluminationSettingsOverride : int /// IndirectShadowsStrength = 1 << 6, + /// + /// Overrides property. + /// + IndirectResolution = 1 << 7, + /// /// All properties. /// - All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength, + All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength | IndirectResolution, }; /// @@ -393,7 +398,7 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable /// /// Indirect lighting shadows intensity. Default is 1 for fully opaque shadowing, lower values bleed the lighting into shadowed areas. Can be sued for artistic control over GI. /// - API_FIELD(Attributes = "EditorOrder(35), Limit(0.0f, 1.0f, 0.001f), PostProcessSetting((int)GlobalIlluminationSettingsOverride.IndirectShadowsStrength)") + API_FIELD(Attributes="EditorOrder(35), Limit(0.0f, 1.0f, 0.001f), PostProcessSetting((int)GlobalIlluminationSettingsOverride.IndirectShadowsStrength)") float IndirectShadowsStrength = 1.0f; /// @@ -402,6 +407,12 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable API_FIELD(Attributes="EditorOrder(40), PostProcessSetting((int)GlobalIlluminationSettingsOverride.FallbackIrradiance)") Color FallbackIrradiance = Color::Transparent; + /// + /// The indirect lighting render resolution. Full gives better quality, but half improves performance. + /// + API_FIELD(Attributes="EditorOrder(50), PostProcessSetting((int)GlobalIlluminationSettingsOverride.IndirectResolution)") + ResolutionMode IndirectResolution = ResolutionMode::Full; + public: /// /// Blends the settings using given weight. diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index ca0558f88..335d0efa2 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -26,6 +26,7 @@ #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Level/Actors/BrushMode.h" #include "Engine/Renderer/GBufferPass.h" +#include "Engine/Renderer/Utils/MultiScaler.h" // Implementation based on: // "Dynamic Diffuse Global Illumination with Ray-Traced Irradiance Probes", Journal of Computer Graphics Tools, April 2019 @@ -292,15 +293,20 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources() auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle; if (!_psIndirectLighting[0]) { - _psIndirectLighting[0] = device->CreatePipelineState(); - _psIndirectLighting[1] = device->CreatePipelineState(); + for (auto& pso : _psIndirectLighting) + pso = device->CreatePipelineState(); psDesc.PS = shader->GetPS("PS_IndirectLighting"); + if (_psIndirectLighting[2]->Init(psDesc)) + return true; psDesc.BlendMode = BlendingMode::Add; if (_psIndirectLighting[0]->Init(psDesc)) return true; psDesc.PS = shader->GetPS("PS_IndirectLighting", 1); if (_psIndirectLighting[1]->Init(psDesc)) return true; + psDesc.BlendMode = BlendingMode::Opaque; + if (_psIndirectLighting[3]->Init(psDesc)) + return true; } return false; @@ -320,8 +326,7 @@ void DynamicDiffuseGlobalIlluminationPass::OnShaderReloading(Asset* obj) _csTraceRays[3] = nullptr; _csUpdateProbesIrradiance = nullptr; _csUpdateProbesDistance = nullptr; - SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting[0]); - SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting[1]); + SAFE_DELETE_GPU_RESOURCES(_psIndirectLighting) invalidateResources(); } @@ -335,8 +340,7 @@ void DynamicDiffuseGlobalIlluminationPass::Dispose() _cb0 = nullptr; _cb1 = nullptr; _shader = nullptr; - SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting[0]); - SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting[1]); + SAFE_DELETE_GPU_RESOURCES(_psIndirectLighting) #if GPU_ENABLE_DEVELOPMENT _debugModel = nullptr; _debugMaterial = nullptr; @@ -856,10 +860,30 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, context->BindSR(4, ddgiData.Result.ProbesData); context->BindSR(5, ddgiData.Result.ProbesDistance); context->BindSR(6, ddgiData.Result.ProbesIrradiance); - context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); - context->SetRenderTarget(lightBuffer); - context->SetState(_psIndirectLighting[Graphics::GICascadesBlending ? 1 : 0]); - context->DrawFullscreenTriangle(); + auto& settings = renderContext.List->Settings.GlobalIllumination; + if (settings.IndirectResolution == ResolutionMode::Full || !MultiScaler::Instance()->IsReady()) + { + // Full-res + context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); + context->SetRenderTarget(lightBuffer); + context->SetState(_psIndirectLighting[Graphics::GICascadesBlending ? 1 : 0]); + context->DrawFullscreenTriangle(); + } + else + { + // Upscale + auto width = RenderTools::GetResolution((int32)renderContext.View.ScreenSize.X, settings.IndirectResolution); + auto height = RenderTools::GetResolution((int32)renderContext.View.ScreenSize.Y, settings.IndirectResolution); + auto temp = RenderTargetPool::Get(GPUTextureDescription::New2D(width, height, lightBuffer->GetFormat(), GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget)); + context->SetViewportAndScissors((float)width, (float)height); + context->SetRenderTarget(temp->View()); + context->SetState(_psIndirectLighting[Graphics::GICascadesBlending ? 3 : 2]); + context->DrawFullscreenTriangle(); + context->ResetRenderTarget(); + MultiScaler::Instance()->BilateralUpscale(context, Viewport(Float2(renderContext.View.ScreenSize)), temp, lightBuffer, renderContext.Buffers->DepthBuffer, renderContext.Buffers->GBuffer1, BlendingMode::Add); + RenderTargetPool::Release(temp); + context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); + } } #if GPU_ENABLE_DEVELOPMENT diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index 4c502351e..b66aa2e3c 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -48,7 +48,7 @@ private: GPUShaderProgramCS* _csTraceRays[4]; GPUShaderProgramCS* _csUpdateProbesIrradiance; GPUShaderProgramCS* _csUpdateProbesDistance; - GPUPipelineState* _psIndirectLighting[2] = {}; + GPUPipelineState* _psIndirectLighting[4] = {}; #if GPU_ENABLE_DEVELOPMENT AssetReference _debugModel; AssetReference _debugMaterial; diff --git a/Source/Engine/Renderer/Utils/MultiScaler.cpp b/Source/Engine/Renderer/Utils/MultiScaler.cpp index 26e4e6408..ab4930f52 100644 --- a/Source/Engine/Renderer/Utils/MultiScaler.cpp +++ b/Source/Engine/Renderer/Utils/MultiScaler.cpp @@ -94,7 +94,9 @@ void MultiScaler::Dispose() RendererPass::Dispose(); // Cleanup - SAFE_DELETE_GPU_RESOURCE(_psUpscale); + for (const auto& e : _psBilateralUpscale) + e.Value->ReleaseGPU(); + _psBilateralUpscale.ClearDelete(); _psBlur5.Delete(); _psBlur9.Delete(); _psBlur13.Delete(); @@ -335,3 +337,48 @@ void MultiScaler::Upscale(GPUContext* context, const Viewport& viewport, GPUText context->ResetRenderTarget(); } + +void MultiScaler::BilateralUpscale(GPUContext* context, const Viewport& viewport, GPUTexture* src, GPUTextureView* dst, GPUTexture* depth, GPUTexture* normal, const BlendingMode& blendMode) +{ + PROFILE_GPU_CPU("Bilateral Upscale"); + + auto rtAction = GPUDrawPassAction::Store; + GPUDrawPass drawPass(context, ToSpan(&dst, 1), ToSpan(&rtAction, 1)); + context->SetViewportAndScissors(viewport); + + if (checkIfSkipPass()) + { + context->Draw(src); + } + else if (depth && normal) + { + GPUPipelineState* pso; + if (!_psBilateralUpscale.TryGet(blendMode, pso)) + { + pso = GPUDevice::Instance->CreatePipelineState(); + auto desc = GPUPipelineState::Description::DefaultFullscreenTriangle; + desc.PS = _shader->GPU->GetPS("PS_BilateralUpscale"); + desc.BlendMode = blendMode; + pso->Init(desc); + _psBilateralUpscale.Add(blendMode, pso); + } + Data data; + data.TexelSize.X = 1.0f / (float)src->Width(); + data.TexelSize.Y = 1.0f / (float)src->Height(); + auto cb = _shader->GPU->GetCB(0); + context->UpdateCB(cb, &data); + context->BindCB(0, cb); + context->BindSR(0, src); + context->BindSR(1, depth); + context->BindSR(2, normal); + context->SetState(pso); + context->DrawFullscreenTriangle(); + context->UnBindCB(0); + } + else + { + Upscale(context, viewport, src, dst); + } + + context->ResetRenderTarget(); +} diff --git a/Source/Engine/Renderer/Utils/MultiScaler.h b/Source/Engine/Renderer/Utils/MultiScaler.h index 67d8881c3..befa2b79c 100644 --- a/Source/Engine/Renderer/Utils/MultiScaler.h +++ b/Source/Engine/Renderer/Utils/MultiScaler.h @@ -3,7 +3,7 @@ #pragma once #include "../RendererPass.h" -#include "Engine/Graphics/GPUContext.h" +#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Graphics/GPUPipelineStatePermutations.h" /// @@ -18,6 +18,7 @@ private: GPUPipelineStatePermutationsPs<2> _psBlur13; GPUPipelineStatePermutationsPs<3> _psHalfDepth; GPUPipelineState* _psUpscale = nullptr; + Dictionary _psBilateralUpscale; public: /// @@ -83,7 +84,7 @@ public: void BuildHiZ(GPUContext* context, GPUTexture* srcDepth, GPUTexture* dstHiZ); /// - /// Upscales the texture. + /// Upscales the texture using Catmull-Rom filtering with 9-taps. /// /// The context. /// The viewport of the destination texture. @@ -91,6 +92,18 @@ public: /// The destination texture. void Upscale(GPUContext* context, const Viewport& viewport, GPUTexture* src, GPUTextureView* dst); + /// + /// Upscales the texture using bilateral filtering (depth+normal weighting). + /// + /// The context. + /// The viewport of the destination texture. + /// The source texture. + /// The destination texture. + /// The depth buffer (hardware depth). + /// The normal buffer (RGB encoded for GBuffer). + /// The blending mode to use when drawing the image. Can be used to composite upscaled image in additive mode (eg. into lighting/reflections buffer). + void BilateralUpscale(GPUContext* context, const Viewport& viewport, GPUTexture* src, GPUTextureView* dst, GPUTexture* depth, GPUTexture* normal, const BlendingMode& blendMode = BlendingMode::Opaque); + public: // [RendererPass] String ToString() const override; @@ -99,6 +112,9 @@ public: #if COMPILE_WITH_DEV_ENV void OnShaderReloading(Asset* obj) { + for (const auto& e : _psBilateralUpscale) + e.Value->ReleaseGPU(); + _psBilateralUpscale.ClearDelete(); _psUpscale->ReleaseGPU(); _psBlur5.Release(); _psBlur9.Release(); diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 9f80ee4aa..f44f988b9 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -969,20 +969,15 @@ Texture2D ProbesIrradiance : register(t6); META_PS(true, FEATURE_LEVEL_SM5) META_PERMUTATION_1(DDGI_CASCADE_BLEND_SMOOTH=0) META_PERMUTATION_1(DDGI_CASCADE_BLEND_SMOOTH=1) -void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) +float4 PS_IndirectLighting(Quad_VS2PS input) : SV_Target0 { - output = 0; - // Sample GBuffer GBufferSample gBuffer = SampleGBuffer(GBuffer, input.TexCoord); // Check if cannot shadow pixel BRANCH if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT) - { - discard; - return; - } + return float4(0, 0, 0, 0); // Sample irradiance float dither = RandN2(input.TexCoord + TemporalTime).x; @@ -992,7 +987,7 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) // Calculate lighting float3 diffuseColor = GetDiffuseColor(gBuffer); float3 diffuse = Diffuse_Lambert(diffuseColor); - output.rgb = diffuse * irradiance * gBuffer.AO; + return float4(diffuse * irradiance * gBuffer.AO, 1); } #endif diff --git a/Source/Shaders/MultiScaler.shader b/Source/Shaders/MultiScaler.shader index bf0c231c7..501e1893d 100644 --- a/Source/Shaders/MultiScaler.shader +++ b/Source/Shaders/MultiScaler.shader @@ -212,3 +212,61 @@ float4 PS_Upscale(Quad_VS2PS input) : SV_Target0 // Catmull-Rom filtering with 9-taps return SampleTextureCatmullRom(Input, SamplerLinearClamp, input.TexCoord, TexelSize); } + +#ifdef _PS_BilateralUpscale + +#include "./Flax/GBufferCommon.hlsl" + +Texture2D Depths : register(t1); +Texture2D Normals : register(t2); + +// Pixel Shader for upscaling image with bilateral filtering (depth and normal weighting) +META_PS(true, FEATURE_LEVEL_ES2) +float4 PS_BilateralUpscale(Quad_VS2PS input) : SV_Target0 +{ + const float epsilon = 0.0001f; + + float2 inputSize = floor(1.0f / TexelSize); + float2 baseUV = floor(input.TexCoord * inputSize - 0.5f) / inputSize + 0.5f * TexelSize; + float2 bilinear = (input.TexCoord - baseUV) * inputSize; + float4 weights = float4((1 - bilinear.y) * (1 - bilinear.x), (1 - bilinear.y) * bilinear.x, bilinear.y * (1 - bilinear.x), bilinear.y * bilinear.x); + + float4 values00 = SAMPLE_RT_LINEAR(Input, baseUV); + float4 values10 = SAMPLE_RT_LINEAR(Input, baseUV + float2(TexelSize.x, 0)); + float4 values01 = SAMPLE_RT_LINEAR(Input, baseUV + float2(0, TexelSize.y)); + float4 values11 = SAMPLE_RT_LINEAR(Input, baseUV + TexelSize); + + float depth00 = SAMPLE_RT_DEPTH(Depths, baseUV); + float depth10 = SAMPLE_RT_DEPTH(Depths, baseUV + float2(TexelSize.x, 0)); + float depth01 = SAMPLE_RT_DEPTH(Depths, baseUV + float2(0, TexelSize.y)); + float depth11 = SAMPLE_RT_DEPTH(Depths, baseUV + TexelSize); + + float minDepth = min(min(min(depth00, depth10), depth01), depth11); + float maxDepth = max(max(max(depth00, depth10), depth01), depth11); + if (maxDepth / minDepth > 1.021f) + { + float depth = SAMPLE_RT_DEPTH(Depths, input.TexCoord); + weights *= 1.0f / (abs(float4(depth00, depth10, depth01, depth11) - depth.xxxx) + epsilon); + } + + float3 normal00 = DecodeNormal(SAMPLE_RT_LINEAR(Normals, baseUV).rgb); + float3 normal10 = DecodeNormal(SAMPLE_RT_LINEAR(Normals, baseUV + float2(TexelSize.x, 0)).rgb); + float3 normal01 = DecodeNormal(SAMPLE_RT_LINEAR(Normals, baseUV + float2(0, TexelSize.y)).rgb); + float3 normal11 = DecodeNormal(SAMPLE_RT_LINEAR(Normals, baseUV + TexelSize).rgb); + + float3 normal = DecodeNormal(SAMPLE_RT(Normals, input.TexCoord).rgb); + float normalPower = 2; + weights.x *= min(pow(saturate(dot(normal, normal00)), normalPower), 1.0); + weights.y *= min(pow(saturate(dot(normal, normal10)), normalPower), 1.0); + weights.z *= min(pow(saturate(dot(normal, normal01)), normalPower), 1.0); + weights.w *= min(pow(saturate(dot(normal, normal11)), normalPower), 1.0); + + values00 *= weights.x; + values10 *= weights.y; + values01 *= weights.z; + values11 *= weights.w; + + return (values00 + values10 + values01 + values11) / (dot(weights, 1) + epsilon); +} + +#endif