Merge branch 'forward-software-refl' of https://github.com/HydrogenC/FlaxEngine into HydrogenC-forward-software-refl

This commit is contained in:
Wojtek Figat
2024-08-05 19:04:28 +02:00
10 changed files with 183 additions and 11 deletions

View File

@@ -67,6 +67,7 @@ void PS_Forward(
gBuffer.Color = material.Color;
gBuffer.Specular = material.Specular;
gBuffer.AO = material.AO;
// gBuffer.ViewPos is the view space position of the pixel
gBuffer.ViewPos = mul(float4(materialInput.WorldPosition, 1), ViewMatrix).xyz;
#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE
gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity);
@@ -125,6 +126,24 @@ void PS_Forward(
float3 screenColor = sceneColorTexture.SampleLevel(SamplerPointClamp, hit.xy, 0).rgb;
reflections = lerp(reflections, screenColor, hit.z);
}
// Fallback to software tracing if possible
#if USE_GLOBAL_SURFACE_ATLAS && CAN_USE_GLOBAL_SURFACE_ATLAS
// If hit.z >= 0.9f then skip sdf tracing
if (hit.z < 0.9f)
{
// Don't use temporal effect in forward pass
float3 reflectWS = ScreenSpaceReflectionDirection(screenUV, gBuffer, ViewPos);
float4 surfaceAtlas;
if(TraceSDFSoftwareReflections(gBuffer, reflectWS, surfaceAtlas)){
float3 screenColor = sceneColorTexture.SampleLevel(SamplerPointClamp, hit.xy, 0).rgb;
reflections = lerp(surfaceAtlas, float4(screenColor, 1), hit.z);
}
}
#endif
#endif
light.rgb += reflections * GetReflectionSpecularLighting(ViewPos, gBuffer) * light.a;

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
@0// SDF Reflections: Defines
#define USE_GLOBAL_SURFACE_ATLAS 1
@1// SDF Reflections: Includes
#include "./Flax/GlobalSignDistanceField.hlsl"
#include "./Flax/GI/GlobalSurfaceAtlas.hlsl"
@2// SDF Reflections: Constants
GlobalSDFData GlobalSDF;
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
@3// SDF Reflections: Resources
Texture3D<float> GlobalSDFTex : register(t__SRV__);
Texture3D<float> GlobalSDFMip : register(t__SRV__);
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t__SRV__);
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t__SRV__);
Buffer<float4> GlobalSurfaceAtlasObjects : register(t__SRV__);
Texture2D GlobalSurfaceAtlasDepth : register(t__SRV__);
Texture2D GlobalSurfaceAtlasTex : register(t__SRV__);
@4// SDF Reflections: Utilities
bool TraceSDFSoftwareReflections(GBufferSample gBuffer, float3 reflectWS, out float4 surfaceAtlas)
{
GlobalSDFTrace sdfTrace;
float maxDistance = GLOBAL_SDF_WORLD_SIZE;
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);
surfaceAtlas = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, RWGlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -reflectWS, surfaceThreshold);
return true;
}
return false;
}
@5// SDF Reflections: Shaders

View File

@@ -51,8 +51,11 @@ void ForwardMaterialShader::Bind(BindParameters& params)
int32 srv = 2;
// Setup features
if ((_info.FeaturesFlags & MaterialFeaturesFlags::GlobalIllumination) != MaterialFeaturesFlags::None)
if ((_info.FeaturesFlags & MaterialFeaturesFlags::GlobalIllumination) != MaterialFeaturesFlags::None) {
GlobalIlluminationFeature::Bind(params, cb, srv);
if ((_info.FeaturesFlags & MaterialFeaturesFlags::ScreenSpaceReflections) != MaterialFeaturesFlags::None)
SDFReflectionsFeature::Bind(params, cb, srv);
}
ForwardShadingFeature::Bind(params, cb, srv);
// Setup parameters
@@ -178,6 +181,7 @@ bool ForwardMaterialShader::Load()
psDesc.VS = _shader->GetVS("VS");
if (psDesc.VS == nullptr)
return true;
psDesc.PS = _shader->GetPS("PS_Forward");
psDesc.DepthWriteEnable = false;
psDesc.BlendMode = BlendingMode::AlphaBlend;

View File

