Add new mode to Screen Space Reflections for DDGI Scene tracing

This commit is contained in:
Wojciech Figat
2022-07-21 09:41:38 +02:00
parent f90808749e
commit 2a53143bc4
7 changed files with 192 additions and 117 deletions

BIN
Content/Shaders/SSR.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -10,7 +10,7 @@
/// <summary>
/// Current materials shader version.
/// </summary>
#define MATERIAL_GRAPH_VERSION 153
#define MATERIAL_GRAPH_VERSION 154
class Material;
class GPUShader;

View File

@@ -155,6 +155,22 @@ API_ENUM() enum class ResolutionMode : int32
Half = 2,
};
/// <summary>
/// The screen space reflections modes.
/// </summary>
API_ENUM() enum class ReflectionsTraceMode : int32
{
/// <summary>
/// Screen-space depth buffer tracing with scene color sampling. Only visible on-screen pixels can be used in reflections.
/// </summary>
ScreenTracing = 0,
/// <summary>
/// Software raytracing using Global SDF and Global Surface Atlas that supports full-scene raytracing (off-screen).
/// </summary>
SoftwareTracing = 1,
};
/// <summary>
/// The <see cref="AmbientOcclusionSettings"/> structure members override flags.
/// </summary>
@@ -1675,10 +1691,15 @@ API_ENUM(Attributes="Flags") enum class ScreenSpaceReflectionsSettingsOverride :
/// </summary>
FadeDistance = 1 << 14,
/// <summary>
/// Overrides <see cref="ScreenSpaceReflectionsSettings.TraceMode"/> property.
/// </summary>
TraceMode = 1 << 15,
/// <summary>
/// All properties.
/// </summary>
All = Intensity | DepthResolution | RayTracePassResolution | BRDFBias | RoughnessThreshold | WorldAntiSelfOcclusionBias | ResolvePassResolution | ResolveSamples | EdgeFadeFactor | UseColorBufferMips | TemporalEffect | TemporalScale | TemporalResponse | FadeOutDistance | FadeDistance,
All = Intensity | DepthResolution | RayTracePassResolution | BRDFBias | RoughnessThreshold | WorldAntiSelfOcclusionBias | ResolvePassResolution | ResolveSamples | EdgeFadeFactor | UseColorBufferMips | TemporalEffect | TemporalScale | TemporalResponse | FadeOutDistance | FadeDistance | TraceMode,
};
/// <summary>
@@ -1702,88 +1723,94 @@ API_STRUCT() struct FLAXENGINE_API ScreenSpaceReflectionsSettings : ISerializabl
API_FIELD(Attributes="Limit(0, 5.0f, 0.01f), EditorOrder(0), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.Intensity)")
float Intensity = 1.0f;
/// <summary>
/// The reflections tracing mode.
/// </summary>
API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TraceMode)")
ReflectionsTraceMode TraceMode = ReflectionsTraceMode::ScreenTracing;
/// <summary>
/// The depth buffer downscale option to optimize raycast performance. Full gives better quality, but half improves performance. The default value is half.
/// </summary>
API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.DepthResolution)")
API_FIELD(Attributes="EditorOrder(2), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.DepthResolution)")
ResolutionMode DepthResolution = ResolutionMode::Half;
/// <summary>
/// The raycast resolution. Full gives better quality, but half improves performance. The default value is half.
/// </summary>
API_FIELD(Attributes="EditorOrder(2), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RayTracePassResolution)")
API_FIELD(Attributes="EditorOrder(3), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RayTracePassResolution)")
ResolutionMode RayTracePassResolution = ResolutionMode::Half;
/// <summary>
/// The reflection spread parameter. This value controls source roughness effect on reflections blur. Smaller values produce wider reflections spread but also introduce more noise. Higher values provide more mirror-like reflections. Default value is 0.82.
/// </summary>
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(3), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.BRDFBias), EditorDisplay(null, \"BRDF Bias\")")
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(10), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.BRDFBias), EditorDisplay(null, \"BRDF Bias\")")
float BRDFBias = 0.82f;
/// <summary>
/// The maximum amount of roughness a material must have to reflect the scene. For example, if this value is set to 0.4, only materials with a roughness value of 0.4 or below reflect the scene. The default value is 0.45.
/// </summary>
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(4), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RoughnessThreshold)")
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(15), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RoughnessThreshold)")
float RoughnessThreshold = 0.45f;
/// <summary>
/// The offset of the raycast origin. Lower values produce more correct reflection placement, but produce more artifacts. We recommend values of 0.3 or lower. The default value is 0.1.
/// </summary>
API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(5), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.WorldAntiSelfOcclusionBias)")
API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(20), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.WorldAntiSelfOcclusionBias)")
float WorldAntiSelfOcclusionBias = 0.1f;
/// <summary>
/// The raycast resolution. Full gives better quality, but half improves performance. The default value is half.
/// </summary>
API_FIELD(Attributes="EditorOrder(6), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolvePassResolution)")
API_FIELD(Attributes="EditorOrder(25), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolvePassResolution)")
ResolutionMode ResolvePassResolution = ResolutionMode::Full;
/// <summary>
/// The number of rays used to resolve the reflection color. Higher values provide better quality but reduce effect performance. Default value is 4. Use 1 for the highest speed.
/// </summary>
API_FIELD(Attributes="Limit(1, 8), EditorOrder(7), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolveSamples)")
API_FIELD(Attributes="Limit(1, 8), EditorOrder(26), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolveSamples)")
int32 ResolveSamples = 4;
/// <summary>
/// The point at which the far edges of the reflection begin to fade. Has no effect on performance. The default value is 0.1.
/// </summary>
API_FIELD(Attributes="Limit(0, 1.0f, 0.02f), EditorOrder(8), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.EdgeFadeFactor)")
API_FIELD(Attributes="Limit(0, 1.0f, 0.02f), EditorOrder(30), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.EdgeFadeFactor)")
float EdgeFadeFactor = 0.1f;
/// <summary>
/// The effect fade out end distance from camera (in world units).
/// </summary>
API_FIELD(Attributes="Limit(0), EditorOrder(9), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.FadeOutDistance)")
API_FIELD(Attributes="Limit(0), EditorOrder(31), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.FadeOutDistance)")
float FadeOutDistance = 5000.0f;
/// <summary>
/// The effect fade distance (in world units). Defines the size of the effect fade from fully visible to fully invisible at FadeOutDistance.
/// </summary>
API_FIELD(Attributes="Limit(0), EditorOrder(10), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.FadeDistance)")
API_FIELD(Attributes="Limit(0), EditorOrder(32), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.FadeDistance)")
float FadeDistance = 500.0f;
/// <summary>
/// "The input color buffer downscale mode that uses blurred mipmaps when resolving the reflection color. Produces more realistic results by blurring distant parts of reflections in rough (low-gloss) materials. It also improves performance on most platforms but uses more memory.
/// </summary>
API_FIELD(Attributes="EditorOrder(11), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.UseColorBufferMips), EditorDisplay(null, \"Use Color Buffer Mips\")")
API_FIELD(Attributes="EditorOrder(40), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.UseColorBufferMips), EditorDisplay(null, \"Use Color Buffer Mips\")")
bool UseColorBufferMips = true;
/// <summary>
/// If checked, enables the temporal pass. Reduces noise, but produces an animated "jittering" effect that's sometimes noticeable. If disabled, the properties below have no effect.
/// </summary>
API_FIELD(Attributes="EditorOrder(12), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalEffect), EditorDisplay(null, \"Enable Temporal Effect\")")
API_FIELD(Attributes="EditorOrder(50), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalEffect), EditorDisplay(null, \"Enable Temporal Effect\")")
bool TemporalEffect = true;
/// <summary>
/// The intensity of the temporal effect. Lower values produce reflections faster, but more noise. The default value is 8.
/// </summary>
API_FIELD(Attributes="Limit(0, 20.0f, 0.5f), EditorOrder(13), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalScale)")
API_FIELD(Attributes="Limit(0, 20.0f, 0.5f), EditorOrder(55), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalScale)")
float TemporalScale = 8.0f;
/// <summary>
/// Defines how quickly reflections blend between the reflection in the current frame and the history buffer. Lower values produce reflections faster, but with more jittering. If the camera in your game doesn't move much, we recommend values closer to 1. The default value is 0.8.
/// </summary>
API_FIELD(Attributes="Limit(0.05f, 1.0f, 0.01f), EditorOrder(14), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalResponse)")
API_FIELD(Attributes="Limit(0.05f, 1.0f, 0.01f), EditorOrder(60), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalResponse)")
float TemporalResponse = 0.8f;
public:

