Add option to render DDGI indirect lighting in half-res with bilateral upscaling
Cuts irradiance resolving time in half at cost of some aliasing. Ideal for low-quality settings or when rendering at high native res.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -339,10 +339,15 @@ API_ENUM(Attributes="Flags") enum class GlobalIlluminationSettingsOverride : int
|
||||
/// </summary>
|
||||
IndirectShadowsStrength = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Overrides <see cref="GlobalIlluminationSettings.IndirectResolution"/> property.
|
||||
/// </summary>
|
||||
IndirectResolution = 1 << 7,
|
||||
|
||||
/// <summary>
|
||||
/// All properties.
|
||||
/// </summary>
|
||||
All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength,
|
||||
All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength | IndirectResolution,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -393,7 +398,7 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
@@ -402,6 +407,12 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable
|
||||
API_FIELD(Attributes="EditorOrder(40), PostProcessSetting((int)GlobalIlluminationSettingsOverride.FallbackIrradiance)")
|
||||
Color FallbackIrradiance = Color::Transparent;
|
||||
|
||||
/// <summary>
|
||||
/// The indirect lighting render resolution. Full gives better quality, but half improves performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(50), PostProcessSetting((int)GlobalIlluminationSettingsOverride.IndirectResolution)")
|
||||
ResolutionMode IndirectResolution = ResolutionMode::Full;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Blends the settings using given weight.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -48,7 +48,7 @@ private:
|
||||
GPUShaderProgramCS* _csTraceRays[4];
|
||||
GPUShaderProgramCS* _csUpdateProbesIrradiance;
|
||||
GPUShaderProgramCS* _csUpdateProbesDistance;
|
||||
GPUPipelineState* _psIndirectLighting[2] = {};
|
||||
GPUPipelineState* _psIndirectLighting[4] = {};
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
AssetReference<Model> _debugModel;
|
||||
AssetReference<MaterialBase> _debugMaterial;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
/// <summary>
|
||||
@@ -18,6 +18,7 @@ private:
|
||||
GPUPipelineStatePermutationsPs<2> _psBlur13;
|
||||
GPUPipelineStatePermutationsPs<3> _psHalfDepth;
|
||||
GPUPipelineState* _psUpscale = nullptr;
|
||||
Dictionary<BlendingMode, GPUPipelineState*> _psBilateralUpscale;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -83,7 +84,7 @@ public:
|
||||
void BuildHiZ(GPUContext* context, GPUTexture* srcDepth, GPUTexture* dstHiZ);
|
||||
|
||||
/// <summary>
|
||||
/// Upscales the texture.
|
||||
/// Upscales the texture using Catmull-Rom filtering with 9-taps.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="viewport">The viewport of the destination texture.</param>
|
||||
@@ -91,6 +92,18 @@ public:
|
||||
/// <param name="dst">The destination texture.</param>
|
||||
void Upscale(GPUContext* context, const Viewport& viewport, GPUTexture* src, GPUTextureView* dst);
|
||||
|
||||
/// <summary>
|
||||
/// Upscales the texture using bilateral filtering (depth+normal weighting).
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="viewport">The viewport of the destination texture.</param>
|
||||
/// <param name="src">The source texture.</param>
|
||||
/// <param name="dst">The destination texture.</param>
|
||||
/// <param name="depth">The depth buffer (hardware depth).</param>
|
||||
/// <param name="normal">The normal buffer (RGB encoded for GBuffer).</param>
|
||||
/// <param name="blendMode">The blending mode to use when drawing the image. Can be used to composite upscaled image in additive mode (eg. into lighting/reflections buffer).</param>
|
||||
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();
|
||||
|
||||
@@ -969,20 +969,15 @@ Texture2D<float4> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user