Add new mode to Screen Space Reflections for DDGI Scene tracing
This commit is contained in:
BIN
Content/Shaders/SSR.flax
(Stored with Git LFS)
BIN
Content/Shaders/SSR.flax
(Stored with Git LFS)
Binary file not shown.
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Current materials shader version.
|
||||
/// </summary>
|
||||
#define MATERIAL_GRAPH_VERSION 153
|
||||
#define MATERIAL_GRAPH_VERSION 154
|
||||
|
||||
class Material;
|
||||
class GPUShader;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user