View File

@@ -4,6 +4,8 @@
#include "ReflectionsPass.h"
#include "GBufferPass.h"
#include "RenderList.h"
#include "GlobalSignDistanceFieldPass.h"
#include "GI/GlobalSurfaceAtlasPass.h"
#include "Engine/Content/Content.h"
#include "Engine/Graphics/Graphics.h"
#include "Engine/Graphics/RenderTools.h"
@@ -15,7 +17,6 @@
#include "Engine/Engine/Engine.h"
#include "Engine/Graphics/RenderTask.h"
#define REFLECTIONS_FORMAT PixelFormat::R11G11B10_Float
#define RESOLVE_PASS_OUTPUT_FORMAT PixelFormat::R16G16B16A16_Float
// Shader input texture slots mapping
@@ -48,15 +49,10 @@ PACK_STRUCT(struct Data
Matrix ViewMatrix;
Matrix ViewProjectionMatrix;
});
ScreenSpaceReflectionsPass::ScreenSpaceReflectionsPass()
: _psRayTracePass(nullptr)
, _psCombinePass(nullptr)
, _psTemporalPass(nullptr)
, _psMixPass(nullptr)
{
}
GlobalSignDistanceFieldPass::ConstantsData GlobalSDF;
GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas;
});
bool ScreenSpaceReflectionsPass::NeedMotionVectors(RenderContext& renderContext)
{
@@ -72,9 +68,9 @@ String ScreenSpaceReflectionsPass::ToString() const
bool ScreenSpaceReflectionsPass::Init()
{
// Create pipeline states
_psRayTracePass = GPUDevice::Instance->CreatePipelineState();
_psCombinePass = GPUDevice::Instance->CreatePipelineState();
_psRayTracePass.CreatePipelineStates();
_psResolvePass.CreatePipelineStates();
_psCombinePass = GPUDevice::Instance->CreatePipelineState();
_psTemporalPass = GPUDevice::Instance->CreatePipelineState();
_psMixPass = GPUDevice::Instance->CreatePipelineState();
@@ -112,10 +108,9 @@ bool ScreenSpaceReflectionsPass::setupResources()
// Create pipeline stages
GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
if (!_psRayTracePass->IsValid())
if (!_psRayTracePass.IsValid())
{
psDesc.PS = shader->GetPS("PS_RayTracePass");
if (_psRayTracePass->Init(psDesc))
if (_psRayTracePass.Create(psDesc, shader, "PS_RayTracePass"))
return true;
}
if (!_psCombinePass->IsValid())
@@ -152,10 +147,10 @@ void ScreenSpaceReflectionsPass::Dispose()
RendererPass::Dispose();
// Cleanup
SAFE_DELETE_GPU_RESOURCE(_psRayTracePass);
SAFE_DELETE_GPU_RESOURCE(_psCombinePass);
SAFE_DELETE_GPU_RESOURCE(_psTemporalPass);
SAFE_DELETE_GPU_RESOURCE(_psMixPass);
_psRayTracePass.Delete();
_psResolvePass.Delete();
_shader = nullptr;
_preIntegratedGF = nullptr;
@@ -201,7 +196,7 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
auto colorBuffer0 = RenderTargetPool::Get(tempDesc);
// TODO: maybe allocate colorBuffer1 smaller because mip0 is not used (the same as PostProcessingPass for Bloom), keep in sync to use the same buffer in frame
auto colorBuffer1 = RenderTargetPool::Get(tempDesc);
tempDesc = GPUTextureDescription::New2D(traceWidth, traceHeight, REFLECTIONS_FORMAT);
tempDesc = GPUTextureDescription::New2D(traceWidth, traceHeight, PixelFormat::R16G16B16A16_Float);
auto traceBuffer = RenderTargetPool::Get(tempDesc);
tempDesc = GPUTextureDescription::New2D(resolveWidth, resolveHeight, RESOLVE_PASS_OUTPUT_FORMAT);
auto resolveBuffer = RenderTargetPool::Get(tempDesc);
@@ -259,16 +254,11 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
data.TemporalTime = static_cast<float>(time - integral);
buffers->LastFrameTemporalSSR = Engine::FrameCount;
if (buffers->TemporalSSR == nullptr)
{
// Missing temporal buffer
tempDesc = GPUTextureDescription::New2D(temporalWidth, temporalHeight, RESOLVE_PASS_OUTPUT_FORMAT);
buffers->TemporalSSR = RenderTargetPool::Get(tempDesc);
}
else if (buffers->TemporalSSR->Width() != temporalWidth || buffers->TemporalSSR->Height() != temporalHeight)
if (!buffers->TemporalSSR || buffers->TemporalSSR->Width() != temporalWidth || buffers->TemporalSSR->Height() != temporalHeight)
{
// Wrong size temporal buffer
RenderTargetPool::Release(buffers->TemporalSSR);
if (buffers->TemporalSSR)
RenderTargetPool::Release(buffers->TemporalSSR);
tempDesc = GPUTextureDescription::New2D(temporalWidth, temporalHeight, RESOLVE_PASS_OUTPUT_FORMAT);
buffers->TemporalSSR = RenderTargetPool::Get(tempDesc);
}
@@ -280,6 +270,23 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
Matrix::Transpose(view.View, data.ViewMatrix);
Matrix::Transpose(view.ViewProjection(), data.ViewProjectionMatrix);
// Try to use Global Surface Atlas (with rendered GI) to perform full-scene tracing (not only screen-space)
bool useGlobalSurfaceAtlas = false;
GlobalSignDistanceFieldPass::BindingData bindingDataSDF;
GlobalSurfaceAtlasPass::BindingData bindingDataSurfaceAtlas;
if (settings.TraceMode == ReflectionsTraceMode::SoftwareTracing &&
renderContext.View.Flags & ViewFlags::GI &&
renderContext.List->Settings.GlobalIllumination.Mode == GlobalIlluminationMode::DDGI)
{
if (!GlobalSignDistanceFieldPass::Instance()->Render(renderContext, context, bindingDataSDF) &&
!GlobalSurfaceAtlasPass::Instance()->Render(renderContext, context, bindingDataSurfaceAtlas))
{
useGlobalSurfaceAtlas = true;
data.GlobalSDF = bindingDataSDF.Constants;
data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants;
}
}
// Check if resize depth
GPUTexture* originalDepthBuffer = buffers->DepthBuffer;
GPUTexture* smallerDepthBuffer = originalDepthBuffer;
@@ -350,14 +357,24 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
// Ray Trace Pass
context->SetViewportAndScissors((float)traceWidth, (float)traceHeight);
context->SetRenderTarget(*traceBuffer);
context->SetState(_psRayTracePass);
context->BindSR(TEXTURE0, blurPassBuffer->View());
if (useGlobalSurfaceAtlas)
{
context->BindSR(7, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr);
context->BindSR(8, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr);
context->BindSR(9, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr);
context->BindSR(10, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr);
context->BindSR(11, bindingDataSurfaceAtlas.Objects ? bindingDataSurfaceAtlas.Objects->View() : nullptr);
context->BindSR(12, bindingDataSurfaceAtlas.AtlasDepth->View());
context->BindSR(13, bindingDataSurfaceAtlas.AtlasLighting->View());
}
context->SetState(_psRayTracePass.Get(useGlobalSurfaceAtlas ? 1 : 0));
context->DrawFullscreenTriangle();
context->ResetRenderTarget();
// Resolve Pass
context->SetRenderTarget(resolveBuffer->View());
context->BindSR(TEXTURE0, blurPassBuffer->View());
context->BindSR(TEXTURE1, traceBuffer->View());
context->BindSR(TEXTURE0, traceBuffer->View());
context->SetState(_psResolvePass.Get(resolvePassIndex));
context->DrawFullscreenTriangle();
context->ResetRenderTarget();
@@ -405,3 +422,17 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
RenderTargetPool::Release(traceBuffer);
RenderTargetPool::Release(resolveBuffer);
}
#if COMPILE_WITH_DEV_ENV
void ScreenSpaceReflectionsPass::OnShaderReloading(Asset* obj)
{
_psCombinePass->ReleaseGPU();
_psTemporalPass->ReleaseGPU();
_psMixPass->ReleaseGPU();
_psRayTracePass.Release();
_psResolvePass.Release();
invalidateResources();
}
#endif

View File

@@ -26,25 +26,16 @@
class ScreenSpaceReflectionsPass : public RendererPass<ScreenSpaceReflectionsPass>
{
private:
GPUPipelineStatePermutationsPs<2> _psRayTracePass;
GPUPipelineStatePermutationsPs<4> _psResolvePass;
GPUPipelineState* _psCombinePass = nullptr;
GPUPipelineState* _psTemporalPass = nullptr;
GPUPipelineState* _psMixPass = nullptr;
AssetReference<Shader> _shader;
GPUPipelineState* _psRayTracePass;
GPUPipelineState* _psCombinePass;
GPUPipelineStatePermutationsPs<4> _psResolvePass;
GPUPipelineState* _psTemporalPass;
GPUPipelineState* _psMixPass;
AssetReference<Texture> _preIntegratedGF;
public:
/// <summary>
/// Init
/// </summary>
ScreenSpaceReflectionsPass();
public:
/// <summary>
/// Determinates whenever this pass requires motion vectors rendering.
/// </summary>
@@ -61,28 +52,17 @@ public:
void Render(RenderContext& renderContext, GPUTextureView* reflectionsRT, GPUTextureView* lightBuffer);
private:
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj)
{
_psRayTracePass->ReleaseGPU();
_psCombinePass->ReleaseGPU();
_psResolvePass.Release();
_psTemporalPass->ReleaseGPU();
_psMixPass->ReleaseGPU();
invalidateResources();
}
void OnShaderReloading(Asset* obj);
#endif
public:
// [RendererPass]
String ToString() const override;
bool Init() override;
void Dispose() override;
protected:
// [RendererPass]
bool setupResources() override;
};