@@ -5,6 +5,7 @@
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/ShadowsPass.h"
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#if USE_EDITOR
#include "Engine/Renderer/Lightmaps.h"
#endif
@@ -131,13 +132,13 @@ bool LightmapFeature::Bind(MaterialShader::BindParameters& params, Span<byte>& c
const bool useLightmap = EnumHasAnyFlags(params.RenderContext.View.Flags, ViewFlags::GI)
#if USE_EDITOR
&& EnableLightmapsUsage
&& EnableLightmapsUsage
#endif
&& drawCall.Surface.Lightmap != nullptr;
&& drawCall.Surface.Lightmap != nullptr;
if (useLightmap)
{
// Bind lightmap textures
GPUTexture *lightmap0, *lightmap1, *lightmap2;
GPUTexture* lightmap0, * lightmap1, * lightmap2;
drawCall.Features.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2);
params.GPUContext->BindSR(srv + 0, lightmap0);
params.GPUContext->BindSR(srv + 1, lightmap1);
@@ -195,6 +196,61 @@ bool GlobalIlluminationFeature::Bind(MaterialShader::BindParameters& params, Spa
return useGI;
}
bool SDFReflectionsFeature::Bind(MaterialShader::BindParameters& params, Span<byte>& cb, int32& srv)
{
auto& data = *(Data*)cb.Get();
ASSERT_LOW_LAYER(cb.Length() >= sizeof(Data));
bool useSDFReflections = false;
if (EnumHasAnyFlags(params.RenderContext.View.Flags, ViewFlags::Reflections))
{
switch (params.RenderContext.List->Settings.ScreenSpaceReflections.TraceMode)
{
case ReflectionsTraceMode::SoftwareTracing:
{
GlobalSignDistanceFieldPass::BindingData bindingDataSDF;
GlobalSurfaceAtlasPass::BindingData bindingDataSurfaceAtlas;
if (!GlobalSignDistanceFieldPass::Instance()->Get(params.RenderContext.Buffers, bindingDataSDF) &&
!GlobalSurfaceAtlasPass::Instance()->Get(params.RenderContext.Buffers, bindingDataSurfaceAtlas))
{
useSDFReflections = true;
// Bind DDGI data
data.GlobalSDF = bindingDataSDF.Constants;
data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants;
params.GPUContext->BindSR(srv + 0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr);
params.GPUContext->BindSR(srv + 1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr);
params.GPUContext->BindSR(srv + 2, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr);
params.GPUContext->BindSR(srv + 3, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr);
params.GPUContext->BindSR(srv + 4, bindingDataSurfaceAtlas.Objects ? bindingDataSurfaceAtlas.Objects->View() : nullptr);
params.GPUContext->BindSR(srv + 5, bindingDataSurfaceAtlas.AtlasDepth->View());
params.GPUContext->BindSR(srv + 6, bindingDataSurfaceAtlas.AtlasLighting->View());
}
break;
}
}
}
if (!useSDFReflections)
{
data.GlobalSDF.CascadesCount = 0;
// Unbind SRVs to prevent issues
params.GPUContext->UnBindSR(srv + 0);
params.GPUContext->UnBindSR(srv + 1);
params.GPUContext->UnBindSR(srv + 2);
params.GPUContext->UnBindSR(srv + 3);
params.GPUContext->UnBindSR(srv + 4);
params.GPUContext->UnBindSR(srv + 5);
params.GPUContext->UnBindSR(srv + 6);
}
cb = Span<byte>(cb.Get() + sizeof(Data), cb.Length() - sizeof(Data));
srv += SRVs;
return useSDFReflections;
}
#if USE_EDITOR
void ForwardShadingFeature::Generate(GeneratorData& data)
@@ -222,6 +278,11 @@ void GlobalIlluminationFeature::Generate(GeneratorData& data)
data.Template = TEXT("Features/GlobalIllumination.hlsl");
}
void SDFReflectionsFeature::Generate(GeneratorData& data)
{
data.Template = TEXT("Features/SDFReflections.hlsl");
}
void DistortionFeature::Generate(GeneratorData& data)
{
data.Template = TEXT("Features/Distortion.hlsl");

View File

@@ -6,6 +6,8 @@
#include "Engine/Core/Math/Rectangle.h"
#include "Engine/Core/Types/Span.h"
#include "Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h"
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h"
// Material shader features are plugin-based functionalities that are reusable between different material domains.
struct MaterialShaderFeature
@@ -91,6 +93,25 @@ struct GlobalIlluminationFeature : MaterialShaderFeature
#endif
};
// Material shader feature that adds SDF Reflections feature (software reflections).
struct SDFReflectionsFeature : MaterialShaderFeature
{
enum { SRVs = 7 };
PACK_STRUCT(struct Data
{
GlobalSignDistanceFieldPass::ConstantsData GlobalSDF;
GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas;
});
static bool Bind(MaterialShader::BindParameters& params, Span<byte>& cb, int32& srv);
#if USE_EDITOR
static void Generate(GeneratorData& data);
#endif
};
// Material shader feature that adds distortion vectors rendering pass.
struct DistortionFeature : MaterialShaderFeature
{

View File

@@ -360,6 +360,17 @@ void GlobalSurfaceAtlasPass::Dispose()
_shader = nullptr;
}
bool GlobalSurfaceAtlasPass::Get(const RenderBuffers* buffers, BindingData& result)
{
auto* surfaceAtlasData = buffers ? buffers->FindCustomBuffer<GlobalSurfaceAtlasCustomBuffer>(TEXT("GlobalSurfaceAtlas")) : nullptr;
if (surfaceAtlasData && surfaceAtlasData->LastFrameUsed + 1 >= Engine::FrameCount) // Allow to use Surface Atlas from the previous frame (not used currently)
{
result = surfaceAtlasData->Result;
return false;
}
return true;
}
bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* context, BindingData& result)
{
// Skip if not supported

View File

@@ -65,6 +65,14 @@ private:
void* _currentActorObject;
public:
/// <summary>
/// Gets the Global Surface Atlas (only if enabled in Graphics Settings).
/// </summary>
/// <param name="buffers">The rendering context buffers.</param>
/// <param name="result">The result Global Surface Atlas data for binding to the shaders.</param>
/// <returns>True if there is no valid Global Surface Atlas rendered during this frame, otherwise false.</returns>
bool Get(const RenderBuffers* buffers, BindingData& result);
/// <summary>
/// Renders the Global Surface Atlas.
/// </summary>

View File

@@ -197,8 +197,13 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
ADD_FEATURE(DeferredShadingFeature);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None)
ADD_FEATURE(DistortionFeature);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination))
ADD_FEATURE(GlobalIlluminationFeature);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination)) {
ADD_FEATURE(GlobalIlluminationFeature);
// SDF Reflections is only valid when both GI and SSR is enabled
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::ScreenSpaceReflections))
ADD_FEATURE(SDFReflectionsFeature);
}
if (materialInfo.BlendMode != MaterialBlendMode::Opaque)
ADD_FEATURE(ForwardShadingFeature);
break;

