Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/Shadows/FirstPersonSelfShadow.cpp
2025-05-18 13:04:45 +08:00

461 lines
23 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "FirstPersonSelfShadow.h"
#include "LightSceneProxy.h"
#include "SceneTextures.h"
#include "../LightRendering.h"
#include "../ShadowRendering.h"
#include "../FirstPersonSceneExtension.h"
#include "RenderGraphUtils.h"
#include "PixelShaderUtils.h"
#include "HZB.h"
static TAutoConsoleVariable<int32> CVarFirstPersonSelfShadow(
TEXT("r.FirstPerson.SelfShadow"),
0,
TEXT("Enables self shadows for first person primitives. Self shadows are achieved with HZB screen space traces. Use r.FirstPerson.SelfShadow.LightTypes to control which shadow casting light types should cast self shadows."),
ECVF_RenderThreadSafe | ECVF_Scalability);
static TAutoConsoleVariable<int32> CVarFirstPersonSelfShadowLightTypes(
TEXT("r.FirstPerson.SelfShadow.LightTypes"),
0,
TEXT("Controls which light types should cast self shadows for first person primitives. 0: Directional Lights Only, 1: Local Lights Only, 2: All Lights"),
ECVF_RenderThreadSafe | ECVF_Scalability);
static TAutoConsoleVariable<int32> CVarFirstPersonSelfShadowCheckerboardMode(
TEXT("r.FirstPerson.SelfShadow.DownsampleCheckerboardMode"),
0,
TEXT("Controls how to downsample depth and normals for first person self shadows. 0: always pick closest depth, 1: always pick farthest depth, 2: alternate closest/farthest in a checkerboard pattern."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarFirstPersonSelfShadowMaxTraceDistance(
TEXT("r.FirstPerson.SelfShadow.MaxTraceDistance"),
100.0f,
TEXT("Maximum world space trace distance for shadow rays."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarFirstPersonSelfShadowMaxIterations(
TEXT("r.FirstPerson.SelfShadow.MaxHZBTraceIterations"),
512,
TEXT("Maximum number of HZB traversal iterations during the first person self shadow screen trace. Lowering this number can improve performance at the cost of potential shadow artifacts."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarFirstPersonSelfShadowRelativeDepthThickness(
TEXT("r.FirstPerson.SelfShadow.RelativeDepthThickness"),
0.2f,
TEXT("Relative depth thickness behind which a screen space tracing hit is ignored."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarFirstPersonSelfShadowMinimumHZBTraceOccupancy(
TEXT("r.FirstPerson.SelfShadow.MinimumHZBTraceOccupancy"),
0,
TEXT("Minimum wave thread occupancy below which HZB tracing is aborted. Setting this to a value higher than 0 can improve performance at the cost of potential shadow artifacts."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarFirstPersonSelfShadowBilateralFilterDepthThreshold(
TEXT("r.FirstPerson.SelfShadow.BilateralFilterDepthThreshold"),
1.0f,
TEXT("Scale applied to depth differences used to weigh sample contributions when filtering and upsampling first person self shadows. A higher value makes the result softer but may lead to leaking of light/shadow across geometric edges."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarFirstPersonSelfShadowRawFullResolution(
TEXT("r.FirstPerson.SelfShadow.RawFullResolution"),
0,
TEXT("Runs first person self shadows at full resolution and without filtering, resulting in pixel perfect shadows. This mostly serves as a ground truth to compare the half-resolution shadow to."),
ECVF_RenderThreadSafe);
enum class EFPLightSourceShape
{
Directional,
Point,
Rect,
MAX
};
class FFirstPersonSelfShadowInputsDownsamplePS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FFirstPersonSelfShadowInputsDownsamplePS);
SHADER_USE_PARAMETER_STRUCT(FFirstPersonSelfShadowInputsDownsamplePS, FGlobalShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate)
SHADER_PARAMETER(uint32, CheckerboardMode)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return HasFirstPersonGBufferBit(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FFirstPersonSelfShadowInputsDownsamplePS, "/Engine/Private/FirstPersonSelfShadow.usf", "FirstPersonSelfShadowDownsamplePS", SF_Pixel);
class FFirstPersonSelfShadowTracingPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FFirstPersonSelfShadowTracingPS, Global)
SHADER_USE_PARAMETER_STRUCT(FFirstPersonSelfShadowTracingPS, FGlobalShader);
class FSourceShapeDim : SHADER_PERMUTATION_ENUM_CLASS("LIGHT_SOURCE_SHAPE", EFPLightSourceShape);
class FRawFullResolution : SHADER_PERMUTATION_BOOL("RAW_FULL_RESOLUTION_FP_SELF_SHADOWS");
using FPermutationDomain = TShaderPermutationDomain<FSourceShapeDim, FRawFullResolution>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FDeferredLightUniformStruct, DeferredLight)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, DownsampledInputsTexture)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, DownsampledDepthTexture)
SHADER_PARAMETER(float, HZBMaxTraceDistance)
SHADER_PARAMETER(float, HZBMaxIterations)
SHADER_PARAMETER(float, HZBRelativeDepthThickness)
SHADER_PARAMETER(uint32, HZBMinimumTracingThreadOccupancy)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return HasFirstPersonGBufferBit(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FFirstPersonSelfShadowTracingPS, "/Engine/Private/FirstPersonSelfShadow.usf", "FirstPersonSelfShadowTracePS", SF_Pixel);
class FFirstPersonSelfShadowBlurPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FFirstPersonSelfShadowBlurPS, Global)
SHADER_USE_PARAMETER_STRUCT(FFirstPersonSelfShadowBlurPS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputsTexture)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, DepthTexture)
SHADER_PARAMETER(float, InvDepthThreshold)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return HasFirstPersonGBufferBit(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FFirstPersonSelfShadowBlurPS, "/Engine/Private/FirstPersonSelfShadow.usf", "FirstPersonSelfShadowBlurPS", SF_Pixel);
class FFirstPersonSelfShadowUpsamplePS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FFirstPersonSelfShadowUpsamplePS, Global)
SHADER_USE_PARAMETER_STRUCT(FFirstPersonSelfShadowUpsamplePS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, DownsampledDepthTexture)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, ShadowFactorsTexture)
SHADER_PARAMETER(FVector2f, DownsampledInvBufferSize)
SHADER_PARAMETER(float, InvDepthThreshold)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return HasFirstPersonGBufferBit(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FFirstPersonSelfShadowUpsamplePS, "/Engine/Private/FirstPersonSelfShadow.usf", "FirstPersonSelfShadowUpsamplePS", SF_Pixel);
bool ShouldRenderFirstPersonSelfShadow(const FSceneViewFamily& ViewFamily)
{
return ViewFamily.EngineShowFlags.DirectLighting
&& CVarFirstPersonSelfShadow.GetValueOnRenderThread() != 0
&& HasFirstPersonGBufferBit(ViewFamily.GetShaderPlatform());
}
bool LightCastsFirstPersonSelfShadow(const FLightSceneInfo& LightSceneInfo)
{
const bool bCastsShadow = LightSceneInfo.Proxy->CastsDynamicShadow();
const bool bSelfShadowEnabled = CVarFirstPersonSelfShadow.GetValueOnRenderThread() != 0;
const int32 EnabledSelfShadowLightTypes = FMath::Clamp(CVarFirstPersonSelfShadowLightTypes.GetValueOnRenderThread(), 0, 2);
const bool bLocalLightSelfShadowEnabled = EnabledSelfShadowLightTypes > 0;
const bool bDirectionalLightSelfShadowEnabled = EnabledSelfShadowLightTypes != 1;
const bool bIsDirectionalLight = LightSceneInfo.Type == LightType_Directional;
return bCastsShadow && bSelfShadowEnabled && ((bIsDirectionalLight && bDirectionalLightSelfShadowEnabled) || (!bIsDirectionalLight && bLocalLightSelfShadowEnabled));
}
static bool IsViewFirstPersonSelfShadowRelevant(const FViewInfo& View, const FFirstPersonViewBounds& FirstPersonViewBounds, const FLightSceneInfo& LightSceneInfo)
{
// First person primitives can be expected to all be very close to one another, so lights will usually either fully affect all of them or none,
// which is why we use a single FBoxSphereBounds object for all first person primitives visible in the view.
return FirstPersonViewBounds.bHasFirstPersonPrimitives && LightSceneInfo.ShouldRenderLight(View) && LightSceneInfo.Proxy->AffectsBounds(FirstPersonViewBounds.FirstPersonBounds);
}
bool ShouldRenderFirstPersonSelfShadowForLight(const FSceneRendererBase& SceneRenderer, const FSceneViewFamily& ViewFamily, const TArray<FViewInfo>& Views, const FLightSceneInfo& LightSceneInfo)
{
const bool bSelfShadowSupported = ShouldRenderFirstPersonSelfShadow(ViewFamily);
const bool bLightCastsSelfShadow = LightCastsFirstPersonSelfShadow(LightSceneInfo);
if (bSelfShadowSupported && bLightCastsSelfShadow)
{
const FFirstPersonSceneExtensionRenderer* FPRenderer = SceneRenderer.GetSceneExtensionsRenderers().GetRendererPtr<FFirstPersonSceneExtensionRenderer>();
if (ensure(FPRenderer))
{
// Return true if any view is relevant, not only if all views are relevant. We can filter out individual views later.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
if (IsViewFirstPersonSelfShadowRelevant(Views[ViewIndex], FPRenderer->GetFirstPersonViewBounds(Views[ViewIndex]), LightSceneInfo))
{
return true;
}
}
}
}
return false;
}
FFirstPersonSelfShadowInputs CreateFirstPersonSelfShadowInputs(FRDGBuilder& GraphBuilder, const TArray<FViewInfo>& Views, const FMinimalSceneTextures& SceneTextures)
{
FFirstPersonSelfShadowInputs Result{};
Result.SceneTextures = &SceneTextures;
const bool bRawFullResolution = CVarFirstPersonSelfShadowRawFullResolution.GetValueOnRenderThread() != 0;
if (bRawFullResolution)
{
// No need to downsample anything in this case; just store a pointer to the scene textures for later
return Result;
}
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
{
const FViewInfo& View = Views[ViewIndex];
auto& DownsampledTextures = Result.DownsampledInputs.AddDefaulted_GetRef();
DownsampledTextures.Resolution = GetDownscaledExtent(View.ViewRect.Size(), FIntPoint(2));
DownsampledTextures.Normals = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(DownsampledTextures.Resolution, PF_R8G8B8A8, FClearValueBinding::Black, TexCreate_RenderTargetable | TexCreate_ShaderResource), TEXT("FPDownsampledNormals"));
DownsampledTextures.DepthStencil = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(DownsampledTextures.Resolution, PF_DepthStencil, FClearValueBinding::DepthFar, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource), TEXT("FPDownsampledDepthStencil"));
// Remap cvar (closest/furthest/CB depth) to min/max/CB for the shader.
uint32 CheckerboardMode = 0;
switch (FMath::Clamp(CVarFirstPersonSelfShadowCheckerboardMode.GetValueOnRenderThread(), 0, 2))
{
case 0: CheckerboardMode = (bool)ERHIZBuffer::IsInverted ? 1 : 0; break;
case 1: CheckerboardMode = (bool)ERHIZBuffer::IsInverted ? 0 : 1; break;
case 2: CheckerboardMode = 2; break;
}
FFirstPersonSelfShadowInputsDownsamplePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FFirstPersonSelfShadowInputsDownsamplePS::FParameters>();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View);
PassParameters->CheckerboardMode = CheckerboardMode;
PassParameters->RenderTargets[0] = FRenderTargetBinding(DownsampledTextures.Normals, ERenderTargetLoadAction::ENoAction);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(DownsampledTextures.DepthStencil, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthWrite_StencilNop);
TShaderMapRef<FFirstPersonSelfShadowInputsDownsamplePS> PixelShader(View.ShaderMap);
// Set all non-first person pixels to the far depth value in the downsampled depth buffer so we can use the hardware to only do work for first person pixels.
FPixelShaderUtils::AddFullscreenPass(
GraphBuilder,
View.ShaderMap,
RDG_EVENT_NAME("FPSelfShadowsDownsampleDepthNormal (View: %i)", ViewIndex),
PixelShader,
PassParameters,
FIntRect(FIntPoint::ZeroValue, DownsampledTextures.Resolution),
nullptr /*BlendState*/,
nullptr /*RasterizerState*/,
TStaticDepthStencilState<true, CF_Always>::GetRHI());
}
return Result;
}
void RenderFirstPersonSelfShadow(FRDGBuilder& GraphBuilder, const FSceneRendererBase& SceneRenderer, const TArray<FViewInfo>& Views, FRDGTextureRef ScreenShadowMaskTexture, const FFirstPersonSelfShadowInputs& Inputs, const FLightSceneInfo& LightSceneInfo)
{
static_assert((bool)ERHIZBuffer::IsInverted, "Inverted depth buffer is assumed because FPixelShaderUtils::AddFullscreenPass is drawing at depth 0!");
check(ScreenShadowMaskTexture);
check(LightCastsFirstPersonSelfShadow(LightSceneInfo));
const FFirstPersonSceneExtensionRenderer* FPRenderer = SceneRenderer.GetSceneExtensionsRenderers().GetRendererPtr<FFirstPersonSceneExtensionRenderer>();
if (!ensure(FPRenderer))
{
return;
}
const FLightSceneProxy* RESTRICT LightProxy = LightSceneInfo.Proxy;
const ELightComponentType LightType = (ELightComponentType)LightProxy->GetLightType();
const bool bIsRadial = LightType != LightType_Directional;
const bool bRawFullResolution = CVarFirstPersonSelfShadowRawFullResolution.GetValueOnRenderThread() != 0;
const float InvDepthThreshold = 1.0f / FMath::Max(UE_SMALL_NUMBER, CVarFirstPersonSelfShadowBilateralFilterDepthThreshold.GetValueOnRenderThread());
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
{
const FViewInfo& View = Views[ViewIndex];
if (!IsViewFirstPersonSelfShadowRelevant(View, FPRenderer->GetFirstPersonViewBounds(View), LightSceneInfo))
{
continue;
}
const FIntPoint DownsampledResolution = !bRawFullResolution ? Inputs.DownsampledInputs[ViewIndex].Resolution : FIntPoint::ZeroValue;
FRDGTextureRef FPShadowsTexture = nullptr;
FRDGTextureRef FPDenoisedShadowsTexture = nullptr;
if (!bRawFullResolution)
{
FPShadowsTexture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(DownsampledResolution, PF_R8, FClearValueBinding::White, TexCreate_RenderTargetable | TexCreate_ShaderResource), TEXT("FPShadows"));
FPDenoisedShadowsTexture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(DownsampledResolution, PF_R8, FClearValueBinding::White, TexCreate_RenderTargetable | TexCreate_ShaderResource), TEXT("FPDenoisedShadows"));
}
auto* DeferredLightStruct = GraphBuilder.AllocParameters<FDeferredLightUniformStruct>();
*DeferredLightStruct = GetDeferredLightParameters(View, LightSceneInfo);
TRDGUniformBufferRef<FDeferredLightUniformStruct> DeferredLightUniformBuffer = GraphBuilder.CreateUniformBuffer(DeferredLightStruct);
// Trace screen space rays
{
check(IsHZBValid(View, EHZBType::ClosestHZB, true /*bCheckedIfProduced*/));
FFirstPersonSelfShadowTracingPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FFirstPersonSelfShadowTracingPS::FParameters>();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = Inputs.SceneTextures->UniformBuffer;
PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View);
PassParameters->DeferredLight = DeferredLightUniformBuffer;
PassParameters->HZBParameters = GetHZBParameters(GraphBuilder, View, EHZBType::ClosestHZB);
PassParameters->HZBMaxTraceDistance = FMath::Max(0.0f, CVarFirstPersonSelfShadowMaxTraceDistance.GetValueOnRenderThread());
PassParameters->HZBMaxIterations = FMath::Max(1.0f, CVarFirstPersonSelfShadowMaxIterations.GetValueOnRenderThread());
PassParameters->HZBRelativeDepthThickness = FMath::Max(UE_SMALL_NUMBER, CVarFirstPersonSelfShadowRelativeDepthThickness.GetValueOnRenderThread());
PassParameters->HZBMinimumTracingThreadOccupancy = FMath::Max(0, CVarFirstPersonSelfShadowMinimumHZBTraceOccupancy.GetValueOnRenderThread());
if (!bRawFullResolution)
{
PassParameters->DownsampledInputsTexture = GraphBuilder.CreateSRV(Inputs.DownsampledInputs[ViewIndex].Normals);
PassParameters->DownsampledDepthTexture = GraphBuilder.CreateSRV(Inputs.DownsampledInputs[ViewIndex].DepthStencil);
PassParameters->RenderTargets[0] = FRenderTargetBinding(FPShadowsTexture, ERenderTargetLoadAction::ENoAction);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(Inputs.DownsampledInputs[ViewIndex].DepthStencil, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthRead_StencilNop);
}
else
{
PassParameters->DownsampledInputsTexture = nullptr;
PassParameters->DownsampledDepthTexture = nullptr;
PassParameters->RenderTargets[0] = FRenderTargetBinding(ScreenShadowMaskTexture, ERenderTargetLoadAction::ELoad);
}
FFirstPersonSelfShadowTracingPS::FPermutationDomain PermutationVector;
PermutationVector.Set<FFirstPersonSelfShadowTracingPS::FSourceShapeDim>(bIsRadial ? (LightProxy->IsRectLight() ? EFPLightSourceShape::Rect : EFPLightSourceShape::Point) : EFPLightSourceShape::Directional);
PermutationVector.Set<FFirstPersonSelfShadowTracingPS::FRawFullResolution>(bRawFullResolution);
TShaderMapRef<FFirstPersonSelfShadowTracingPS> PixelShader(View.ShaderMap, PermutationVector);
FRHIBlendState* BlendState = nullptr;
FRHIDepthStencilState* DepthStencilState = nullptr;
if (!bRawFullResolution)
{
// Default blend state, but early out on depth such that we only process first person pixels.
BlendState = TStaticBlendState<>::GetRHI();
DepthStencilState = TStaticDepthStencilState<false, CF_NotEqual>::GetRHI();
}
else
{
// Use the shadow projection blend state when running at full resolution as we directly render into the screen shadow mask texture.
BlendState = FProjectedShadowInfo::GetBlendStateForProjection(LightSceneInfo.GetDynamicShadowMapChannel(), LightType == LightType_Directional, false, false, false);
DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
}
FPixelShaderUtils::AddFullscreenPass(
GraphBuilder,
View.ShaderMap,
RDG_EVENT_NAME("FirstPersonSelfShadowTracing: %s (View: %i)", *LightProxy->GetOwnerNameOrLabel(), ViewIndex),
PixelShader,
PassParameters,
!bRawFullResolution ? FIntRect(FIntPoint::ZeroValue, DownsampledResolution) : View.ViewRect,
BlendState,
nullptr /*RasterizerState*/,
DepthStencilState);
}
// Apply a 3x3 blur with some contact hardening depending on shadow caster distance. This helps with achieving a nicer upsampled shadow while still giving some small scale details.
if (!bRawFullResolution)
{
FFirstPersonSelfShadowBlurPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FFirstPersonSelfShadowBlurPS::FParameters>();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->InputsTexture = GraphBuilder.CreateSRV(FPShadowsTexture);
PassParameters->DepthTexture = GraphBuilder.CreateSRV(Inputs.DownsampledInputs[ViewIndex].DepthStencil);
PassParameters->InvDepthThreshold = InvDepthThreshold;
PassParameters->RenderTargets[0] = FRenderTargetBinding(FPDenoisedShadowsTexture, ERenderTargetLoadAction::ENoAction);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(Inputs.DownsampledInputs[ViewIndex].DepthStencil, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthRead_StencilNop);
TShaderMapRef<FFirstPersonSelfShadowBlurPS> PixelShader(View.ShaderMap);
// Default blend state, but early out on depth such that we only process first person pixels.
FPixelShaderUtils::AddFullscreenPass(
GraphBuilder,
View.ShaderMap,
RDG_EVENT_NAME("FirstPersonSelfShadowBlur: %s (View: %i)", *LightProxy->GetOwnerNameOrLabel(), ViewIndex),
PixelShader,
PassParameters,
FIntRect(FIntPoint::ZeroValue, DownsampledResolution),
nullptr /*BlendState*/,
nullptr /*RasterizerState*/,
TStaticDepthStencilState<false, CF_NotEqual>::GetRHI());
}
// Upsample into ScreenShadowMaskTexture
if (!bRawFullResolution)
{
FFirstPersonSelfShadowUpsamplePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FFirstPersonSelfShadowUpsamplePS::FParameters>();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = Inputs.SceneTextures->UniformBuffer;
PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View);
PassParameters->DownsampledDepthTexture = GraphBuilder.CreateSRV(Inputs.DownsampledInputs[ViewIndex].DepthStencil);
PassParameters->ShadowFactorsTexture = GraphBuilder.CreateSRV(FPDenoisedShadowsTexture);
PassParameters->DownsampledInvBufferSize = FVector2f(1.0f) / FVector2f(DownsampledResolution);
PassParameters->InvDepthThreshold = InvDepthThreshold;
PassParameters->RenderTargets[0] = FRenderTargetBinding(ScreenShadowMaskTexture, ERenderTargetLoadAction::ELoad);
TShaderMapRef<FFirstPersonSelfShadowUpsamplePS> PixelShader(View.ShaderMap);
FRHIBlendState* BlendState = FProjectedShadowInfo::GetBlendStateForProjection(LightSceneInfo.GetDynamicShadowMapChannel(), LightType == LightType_Directional, false, false, false);
FPixelShaderUtils::AddFullscreenPass(
GraphBuilder,
View.ShaderMap,
RDG_EVENT_NAME("FirstPersonSelfShadowUpsample: %s (View: %i)", *LightProxy->GetOwnerNameOrLabel(), ViewIndex),
PixelShader,
PassParameters,
View.ViewRect,
BlendState);
}
}
}