View File

@@ -42,7 +42,21 @@ float RayAttenBorder(float2 pos, float value)
// Screen Space Reflection ray tracing utility.
// Returns: xy: hitUV, z: hitMask, where hitUV is the result UV of hit pixel, hitMask is the normalized sample weight (0 if no hit).
float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D depthBuffer, float3 viewPos, float4x4 viewMatrix, float4x4 viewProjectionMatrix, float stepSize, float maxSamples = 20, bool temporal = false, float temporalTime = 0.0f, float worldAntiSelfOcclusionBias = 0.1f, float brdfBias = 0.82f, float drawDistance = 5000.0f, float roughnessThreshold = 0.4f, float edgeFade = 0.1f)
float3 ScreenSpaceReflectionDirection(float2 uv, GBufferSample gBuffer, float3 viewPos, bool temporal = false, float temporalTime = 0.0f, float brdfBias = 0.82f)
{
// Randomize it a little
float2 jitter = RandN2(uv + temporalTime);
float2 Xi = jitter;
Xi.y = lerp(Xi.y, 0.0, brdfBias);
float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal;
float3 viewWS = normalize(gBuffer.WorldPos - viewPos);
return reflect(viewWS, H.xyz);
}
// Screen Space Reflection ray tracing utility.
// Returns: xy: hitUV, z: hitMask, where hitUV is the result UV of hit pixel, hitMask is the normalized sample weight (0 if no hit).
float3 TraceScreenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D depthBuffer, float3 viewPos, float4x4 viewMatrix, float4x4 viewProjectionMatrix, float stepSize, float maxSamples = 20, bool temporal = false, float temporalTime = 0.0f, float worldAntiSelfOcclusionBias = 0.1f, float brdfBias = 0.82f, float drawDistance = 5000.0f, float roughnessThreshold = 0.4f, float edgeFade = 0.1f)
{
// Reject invalid pixels
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > roughnessThreshold || gBuffer.ViewPos.z > drawDistance)
@@ -50,21 +64,11 @@ float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D dep
// Calculate view space normal vector
float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix);
// Randomize it a little
float2 jitter = RandN2(uv + temporalTime);
float2 Xi = jitter;
Xi.y = lerp(Xi.y, 0.0, brdfBias);
float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal;
// Calculate normalized view space reflection vector
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4)
return 0;
float3 viewWS = normalize(gBuffer.WorldPos - viewPos);
float3 reflectWS = reflect(viewWS, H.xyz);
float3 reflectWS = ScreenSpaceReflectionDirection(uv, gBuffer, viewPos, temporal, temporalTime, brdfBias);
float3 startWS = gBuffer.WorldPos + gBuffer.Normal * worldAntiSelfOcclusionBias;
float3 startUV = ProjectWorldToUv(startWS, viewProjectionMatrix);
float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix);

