You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #preflight 6290efe4fe5e30102ab0d9a7 [CL 20394554 by Tiantian Xie in ue5-main branch]
1038 lines
47 KiB
C++
1038 lines
47 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SingleLayerWaterRendering.h"
|
|
#include "BasePassRendering.h"
|
|
#include "DeferredShadingRenderer.h"
|
|
#include "MeshPassProcessor.inl"
|
|
#include "PixelShaderUtils.h"
|
|
#include "PostProcess/PostProcessSubsurface.h"
|
|
#include "PostProcess/SceneRenderTargets.h"
|
|
#include "TemporalAA.h"
|
|
#include "RayTracing/RaytracingOptions.h"
|
|
#include "RayTracing/RayTracingReflections.h"
|
|
#include "VolumetricRenderTarget.h"
|
|
#include "RenderGraph.h"
|
|
#include "ScenePrivate.h"
|
|
#include "SceneRendering.h"
|
|
#include "ScreenSpaceRayTracing.h"
|
|
#include "SceneTextureParameters.h"
|
|
#include "Strata/Strata.h"
|
|
#include "VirtualShadowMaps/VirtualShadowMapArray.h"
|
|
#include "Lumen/LumenSceneData.h"
|
|
#include "Lumen/LumenTracingUtils.h"
|
|
|
|
DECLARE_GPU_STAT_NAMED(RayTracingWaterReflections, TEXT("Ray Tracing Water Reflections"));
|
|
|
|
DECLARE_GPU_STAT(SingleLayerWater);
|
|
DECLARE_CYCLE_STAT(TEXT("WaterSingleLayer"), STAT_CLP_WaterSingleLayerPass, STATGROUP_ParallelCommandListMarkers);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayer(
|
|
TEXT("r.Water.SingleLayer"), 1,
|
|
TEXT("Enable the single water rendering system."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerReflection(
|
|
TEXT("r.Water.SingleLayer.Reflection"), 1,
|
|
TEXT("Enable reflection rendering on water."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerTiledComposite(
|
|
TEXT("r.Water.SingleLayer.TiledComposite"), 1,
|
|
TEXT("Enable tiled optimisation of the water reflection rendering."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
int32 GSingleLayerWaterRefractionDownsampleFactor = 1;
|
|
static FAutoConsoleVariableRef CVarWaterSingleLayerRefractionDownsampleFactor(
|
|
TEXT("r.Water.SingleLayer.RefractionDownsampleFactor"),
|
|
GSingleLayerWaterRefractionDownsampleFactor,
|
|
TEXT("Resolution divider for the water refraction buffer."),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarParallelSingleLayerWaterPass(
|
|
TEXT("r.ParallelSingleLayerWaterPass"),
|
|
1,
|
|
TEXT("Toggles parallel single layer water pass rendering. Parallel rendering must be enabled for this to have an effect."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerSSR(
|
|
TEXT("r.Water.SingleLayer.SSR"), 1,
|
|
TEXT("Enable SSR for the single water rendering system."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerLumenReflections(
|
|
TEXT("r.Water.SingleLayer.LumenReflections"), 1,
|
|
TEXT("Enable Lumen reflections for the single water rendering system."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerShadersSupportDistanceFieldShadow(
|
|
TEXT("r.Water.SingleLayer.ShadersSupportDistanceFieldShadow"),
|
|
1,
|
|
TEXT("Whether or not the single layer water material shaders are compiled with support for distance field shadow, i.e. output main directional light luminance in a separate render target. This is preconditioned on using deferred shading and having distance field support enabled in the project."),
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerDistanceFieldShadow(
|
|
TEXT("r.Water.SingleLayer.DistanceFieldShadow"), 1,
|
|
TEXT("When using deferred, distance field shadow tracing is supported on single layer water. This cvar can be used to toggle it on/off at runtime."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerRTR(
|
|
TEXT("r.Water.SingleLayer.RTR"), 1,
|
|
TEXT("Enable RTR for the single water renderring system."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
static TAutoConsoleVariable<int32> CVarWaterSingleLayerSSRTAA(
|
|
TEXT("r.Water.SingleLayer.SSRTAA"), 1,
|
|
TEXT("Enable SSR denoising using TAA for the single water renderring system."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRHICmdFlushRenderThreadTasksSingleLayerWater(
|
|
TEXT("r.RHICmdFlushRenderThreadTasksSingleLayerWater"),
|
|
0,
|
|
TEXT("Wait for completion of parallel render thread tasks at the end of Single layer water. A more granular version of r.RHICmdFlushRenderThreadTasks. If either r.RHICmdFlushRenderThreadTasks or r.RHICmdFlushRenderThreadTasksSingleLayerWater is > 0 we will flush."));
|
|
|
|
// This is to have platforms use the simple single layer water shading similar to mobile: no dynamic lights, only sun and sky, no distortion, no colored transmittance on background, no custom depth read.
|
|
bool SingleLayerWaterUsesSimpleShading(EShaderPlatform ShaderPlatform)
|
|
{
|
|
return FDataDrivenShaderPlatformInfo::GetWaterUsesSimpleForwardShading(ShaderPlatform) && IsForwardShadingEnabled(ShaderPlatform);
|
|
}
|
|
|
|
bool ShouldRenderSingleLayerWater(TArrayView<const FViewInfo> Views)
|
|
{
|
|
if (CVarWaterSingleLayer.GetValueOnRenderThread() > 0)
|
|
{
|
|
for (const FViewInfo& View : Views)
|
|
{
|
|
if (View.bHasSingleLayerWaterMaterial)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ShouldRenderSingleLayerWaterSkippedRenderEditorNotification(TArrayView<const FViewInfo> Views)
|
|
{
|
|
if (CVarWaterSingleLayer.GetValueOnRenderThread() <= 0)
|
|
{
|
|
for (const FViewInfo& View : Views)
|
|
{
|
|
if (View.bHasSingleLayerWaterMaterial)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UseSingleLayerWaterIndirectDraw(EShaderPlatform ShaderPlatform)
|
|
{
|
|
return IsFeatureLevelSupported(ShaderPlatform, ERHIFeatureLevel::SM5)
|
|
// Vulkan gives error with WaterTileCatergorisationCS usage of atomic, and Metal does not play nice, either.
|
|
&& !IsVulkanMobilePlatform(ShaderPlatform)
|
|
&& FDataDrivenShaderPlatformInfo::GetSupportsWaterIndirectDraw(ShaderPlatform);
|
|
}
|
|
|
|
bool IsWaterDistanceFieldShadowEnabled_Runtime(const FStaticShaderPlatform Platform)
|
|
{
|
|
return IsWaterDistanceFieldShadowEnabled(Platform) && CVarWaterSingleLayerDistanceFieldShadow.GetValueOnRenderThread() > 0;
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FSingleLayerWaterCommonShaderParameters, )
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ScreenSpaceReflectionsTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, ScreenSpaceReflectionsSampler)
|
|
SHADER_PARAMETER_TEXTURE(Texture2D, PreIntegratedGF)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, PreIntegratedGFSampler)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneNoWaterDepthTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, SceneNoWaterDepthSampler)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SeparatedMainDirLightTexture)
|
|
SHADER_PARAMETER(FVector4f, SceneNoWaterMinMaxUV)
|
|
SHADER_PARAMETER(float, UseSeparatedMainDirLightTexture)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures) // Water scene texture
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
|
|
SHADER_PARAMETER_STRUCT_REF(FReflectionCaptureShaderData, ReflectionCaptureData)
|
|
SHADER_PARAMETER_STRUCT_REF(FReflectionUniformParameters, ReflectionsParameters)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FForwardLightData, ForwardLightData)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FStrataGlobalUniformParameters, Strata)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FSingleLayerWaterCompositePS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FSingleLayerWaterCompositePS);
|
|
SHADER_USE_PARAMETER_STRUCT(FSingleLayerWaterCompositePS, FGlobalShader)
|
|
|
|
class FHasBoxCaptures : SHADER_PERMUTATION_BOOL("REFLECTION_COMPOSITE_HAS_BOX_CAPTURES");
|
|
class FHasSphereCaptures : SHADER_PERMUTATION_BOOL("REFLECTION_COMPOSITE_HAS_SPHERE_CAPTURES");
|
|
using FPermutationDomain = TShaderPermutationDomain<FHasBoxCaptures, FHasSphereCaptures>;
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSingleLayerWaterCommonShaderParameters, CommonParameters)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
|
|
{
|
|
return PermutationVector;
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment);//Support reflection captures
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FSingleLayerWaterCompositePS, "/Engine/Private/SingleLayerWaterComposite.usf", "SingleLayerWaterCompositePS", SF_Pixel);
|
|
|
|
class FWaterTileCategorisationCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FWaterTileCategorisationCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FWaterTileCategorisationCS, FGlobalShader)
|
|
|
|
static int32 GetTileSize()
|
|
{
|
|
return 8;
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSingleLayerWaterCommonShaderParameters, CommonParameters)
|
|
SHADER_PARAMETER(uint32, VertexCountPerInstanceIndirect)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, DrawIndirectDataUAV)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, DispatchIndirectDataUAV)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, WaterTileListDataUAV)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
|
|
{
|
|
return PermutationVector;
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return UseSingleLayerWaterIndirectDraw(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("TILE_CATERGORISATION_SHADER"), 1.0f);
|
|
OutEnvironment.SetDefine(TEXT("WORK_TILE_SIZE"), GetTileSize());
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FWaterTileCategorisationCS, "/Engine/Private/SingleLayerWaterComposite.usf", "WaterTileCatergorisationCS", SF_Compute);
|
|
|
|
bool FWaterTileVS::ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return UseSingleLayerWaterIndirectDraw(Parameters.Platform);
|
|
}
|
|
|
|
void FWaterTileVS::ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("TILE_VERTEX_SHADER"), 1.0f);
|
|
OutEnvironment.SetDefine(TEXT("WORK_TILE_SIZE"), FWaterTileCategorisationCS::GetTileSize());
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
}
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FWaterTileVS, "/Engine/Private/SingleLayerWaterComposite.usf", "WaterTileVS", SF_Vertex);
|
|
|
|
class FWaterRefractionCopyPS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FWaterRefractionCopyPS);
|
|
SHADER_USE_PARAMETER_STRUCT(FWaterRefractionCopyPS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorCopyDownsampleTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, SceneColorCopyDownsampleSampler)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthCopyDownsampleTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, SceneDepthCopyDownsampleSampler)
|
|
SHADER_PARAMETER(FVector2f, SVPositionToSourceTextureUV)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FDownsampleRefraction : SHADER_PERMUTATION_BOOL("DOWNSAMPLE_REFRACTION");
|
|
class FDownsampleColor : SHADER_PERMUTATION_BOOL("DOWNSAMPLE_COLOR");
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<FDownsampleRefraction, FDownsampleColor>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FWaterRefractionCopyPS, "/Engine/Private/SingleLayerWaterComposite.usf", "WaterRefractionCopyPS", SF_Pixel);
|
|
|
|
static FSceneWithoutWaterTextures AddCopySceneWithoutWaterPass(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneViewFamily& ViewFamily,
|
|
TArrayView<const FViewInfo> Views,
|
|
FRDGTextureRef SceneColorTexture,
|
|
FRDGTextureRef SceneDepthTexture)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "SLW::CopySceneWithoutWater");
|
|
|
|
check(Views.Num() > 0);
|
|
check(SceneColorTexture);
|
|
check(SceneDepthTexture);
|
|
|
|
EShaderPlatform ShaderPlatform = Views[0].GetShaderPlatform();
|
|
const bool bCopyColor = !SingleLayerWaterUsesSimpleShading(ShaderPlatform);
|
|
|
|
const FRDGTextureDesc& SceneColorDesc = SceneColorTexture->Desc;
|
|
const FRDGTextureDesc& SceneDepthDesc = SceneColorTexture->Desc;
|
|
|
|
const int32 RefractionDownsampleFactor = FMath::Clamp(GSingleLayerWaterRefractionDownsampleFactor, 1, 8);
|
|
const FIntPoint RefractionResolution = FIntPoint::DivideAndRoundDown(SceneColorDesc.Extent, RefractionDownsampleFactor);
|
|
FRDGTextureRef SceneColorWithoutSingleLayerWaterTexture = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
|
|
|
|
if (bCopyColor)
|
|
{
|
|
const FRDGTextureDesc ColorDesc = FRDGTextureDesc::Create2D(RefractionResolution, SceneColorDesc.Format, SceneColorDesc.ClearValue, TexCreate_ShaderResource | TexCreate_RenderTargetable);
|
|
SceneColorWithoutSingleLayerWaterTexture = GraphBuilder.CreateTexture(ColorDesc, TEXT("SLW.SceneColorWithout"));
|
|
}
|
|
|
|
const FRDGTextureDesc DepthDesc(FRDGTextureDesc::Create2D(RefractionResolution, PF_R32_FLOAT, SceneDepthDesc.ClearValue, TexCreate_ShaderResource | TexCreate_RenderTargetable));
|
|
FRDGTextureRef SceneDepthWithoutSingleLayerWaterTexture = GraphBuilder.CreateTexture(DepthDesc, TEXT("SLW.SceneDepthWithout"));
|
|
|
|
const FRDGTextureDesc SeparatedMainDirLightDesc(FRDGTextureDesc::Create2D(SceneColorDesc.Extent, PF_FloatR11G11B10, FClearValueBinding(FLinearColor::White), TexCreate_ShaderResource | TexCreate_RenderTargetable));
|
|
FRDGTextureRef SeparatedMainDirLightTexture = GraphBuilder.CreateTexture(SeparatedMainDirLightDesc, TEXT("SLW.SeparatedMainDirLight"));
|
|
if (IsWaterDistanceFieldShadowEnabled_Runtime(ShaderPlatform) && Strata::IsStrataEnabled())
|
|
{
|
|
// This clear is needed with strata because that texture will be modulated by DFShadows.
|
|
// STRATA_TODO: when strata is enabled, we can change RenderRayTracedDistanceFieldProjection to have a bForceNoBlending instead if bForceRGBModulation and remove that clear.
|
|
AddClearRenderTargetPass(GraphBuilder, SeparatedMainDirLightTexture);
|
|
}
|
|
|
|
FSceneWithoutWaterTextures Textures;
|
|
Textures.RefractionDownsampleFactor = float(RefractionDownsampleFactor);
|
|
Textures.Views.SetNum(Views.Num());
|
|
|
|
ERenderTargetLoadAction LoadAction = ERenderTargetLoadAction::ENoAction;
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
if (!View.ShouldRenderView())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
|
|
|
FWaterRefractionCopyPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FWaterRefractionCopyPS::FParameters>();
|
|
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
|
|
PassParameters->SceneColorCopyDownsampleTexture = SceneColorTexture;
|
|
PassParameters->SceneColorCopyDownsampleSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
PassParameters->SceneDepthCopyDownsampleTexture = SceneDepthTexture;
|
|
PassParameters->SceneDepthCopyDownsampleSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
PassParameters->SVPositionToSourceTextureUV = FVector2f(RefractionDownsampleFactor / float(SceneColorDesc.Extent.X), RefractionDownsampleFactor / float(SceneColorDesc.Extent.Y));
|
|
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneDepthWithoutSingleLayerWaterTexture, LoadAction);
|
|
|
|
if (bCopyColor)
|
|
{
|
|
PassParameters->RenderTargets[1] = FRenderTargetBinding(SceneColorWithoutSingleLayerWaterTexture, LoadAction);
|
|
}
|
|
|
|
if (!View.Family->bMultiGPUForkAndJoin)
|
|
{
|
|
LoadAction = ERenderTargetLoadAction::ELoad;
|
|
}
|
|
|
|
FWaterRefractionCopyPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FWaterRefractionCopyPS::FDownsampleRefraction>(RefractionDownsampleFactor > 1);
|
|
PermutationVector.Set<FWaterRefractionCopyPS::FDownsampleColor>(bCopyColor);
|
|
auto PixelShader = View.ShaderMap->GetShader<FWaterRefractionCopyPS>(PermutationVector);
|
|
|
|
const FIntRect RefractionViewRect = FIntRect(FIntPoint::DivideAndRoundDown(View.ViewRect.Min, RefractionDownsampleFactor), FIntPoint::DivideAndRoundDown(View.ViewRect.Max, RefractionDownsampleFactor));
|
|
|
|
Textures.Views[ViewIndex].ViewRect = RefractionViewRect;
|
|
|
|
// This is usually half a pixel. But it seems that when using Gather4, 0.5 is not conservative enough and can return pixel outside the guard band.
|
|
// That is why it is a tiny bit higher than 0.5: for Gathre4 to always return pixels within the valid side of UVs (see EvaluateWaterVolumeLighting).
|
|
const float PixelSafeGuardBand = 0.55;
|
|
Textures.Views[ViewIndex].MinMaxUV.X = (RefractionViewRect.Min.X + PixelSafeGuardBand) / RefractionResolution.X;
|
|
Textures.Views[ViewIndex].MinMaxUV.Y = (RefractionViewRect.Min.Y + PixelSafeGuardBand) / RefractionResolution.Y;
|
|
Textures.Views[ViewIndex].MinMaxUV.Z = (RefractionViewRect.Max.X - PixelSafeGuardBand) / RefractionResolution.X;
|
|
Textures.Views[ViewIndex].MinMaxUV.W = (RefractionViewRect.Max.Y - PixelSafeGuardBand) / RefractionResolution.Y;
|
|
|
|
FPixelShaderUtils::AddFullscreenPass(
|
|
GraphBuilder,
|
|
View.ShaderMap,
|
|
{},
|
|
PixelShader,
|
|
PassParameters,
|
|
RefractionViewRect);
|
|
}
|
|
|
|
check(SceneColorWithoutSingleLayerWaterTexture);
|
|
check(SceneDepthWithoutSingleLayerWaterTexture);
|
|
Textures.ColorTexture = SceneColorWithoutSingleLayerWaterTexture;
|
|
Textures.DepthTexture = SceneDepthWithoutSingleLayerWaterTexture;
|
|
Textures.SeparatedMainDirLightTexture = SeparatedMainDirLightTexture;
|
|
return MoveTemp(Textures);
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FWaterCompositeParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FWaterTileVS::FParameters, VS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSingleLayerWaterCompositePS::FParameters, PS)
|
|
RDG_BUFFER_ACCESS(IndirectDrawParameter, ERHIAccess::IndirectArgs)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FDeferredShadingSceneRenderer::RenderSingleLayerWaterReflections(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneTextures& SceneTextures,
|
|
const FSceneWithoutWaterTextures& SceneWithoutWaterTextures,
|
|
FLumenSceneFrameTemporaries& LumenFrameTemporaries)
|
|
{
|
|
if (CVarWaterSingleLayer.GetValueOnRenderThread() <= 0 || CVarWaterSingleLayerReflection.GetValueOnRenderThread() <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder);
|
|
FRDGTextureRef SceneColorTexture = SceneTextures.Color.Resolve;
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
|
|
if (!View.ShouldRenderView())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
|
|
|
FRDGTextureRef ReflectionsColor = nullptr;
|
|
FRDGTextureRef BlackDummyTexture = SystemTextures.Black;
|
|
FRDGTextureRef WhiteDummyTexture = SystemTextures.White;
|
|
const FSceneTextureParameters SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, SceneTextures);
|
|
|
|
auto SetCommonParameters = [&](FSingleLayerWaterCommonShaderParameters& Parameters)
|
|
{
|
|
Parameters.ScreenSpaceReflectionsTexture = ReflectionsColor ? ReflectionsColor : BlackDummyTexture;
|
|
Parameters.ScreenSpaceReflectionsSampler = TStaticSamplerState<SF_Point>::GetRHI();
|
|
Parameters.PreIntegratedGF = GSystemTextures.PreintegratedGF->GetRHI();
|
|
Parameters.PreIntegratedGFSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Parameters.SceneNoWaterDepthTexture = SceneWithoutWaterTextures.DepthTexture ? SceneWithoutWaterTextures.DepthTexture : BlackDummyTexture;
|
|
Parameters.SceneNoWaterDepthSampler = TStaticSamplerState<SF_Point>::GetRHI();
|
|
Parameters.SceneNoWaterMinMaxUV = SceneWithoutWaterTextures.Views[ViewIndex].MinMaxUV;
|
|
Parameters.SeparatedMainDirLightTexture = BlackDummyTexture;
|
|
Parameters.UseSeparatedMainDirLightTexture = 0.0f;
|
|
Parameters.SceneTextures = SceneTextureParameters;
|
|
Parameters.ViewUniformBuffer = GetShaderBinding(View.ViewUniformBuffer);
|
|
Parameters.ReflectionCaptureData = View.ReflectionCaptureUniformBuffer;
|
|
{
|
|
FReflectionUniformParameters ReflectionUniformParameters;
|
|
SetupReflectionUniformParameters(View, ReflectionUniformParameters);
|
|
Parameters.ReflectionsParameters = CreateUniformBufferImmediate(ReflectionUniformParameters, UniformBuffer_SingleDraw);
|
|
}
|
|
Parameters.ForwardLightData = View.ForwardLightingResources.ForwardLightUniformBuffer;
|
|
Parameters.Strata = Strata::BindStrataGlobalUniformParameters(View);
|
|
};
|
|
|
|
const bool bRunTiled = UseSingleLayerWaterIndirectDraw(View.GetShaderPlatform()) && CVarWaterSingleLayerTiledComposite.GetValueOnRenderThread();
|
|
FTiledReflection TiledScreenSpaceReflection = {nullptr, nullptr, nullptr, 8};
|
|
FIntVector ViewRes(View.ViewRect.Width(), View.ViewRect.Height(), 1);
|
|
FIntVector TiledViewRes = FIntVector::DivideAndRoundUp(ViewRes, TiledScreenSpaceReflection.TileSize);
|
|
|
|
if (bRunTiled)
|
|
{
|
|
TiledScreenSpaceReflection.DrawIndirectParametersBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDrawIndirectParameters>(), TEXT("SLW.WaterIndirectDrawParameters"));
|
|
TiledScreenSpaceReflection.DispatchIndirectParametersBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDispatchIndirectParameters>(1), TEXT("SLW.WaterIndirectDispatchParameters"));
|
|
|
|
FRDGBufferRef TileListDataBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), TiledViewRes.X * TiledViewRes.Y), TEXT("SLW.TileListDataBuffer"));
|
|
TiledScreenSpaceReflection.TileListDataBufferSRV = GraphBuilder.CreateSRV(TileListDataBuffer, PF_R32_UINT);
|
|
|
|
FRDGBufferUAVRef DrawIndirectParametersBufferUAV = GraphBuilder.CreateUAV(TiledScreenSpaceReflection.DrawIndirectParametersBuffer);
|
|
FRDGBufferUAVRef DispatchIndirectParametersBufferUAV = GraphBuilder.CreateUAV(TiledScreenSpaceReflection.DispatchIndirectParametersBuffer);
|
|
FRDGBufferUAVRef TileListDataBufferUAV = GraphBuilder.CreateUAV(TileListDataBuffer, PF_R32_UINT);
|
|
|
|
// Clear DrawIndirectParametersBuffer
|
|
AddClearUAVPass(GraphBuilder, DrawIndirectParametersBufferUAV, 0);
|
|
AddClearUAVPass(GraphBuilder, DispatchIndirectParametersBufferUAV, 0);
|
|
|
|
// Categorization based on SHADING_MODEL_ID
|
|
{
|
|
TShaderMapRef<FWaterTileCategorisationCS> ComputeShader(View.ShaderMap);
|
|
|
|
FWaterTileCategorisationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FWaterTileCategorisationCS::FParameters>();
|
|
SetCommonParameters(PassParameters->CommonParameters);
|
|
PassParameters->VertexCountPerInstanceIndirect = GRHISupportsRectTopology ? 3 : 6;
|
|
PassParameters->DrawIndirectDataUAV = DrawIndirectParametersBufferUAV;
|
|
PassParameters->DispatchIndirectDataUAV = DispatchIndirectParametersBufferUAV;
|
|
PassParameters->WaterTileListDataUAV = TileListDataBufferUAV;
|
|
|
|
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("SLW::TileCategorisation"), ComputeShader, PassParameters, TiledViewRes);
|
|
}
|
|
}
|
|
|
|
const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View);
|
|
|
|
FProjectedShadowInfo* DistanceFieldShadowInfo = nullptr;
|
|
|
|
// Try to find the ProjectedShadowInfo corresponding to ray trace shadow info for the main directional light.
|
|
const FLightSceneProxy* SelectedForwardDirectionalLightProxy = View.ForwardLightingResources.SelectedForwardDirectionalLightProxy;
|
|
if (SelectedForwardDirectionalLightProxy)
|
|
{
|
|
FLightSceneInfo* LightSceneInfo = SelectedForwardDirectionalLightProxy->GetLightSceneInfo();
|
|
FVisibleLightInfo& VisibleLightViewInfo = ActiveViewFamily->VisibleLightInfos[LightSceneInfo->Id];
|
|
|
|
for (int32 ShadowIndex = 0; ShadowIndex < VisibleLightViewInfo.ShadowsToProject.Num(); ShadowIndex++)
|
|
{
|
|
FProjectedShadowInfo* ProjectedShadowInfo = VisibleLightViewInfo.ShadowsToProject[ShadowIndex];
|
|
if (ProjectedShadowInfo->bRayTracedDistanceField)
|
|
{
|
|
DistanceFieldShadowInfo = ProjectedShadowInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If DFShadow data has been found, then combine it with the separate main directional light luminance texture.
|
|
FRDGTextureRef ScreenShadowMaskTexture = SystemTextures.White;
|
|
if (DistanceFieldShadowInfo)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "SLW::DistanceFieldShadow");
|
|
|
|
FIntRect ScissorRect;
|
|
if (!SelectedForwardDirectionalLightProxy->GetScissorRect(ScissorRect, View, View.ViewRect))
|
|
{
|
|
ScissorRect = View.ViewRect;
|
|
}
|
|
|
|
// Reset the cached texture to create a new one mapping to the water depth buffer
|
|
DistanceFieldShadowInfo->ResetRayTracedDistanceFieldShadow(&View);
|
|
|
|
FTiledShadowRendering TiledShadowRendering;
|
|
if (bRunTiled)
|
|
{
|
|
TiledShadowRendering.DrawIndirectParametersBuffer = TiledScreenSpaceReflection.DrawIndirectParametersBuffer;
|
|
TiledShadowRendering.TileListDataBufferSRV = TiledScreenSpaceReflection.TileListDataBufferSRV;
|
|
TiledShadowRendering.TileSize = TiledScreenSpaceReflection.TileSize;
|
|
}
|
|
|
|
const bool bProjectingForForwardShading = false;
|
|
const bool bForceRGBModulation = true;
|
|
DistanceFieldShadowInfo->RenderRayTracedDistanceFieldProjection(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
SceneWithoutWaterTextures.SeparatedMainDirLightTexture,
|
|
View,
|
|
ScissorRect,
|
|
bProjectingForForwardShading,
|
|
bForceRGBModulation,
|
|
bRunTiled ? &TiledShadowRendering : nullptr);
|
|
}
|
|
|
|
if (ViewPipelineState.ReflectionsMethod == EReflectionsMethod::Lumen && CVarWaterSingleLayerLumenReflections.GetValueOnRenderThread() != 0)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "SLW::LumenReflections");
|
|
|
|
FLumenMeshSDFGridParameters MeshSDFGridParameters;
|
|
LumenRadianceCache::FRadianceCacheInterpolationParameters RadianceCacheParameters;
|
|
FLumenReflectionCompositeParameters LumenReflectionCompositeParameters;
|
|
|
|
ReflectionsColor = RenderLumenReflections(
|
|
GraphBuilder,
|
|
View,
|
|
SceneTextures,
|
|
LumenFrameTemporaries,
|
|
MeshSDFGridParameters,
|
|
RadianceCacheParameters,
|
|
ELumenReflectionPass::SingleLayerWater,
|
|
&TiledScreenSpaceReflection,
|
|
nullptr,
|
|
LumenReflectionCompositeParameters);
|
|
}
|
|
else if (ViewPipelineState.ReflectionsMethod == EReflectionsMethod::RTR
|
|
&& CVarWaterSingleLayerRTR.GetValueOnRenderThread() != 0
|
|
&& FDataDrivenShaderPlatformInfo::GetSupportsHighEndRayTracingReflections(View.GetShaderPlatform()))
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "SLW::RayTracingReflections");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, RayTracingWaterReflections);
|
|
|
|
IScreenSpaceDenoiser::FReflectionsInputs DenoiserInputs;
|
|
IScreenSpaceDenoiser::FReflectionsRayTracingConfig RayTracingConfig;
|
|
|
|
//RayTracingConfig.ResolutionFraction = FMath::Clamp(GetRayTracingReflectionsScreenPercentage() / 100.0f, 0.25f, 1.0f);
|
|
RayTracingConfig.ResolutionFraction = 1.0f;
|
|
//RayTracingConfig.RayCountPerPixel = GetRayTracingReflectionsSamplesPerPixel(View) > -1 ? GetRayTracingReflectionsSamplesPerPixel(View) : View.FinalPostProcessSettings.RayTracingReflectionsSamplesPerPixel;
|
|
RayTracingConfig.RayCountPerPixel = 1;
|
|
|
|
// Water is assumed to have zero roughness and is not currently denoised.
|
|
//int32 DenoiserMode = GetReflectionsDenoiserMode();
|
|
//bool bDenoise = DenoiserMode != 0;
|
|
int32 DenoiserMode = 0;
|
|
bool bDenoise = false;
|
|
|
|
if (!bDenoise)
|
|
{
|
|
RayTracingConfig.ResolutionFraction = 1.0f;
|
|
}
|
|
|
|
FRayTracingReflectionOptions Options;
|
|
Options.Algorithm = FRayTracingReflectionOptions::BruteForce;
|
|
Options.SamplesPerPixel = 1;
|
|
Options.ResolutionFraction = 1.0;
|
|
Options.bReflectOnlyWater = true;
|
|
|
|
{
|
|
float UpscaleFactor = 1.0;
|
|
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
|
|
SceneTextures.Config.Extent / UpscaleFactor,
|
|
PF_FloatRGBA,
|
|
FClearValueBinding::None,
|
|
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
|
|
|
|
DenoiserInputs.Color = GraphBuilder.CreateTexture(Desc, TEXT("SLW.RayTracingReflections"));
|
|
|
|
Desc.Format = PF_R16F;
|
|
DenoiserInputs.RayHitDistance = GraphBuilder.CreateTexture(Desc, TEXT("SLW.RayTracingReflectionsHitDistance"));
|
|
DenoiserInputs.RayImaginaryDepth = GraphBuilder.CreateTexture(Desc, TEXT("SLW.RayTracingReflectionsImaginaryDepth"));
|
|
}
|
|
|
|
RenderRayTracingReflections(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
View,
|
|
DenoiserMode,
|
|
Options,
|
|
&DenoiserInputs);
|
|
|
|
if (bDenoise)
|
|
{
|
|
const IScreenSpaceDenoiser* DefaultDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
|
|
const IScreenSpaceDenoiser* DenoiserToUse = DenoiserMode == 1 ? DefaultDenoiser : GScreenSpaceDenoiser;
|
|
|
|
// Standard event scope for denoiser to have all profiling information not matter what, and with explicit detection of third party.
|
|
RDG_EVENT_SCOPE(GraphBuilder, "%s%s(WaterReflections) %dx%d",
|
|
DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""),
|
|
DenoiserToUse->GetDebugName(),
|
|
View.ViewRect.Width(), View.ViewRect.Height());
|
|
|
|
IScreenSpaceDenoiser::FReflectionsOutputs DenoiserOutputs = DenoiserToUse->DenoiseWaterReflections(
|
|
GraphBuilder,
|
|
View,
|
|
&View.PrevViewInfo,
|
|
SceneTextureParameters,
|
|
DenoiserInputs,
|
|
RayTracingConfig);
|
|
|
|
ReflectionsColor = DenoiserOutputs.Color;
|
|
}
|
|
else
|
|
{
|
|
ReflectionsColor = DenoiserInputs.Color;
|
|
}
|
|
}
|
|
else if (ViewPipelineState.ReflectionsMethod == EReflectionsMethod::SSR && CVarWaterSingleLayerSSR.GetValueOnRenderThread() != 0)
|
|
{
|
|
// RUN SSR
|
|
// Uses the water GBuffer (depth, ABCDEF) to know how to start tracing.
|
|
// The water scene depth is used to know where to start tracing.
|
|
// Then it uses the scene HZB for the ray casting process.
|
|
|
|
IScreenSpaceDenoiser::FReflectionsInputs DenoiserInputs;
|
|
IScreenSpaceDenoiser::FReflectionsRayTracingConfig RayTracingConfig;
|
|
ESSRQuality SSRQuality;
|
|
ScreenSpaceRayTracing::GetSSRQualityForView(View, &SSRQuality, &RayTracingConfig);
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "SLW::ScreenSpaceReflections(Quality=%d)", int32(SSRQuality));
|
|
|
|
const bool bDenoise = false;
|
|
const bool bSingleLayerWater = true;
|
|
ScreenSpaceRayTracing::RenderScreenSpaceReflections(
|
|
GraphBuilder, SceneTextureParameters, SceneTextures.Color.Resolve, View, SSRQuality, bDenoise, &DenoiserInputs, bSingleLayerWater, bRunTiled ? &TiledScreenSpaceReflection : nullptr);
|
|
|
|
ReflectionsColor = DenoiserInputs.Color;
|
|
|
|
if (CVarWaterSingleLayerSSRTAA.GetValueOnRenderThread() && ScreenSpaceRayTracing::IsSSRTemporalPassRequired(View)) // TAA pass is an option
|
|
{
|
|
check(View.ViewState);
|
|
FTAAPassParameters TAASettings(View);
|
|
TAASettings.SceneDepthTexture = SceneTextureParameters.SceneDepthTexture;
|
|
TAASettings.SceneVelocityTexture = SceneTextureParameters.GBufferVelocityTexture;
|
|
TAASettings.Pass = ETAAPassConfig::ScreenSpaceReflections;
|
|
TAASettings.SceneColorInput = DenoiserInputs.Color;
|
|
TAASettings.bOutputRenderTargetable = true;
|
|
|
|
FTAAOutputs TAAOutputs = AddTemporalAAPass(
|
|
GraphBuilder,
|
|
View,
|
|
TAASettings,
|
|
View.PrevViewInfo.WaterSSRHistory,
|
|
&View.ViewState->PrevFrameViewInfo.WaterSSRHistory);
|
|
|
|
ReflectionsColor = TAAOutputs.SceneColor;
|
|
}
|
|
}
|
|
|
|
// Composite reflections on water
|
|
{
|
|
const bool bHasBoxCaptures = (View.NumBoxReflectionCaptures > 0);
|
|
const bool bHasSphereCaptures = (View.NumSphereReflectionCaptures > 0);
|
|
|
|
FSingleLayerWaterCompositePS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FSingleLayerWaterCompositePS::FHasBoxCaptures>(bHasBoxCaptures);
|
|
PermutationVector.Set<FSingleLayerWaterCompositePS::FHasSphereCaptures>(bHasSphereCaptures);
|
|
TShaderMapRef<FSingleLayerWaterCompositePS> PixelShader(View.ShaderMap, PermutationVector);
|
|
|
|
FWaterCompositeParameters* PassParameters = GraphBuilder.AllocParameters<FWaterCompositeParameters>();
|
|
|
|
PassParameters->VS.ViewUniformBuffer = GetShaderBinding(View.ViewUniformBuffer);
|
|
PassParameters->VS.TileListData = TiledScreenSpaceReflection.TileListDataBufferSRV;
|
|
|
|
SetCommonParameters(PassParameters->PS.CommonParameters);
|
|
if (IsWaterDistanceFieldShadowEnabled_Runtime(Scene->GetShaderPlatform()))
|
|
{
|
|
PassParameters->PS.CommonParameters.SeparatedMainDirLightTexture = SceneWithoutWaterTextures.SeparatedMainDirLightTexture;
|
|
PassParameters->PS.CommonParameters.UseSeparatedMainDirLightTexture = 1.0f;
|
|
}
|
|
|
|
PassParameters->IndirectDrawParameter = TiledScreenSpaceReflection.DrawIndirectParametersBuffer;
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneColorTexture, ERenderTargetLoadAction::ELoad);
|
|
|
|
ValidateShaderParameters(PixelShader, PassParameters->PS);
|
|
ClearUnusedGraphResources(PixelShader, &PassParameters->PS);
|
|
|
|
if (bRunTiled)
|
|
{
|
|
FWaterTileVS::FPermutationDomain VsPermutationVector;
|
|
TShaderMapRef<FWaterTileVS> VertexShader(View.ShaderMap, VsPermutationVector);
|
|
ValidateShaderParameters(VertexShader, PassParameters->VS);
|
|
ClearUnusedGraphResources(VertexShader, &PassParameters->VS);
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("SLW::Composite %dx%d", View.ViewRect.Width(), View.ViewRect.Height()),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[PassParameters, &View, TiledScreenSpaceReflection, VertexShader, PixelShader, bRunTiled](FRHICommandList& InRHICmdList)
|
|
{
|
|
InRHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
InRHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
GraphicsPSOInit.PrimitiveType = GRHISupportsRectTopology ? PT_RectList : PT_TriangleList;
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_SourceAlpha>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GEmptyVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
SetGraphicsPipelineState(InRHICmdList, GraphicsPSOInit, 0);
|
|
|
|
SetShaderParameters(InRHICmdList, VertexShader, VertexShader.GetVertexShader(), PassParameters->VS);
|
|
SetShaderParameters(InRHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
|
|
InRHICmdList.DrawPrimitiveIndirect(PassParameters->IndirectDrawParameter->GetIndirectRHICallBuffer(), 0);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("SLW::Composite %dx%d", View.ViewRect.Width(), View.ViewRect.Height()),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[PassParameters, &View, TiledScreenSpaceReflection, PixelShader, bRunTiled](FRHICommandList& InRHICmdList)
|
|
{
|
|
InRHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
FPixelShaderUtils::InitFullscreenPipelineState(InRHICmdList, View.ShaderMap, PixelShader, GraphicsPSOInit);
|
|
|
|
// Premultiplied alpha where alpha is transmittance.
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_SourceAlpha>::GetRHI();
|
|
|
|
SetGraphicsPipelineState(InRHICmdList, GraphicsPSOInit, 0);
|
|
SetShaderParameters(InRHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
FPixelShaderUtils::DrawFullscreenTriangle(InRHICmdList);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderSingleLayerWater(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneTextures& SceneTextures,
|
|
bool bShouldRenderVolumetricCloud,
|
|
FSceneWithoutWaterTextures& SceneWithoutWaterTextures,
|
|
FLumenSceneFrameTemporaries& LumenFrameTemporaries)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "SingleLayerWater");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, SingleLayerWater);
|
|
|
|
// Copy the texture to be available for the water surface to refract
|
|
SceneWithoutWaterTextures = AddCopySceneWithoutWaterPass(GraphBuilder, *ActiveViewFamily, Views, SceneTextures.Color.Resolve, SceneTextures.Depth.Resolve);
|
|
|
|
// Render height fog over the color buffer if it is allocated, e.g. SingleLayerWaterUsesSimpleShading is true.
|
|
if (SceneWithoutWaterTextures.ColorTexture && ShouldRenderFog(*ActiveViewFamily))
|
|
{
|
|
RenderUnderWaterFog(GraphBuilder, SceneWithoutWaterTextures, SceneTextures.UniformBuffer);
|
|
}
|
|
if (SceneWithoutWaterTextures.ColorTexture && bShouldRenderVolumetricCloud)
|
|
{
|
|
// This path is only taken when rendering the clouds in a render target that can be composited
|
|
ComposeVolumetricRenderTargetOverSceneUnderWater(GraphBuilder, Views, SceneWithoutWaterTextures, SceneTextures);
|
|
}
|
|
|
|
RenderSingleLayerWaterInner(GraphBuilder, SceneTextures, SceneWithoutWaterTextures);
|
|
|
|
// No SSR or composite needed in Forward. Reflections are applied in the WaterGBuffer pass.
|
|
if (!IsAnyForwardShadingEnabled(ShaderPlatform))
|
|
{
|
|
// If supported render SSR, the composite pass in non deferred and/or under water effect.
|
|
RenderSingleLayerWaterReflections(GraphBuilder, SceneTextures, SceneWithoutWaterTextures, LumenFrameTemporaries);
|
|
}
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FSingleLayerWaterPassParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
|
|
SHADER_PARAMETER_STRUCT_REF(FReflectionCaptureShaderData, ReflectionCapture)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FOpaqueBasePassUniformParameters, BasePass)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMapSamplingParameters)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FDeferredShadingSceneRenderer::RenderSingleLayerWaterInner(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneTextures& SceneTextures,
|
|
const FSceneWithoutWaterTextures& SceneWithoutWaterTextures)
|
|
{
|
|
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, Water);
|
|
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderSingleLayerWaterPass, FColor::Emerald);
|
|
SCOPE_CYCLE_COUNTER(STAT_WaterPassDrawTime);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "SLW::Draw");
|
|
|
|
const bool bRenderInParallel = GRHICommandList.UseParallelAlgorithms() && CVarParallelSingleLayerWaterPass.GetValueOnRenderThread() == 1;
|
|
|
|
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder);
|
|
|
|
TStaticArray<FTextureRenderTargetBinding, MaxSimultaneousRenderTargets> BasePassTextures;
|
|
uint32 BasePassTextureCount = SceneTextures.GetGBufferRenderTargets(BasePassTextures);
|
|
if(IsWaterDistanceFieldShadowEnabled_Runtime(Scene->GetShaderPlatform())
|
|
&& !Strata::IsStrataEnabled()) // We do not bind that texture if Strata is enabled as the data will go through the Strata material buffer.
|
|
{
|
|
const bool bNeverClear = true;
|
|
BasePassTextures[BasePassTextureCount++] = FTextureRenderTargetBinding(SceneWithoutWaterTextures.SeparatedMainDirLightTexture, bNeverClear);
|
|
}
|
|
Strata::AppendStrataMRTs(*this, BasePassTextureCount, BasePassTextures);
|
|
TArrayView<FTextureRenderTargetBinding> BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);
|
|
|
|
FRDGTextureRef WhiteForwardScreenSpaceShadowMask = SystemTextures.White;
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
|
|
if (!View.ShouldRenderView())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
|
View.BeginRenderView();
|
|
|
|
FSingleLayerWaterPassParameters* PassParameters = GraphBuilder.AllocParameters<FSingleLayerWaterPassParameters>();
|
|
PassParameters->View = View.GetShaderParameters();
|
|
PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
|
PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, {}, {}, &SceneWithoutWaterTextures);
|
|
PassParameters->VirtualShadowMapSamplingParameters = ActiveViewFamily->VirtualShadowMapArray.GetSamplingParameters(GraphBuilder);
|
|
PassParameters->RenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
|
|
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite);
|
|
|
|
View.ParallelMeshDrawCommandPasses[EMeshPass::SingleLayerWaterPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
|
|
|
if (bRenderInParallel)
|
|
{
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("SingleLayerWaterParallel"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
|
[this, &View, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_WaterSingleLayerPass), *this, View, FParallelCommandListBindings(PassParameters));
|
|
View.ParallelMeshDrawCommandPasses[EMeshPass::SingleLayerWaterPass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("SingleLayerWater"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, &View, PassParameters](FRHICommandList& RHICmdList)
|
|
{
|
|
SetStereoViewport(RHICmdList, View, 1.0f);
|
|
View.ParallelMeshDrawCommandPasses[EMeshPass::SingleLayerWaterPass].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
|
});
|
|
}
|
|
}
|
|
|
|
AddResolveSceneDepthPass(GraphBuilder, Views, SceneTextures.Depth);
|
|
}
|
|
|
|
class FSingleLayerWaterPassMeshProcessor : public FMeshPassProcessor
|
|
{
|
|
public:
|
|
FSingleLayerWaterPassMeshProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InPassDrawRenderState, FMeshPassDrawListContext* InDrawListContext);
|
|
|
|
void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final;
|
|
|
|
private:
|
|
bool TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material);
|
|
|
|
bool Process(
|
|
const FMeshBatch& MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode);
|
|
|
|
FMeshPassProcessorRenderState PassDrawRenderState;
|
|
};
|
|
|
|
FSingleLayerWaterPassMeshProcessor::FSingleLayerWaterPassMeshProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, const FMeshPassProcessorRenderState& InPassDrawRenderState, FMeshPassDrawListContext* InDrawListContext)
|
|
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
|
|
, PassDrawRenderState(InPassDrawRenderState)
|
|
{
|
|
if (SingleLayerWaterUsesSimpleShading(Scene->GetShaderPlatform()))
|
|
{
|
|
// Force non opaque, pre multiplied alpha, transparent blend mode because water is going to be blended against scene color (no distortion from texture scene color).
|
|
FRHIBlendState* ForwardSimpleWaterBlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
|
|
PassDrawRenderState.SetBlendState(ForwardSimpleWaterBlendState);
|
|
}
|
|
}
|
|
|
|
void FSingleLayerWaterPassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
|
|
{
|
|
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
|
|
while (MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
|
|
if (Material)
|
|
{
|
|
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
|
|
}
|
|
}
|
|
|
|
bool FSingleLayerWaterPassMeshProcessor::TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material)
|
|
{
|
|
if (Material.GetShadingModels().HasShadingModel(MSM_SingleLayerWater))
|
|
{
|
|
// Determine the mesh's material and blend mode.
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, Material, OverrideSettings);
|
|
return Process(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, MaterialRenderProxy, Material, MeshFillMode, MeshCullMode);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FSingleLayerWaterPassMeshProcessor::Process(
|
|
const FMeshBatch& MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode)
|
|
{
|
|
FUniformLightMapPolicy NoLightmapPolicy(LMP_NO_LIGHTMAP);
|
|
typedef FUniformLightMapPolicy LightMapPolicyType;
|
|
TMeshProcessorShaders<
|
|
TBasePassVertexShaderPolicyParamType<LightMapPolicyType>,
|
|
TBasePassPixelShaderPolicyParamType<LightMapPolicyType>> WaterPassShaders;
|
|
|
|
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
|
|
const bool bRenderSkylight = true;
|
|
if (!GetBasePassShaders<LightMapPolicyType>(
|
|
MaterialResource,
|
|
VertexFactory->GetType(),
|
|
NoLightmapPolicy,
|
|
FeatureLevel,
|
|
bRenderSkylight,
|
|
false,
|
|
&WaterPassShaders.VertexShader,
|
|
&WaterPassShaders.PixelShader
|
|
))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TBasePassShaderElementData<LightMapPolicyType> ShaderElementData(nullptr);
|
|
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
|
|
|
|
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(WaterPassShaders.VertexShader, WaterPassShaders.PixelShader);
|
|
|
|
BuildMeshDrawCommands(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
MaterialResource,
|
|
PassDrawRenderState,
|
|
WaterPassShaders,
|
|
MeshFillMode,
|
|
MeshCullMode,
|
|
SortKey,
|
|
EMeshPassFeatures::Default,
|
|
ShaderElementData);
|
|
|
|
return true;
|
|
}
|
|
|
|
FMeshPassProcessor* CreateSingleLayerWaterPassProcessor(const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
|
|
{
|
|
FMeshPassProcessorRenderState DrawRenderState;
|
|
|
|
// Make sure depth write is enabled.
|
|
FExclusiveDepthStencil::Type BasePassDepthStencilAccess_DepthWrite = FExclusiveDepthStencil::Type(Scene->DefaultBasePassDepthStencilAccess | FExclusiveDepthStencil::DepthWrite);
|
|
SetupBasePassState(BasePassDepthStencilAccess_DepthWrite, false, DrawRenderState);
|
|
|
|
return new(FMemStack::Get()) FSingleLayerWaterPassMeshProcessor(Scene, InViewIfDynamicMeshCommand, DrawRenderState, InDrawListContext);
|
|
}
|
|
|
|
FRegisterPassProcessorCreateFunction RegisterSingleLayerWaterPass(&CreateSingleLayerWaterPassProcessor, EShadingPath::Deferred, EMeshPass::SingleLayerWaterPass, EMeshPassFlags::MainView);
|