View File

@@ -14,6 +14,7 @@ struct GBufferSample
float Metalness;
float3 Color;
float Specular;
// View space position of pixel, DIFFERENT FROM GBufferData.ViewPos
float3 ViewPos;
float AO;
int ShadingModel;

View File

@@ -133,13 +133,14 @@ float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
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 && CAN_USE_GLOBAL_SURFACE_ATLAS
// Calculate reflection direction (the same TraceScreenSpaceReflection)
float3 reflectWS = ScreenSpaceReflectionDirection(input.TexCoord, gBuffer, gBufferData.ViewPos, TemporalEffect, TemporalTime, BRDFBias);
GlobalSDFTrace sdfTrace;
float maxDistance = 100000;
float maxDistance = GLOBAL_SDF_WORLD_SIZE;
float selfOcclusionBias = GlobalSDF.CascadeVoxelSize[0];
sdfTrace.Init(gBuffer.WorldPos + gBuffer.Normal * selfOcclusionBias, reflectWS, 0.0f, maxDistance);
GlobalSDFHit sdfHit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, sdfTrace);
@@ -148,7 +149,9 @@ float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
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);
// Now the sdf reflection part is significantly darker than the screen space part
// TODO: Maybe multiply surfaceAtlas by a constant to make it brighter, adding 2* looks fine
result = lerp(surfaceAtlas, float4(result.rgb, 1), result.a);
}
#endif