View File

@@ -5,6 +5,8 @@
#include "./Flax/ReflectionsCommon.hlsl"
#include "./Flax/SSR.hlsl"
#include "./Flax/GBuffer.hlsl"
#include "./Flax/GlobalSignDistanceField.hlsl"
#include "./Flax/GI/GlobalSurfaceAtlas.hlsl"
// Enable/disable luminance filter to reduce reflections highlights
#define SSR_REDUCE_HIGHLIGHTS 1
@@ -38,6 +40,9 @@ float FadeOutDistance;
float4x4 ViewMatrix;
float4x4 ViewProjectionMatrix;
GlobalSDFData GlobalSDF;
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
META_CB_END
DECLARE_GBUFFERDATA_ACCESS(GBuffer)
@@ -45,6 +50,15 @@ DECLARE_GBUFFERDATA_ACCESS(GBuffer)
Texture2D Texture0 : register(t4);
Texture2D Texture1 : register(t5);
Texture2D Texture2 : register(t6);
#if USE_GLOBAL_SURFACE_ATLAS
Texture3D<float> GlobalSDFTex : register(t7);
Texture3D<float> GlobalSDFMip : register(t8);
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t9);
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t10);
Buffer<float4> GlobalSurfaceAtlasObjects : register(t11);
Texture2D GlobalSurfaceAtlasDepth : register(t12);
Texture2D GlobalSurfaceAtlasTex : register(t13);
#endif
// Pixel Shader for screen space reflections rendering - combine pass
META_PS(true, FEATURE_LEVEL_ES2)
@@ -83,17 +97,59 @@ float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
// Pixel Shader for screen space reflections rendering - ray trace pass
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(USE_GLOBAL_SURFACE_ATLAS=0)
META_PERMUTATION_1(USE_GLOBAL_SURFACE_ATLAS=1)
float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
{
// Inputs:
// Texture0 - color buffer (rgb color, with mip maps chain or without)
// SRV 7-8 Global SDF
// SRV 9-13 Global Surface Atlas
// Sample GBuffer
GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, input.TexCoord);
// Trace depth buffer to find intersection
float3 hit = TraceSceenSpaceReflection(input.TexCoord, gBuffer, Depth, gBufferData.ViewPos, ViewMatrix, ViewProjectionMatrix, RayTraceStep, MaxTraceSamples, TemporalEffect, TemporalTime, WorldAntiSelfOcclusionBias, BRDFBias, FadeOutDistance, RoughnessFade, EdgeFadeFactor);
// Reject invalid pixels
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > RoughnessFade || gBuffer.ViewPos.z > FadeOutDistance)
return 0;
// Output: xy: hitUV, z: hitMask, w: unused
return float4(hit.xy, hit.z * Intensity, 0);
// Trace depth buffer to find intersection
float3 screenHit = TraceScreenSpaceReflection(input.TexCoord, gBuffer, Depth, gBufferData.ViewPos, ViewMatrix, ViewProjectionMatrix, RayTraceStep, MaxTraceSamples, TemporalEffect, TemporalTime, WorldAntiSelfOcclusionBias, BRDFBias, FadeOutDistance, RoughnessFade, EdgeFadeFactor);
float4 result = 0;
if (screenHit.z > 0)
{
float3 viewVector = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
float NdotV = saturate(dot(gBuffer.Normal, viewVector));
float coneTangent = lerp(0.0, gBuffer.Roughness * 5 * (1.0 - BRDFBias), pow(NdotV, 1.5) * sqrt(gBuffer.Roughness));
float intersectionCircleRadius = coneTangent * length(screenHit.xy - input.TexCoord);
float mip = clamp(log2(intersectionCircleRadius * TraceSizeMax), 0.0, MaxColorMiplevel);
float3 sampleColor = Texture0.SampleLevel(SamplerLinearClamp, screenHit.xy, mip).rgb;
result = float4(sampleColor, screenHit.z);
if (screenHit.z >= 0.9f)
return result;
}
// Calculate reflection direction (the same TraceScreenSpaceReflection)
float3 reflectWS = ScreenSpaceReflectionDirection(input.TexCoord, gBuffer, gBufferData.ViewPos, TemporalEffect, TemporalTime, BRDFBias);
// Fallback to Global SDF and Global Surface Atlas tracing
#if USE_GLOBAL_SURFACE_ATLAS
GlobalSDFTrace sdfTrace;
float maxDistance = 100000;
float selfOcclusionBias = GlobalSDF.CascadeVoxelSize[0];
sdfTrace.Init(gBuffer.WorldPos + gBuffer.Normal * selfOcclusionBias, reflectWS, 0.0f, maxDistance);
GlobalSDFHit sdfHit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, sdfTrace);
if (sdfHit.IsHit())
{
float3 hitPosition = sdfHit.GetHitPosition(sdfTrace);
float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, sdfHit);
float4 surfaceAtlas = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, RWGlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -reflectWS, surfaceThreshold);
result = lerp(surfaceAtlas, float4(result.rgb, 1), result.a);
}
#endif
return result;
}
#ifndef RESOLVE_SAMPLES
@@ -123,28 +179,19 @@ float4 PS_ResolvePass(Quad_VS2PS input) : SV_Target0
float2 uv = input.TexCoord;
// Inputs:
// Texture0 - color buffer (rgb color, with mip maps chain or without)
// Texture1 - ray trace buffer (xy: hitUV, z: hitMask)
// Early out for pixels with no hit result
if (Texture1.SampleLevel(SamplerLinearClamp, uv, 0).z <= 0.001)
return 0;
// Texture0 - ray trace buffer (xy: HDR color, z: weight)
// Sample GBuffer
GBufferData gBufferData = GetGBufferData();
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
// Calculate view vector
float3 viewVector = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT)
return 0;
// Randomize it a little
float2 random = RandN2(uv + TemporalTime);
float2 blueNoise = random.xy * 2.0 - 1.0;
float2x2 offsetRotationMatrix = float2x2(blueNoise.x, blueNoise.y, -blueNoise.y, blueNoise.x);
float NdotV = saturate(dot(gBuffer.Normal, viewVector));
float coneTangent = lerp(0.0, gBuffer.Roughness * 5 * (1.0 - BRDFBias), pow(NdotV, 1.5) * sqrt(gBuffer.Roughness));
// Resolve samples
float4 result = 0.0;
UNROLL
@@ -152,30 +199,16 @@ float4 PS_ResolvePass(Quad_VS2PS input) : SV_Target0
{
float2 offsetUV = Offsets[i] * SSRtexelSize;
offsetUV = mul(offsetRotationMatrix, offsetUV);
// "uv" is the location of the current (or "local") pixel. We want to resolve the local pixel using
// intersections spawned from neighbouring pixels. The neighbouring pixel is this one:
float2 neighborUv = uv + offsetUV;
// Now we fetch the intersection point
float4 hitPacked = Texture1.SampleLevel(SamplerLinearClamp, neighborUv, 0);
float2 hitUv = hitPacked.xy;
float hitMask = hitPacked.z;
float intersectionCircleRadius = coneTangent * length(hitUv - uv);
float mip = clamp(log2(intersectionCircleRadius * TraceSizeMax), 0.0, MaxColorMiplevel);
float3 sampleColor = Texture0.SampleLevel(SamplerLinearClamp, hitUv, mip).rgb;
float4 sample = Texture0.SampleLevel(SamplerLinearClamp, uv + offsetUV, 0);
#if SSR_REDUCE_HIGHLIGHTS
sampleColor /= 1 + Luminance(sampleColor);
sample.rgb /= 1 + Luminance(sample.rgb);
#endif
result.rgb += sampleColor;
result.a += hitMask;
result += sample;
}
// Calculate final result value
result /= RESOLVE_SAMPLES;
result.a *= Intensity;
#if SSR_REDUCE_HIGHLIGHTS
result.rgb /= 1 - Luminance(result.rgb);
#endif