diff --git a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl index 9dbc19369..dc866c1ea 100644 --- a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl +++ b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl @@ -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; diff --git a/Content/Editor/MaterialTemplates/Features/SDFReflections.hlsl b/Content/Editor/MaterialTemplates/Features/SDFReflections.hlsl new file mode 100644 index 000000000..a7177e686 --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/SDFReflections.hlsl @@ -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 GlobalSDFTex : register(t__SRV__); +Texture3D GlobalSDFMip : register(t__SRV__); +ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t__SRV__); +ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t__SRV__); +Buffer 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 diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index af2b30348..fd9568bcf 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -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; diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 43646cc0e..94c963b74 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -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& 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& 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(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"); diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h index 0df2e9a2e..5b85353a0 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -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& cb, int32& srv); +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; + // Material shader feature that adds distortion vectors rendering pass. struct DistortionFeature : MaterialShaderFeature { diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index fb0faa27a..f2bc3169d 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -360,6 +360,17 @@ void GlobalSurfaceAtlasPass::Dispose() _shader = nullptr; } +bool GlobalSurfaceAtlasPass::Get(const RenderBuffers* buffers, BindingData& result) +{ + auto* surfaceAtlasData = buffers ? buffers->FindCustomBuffer(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 diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h index 443b3a401..45d144767 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h @@ -65,6 +65,14 @@ private: void* _currentActorObject; public: + /// + /// Gets the Global Surface Atlas (only if enabled in Graphics Settings). + /// + /// The rendering context buffers. + /// The result Global Surface Atlas data for binding to the shaders. + /// True if there is no valid Global Surface Atlas rendered during this frame, otherwise false. + bool Get(const RenderBuffers* buffers, BindingData& result); + /// /// Renders the Global Surface Atlas. /// diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 8cacddf51..bff70e709 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -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; diff --git a/Source/Shaders/GBufferCommon.hlsl b/Source/Shaders/GBufferCommon.hlsl index 88a6c3688..27095f000 100644 --- a/Source/Shaders/GBufferCommon.hlsl +++ b/Source/Shaders/GBufferCommon.hlsl @@ -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; diff --git a/Source/Shaders/SSR.shader b/Source/Shaders/SSR.shader index a813bdf36..3aa3cbc60 100644 --- a/Source/Shaders/SSR.shader +++ b/Source/Shaders/SSR.shader @@ -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