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