// 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 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 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 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 CVarFirstPersonSelfShadowMaxTraceDistance( TEXT("r.FirstPerson.SelfShadow.MaxTraceDistance"), 100.0f, TEXT("Maximum world space trace distance for shadow rays."), ECVF_RenderThreadSafe); static TAutoConsoleVariable 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 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 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 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 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; 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& Views, const FLightSceneInfo& LightSceneInfo) { const bool bSelfShadowSupported = ShouldRenderFirstPersonSelfShadow(ViewFamily); const bool bLightCastsSelfShadow = LightCastsFirstPersonSelfShadow(LightSceneInfo); if (bSelfShadowSupported && bLightCastsSelfShadow) { const FFirstPersonSceneExtensionRenderer* FPRenderer = SceneRenderer.GetSceneExtensionsRenderers().GetRendererPtr(); 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& 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(); 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 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::GetRHI()); } return Result; } void RenderFirstPersonSelfShadow(FRDGBuilder& GraphBuilder, const FSceneRendererBase& SceneRenderer, const TArray& 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(); 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(); *DeferredLightStruct = GetDeferredLightParameters(View, LightSceneInfo); TRDGUniformBufferRef DeferredLightUniformBuffer = GraphBuilder.CreateUniformBuffer(DeferredLightStruct); // Trace screen space rays { check(IsHZBValid(View, EHZBType::ClosestHZB, true /*bCheckedIfProduced*/)); FFirstPersonSelfShadowTracingPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); 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(bIsRadial ? (LightProxy->IsRectLight() ? EFPLightSourceShape::Rect : EFPLightSourceShape::Point) : EFPLightSourceShape::Directional); PermutationVector.Set(bRawFullResolution); TShaderMapRef 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::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::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(); 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 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::GetRHI()); } // Upsample into ScreenShadowMaskTexture if (!bRawFullResolution) { FFirstPersonSelfShadowUpsamplePS::FParameters* PassParameters = GraphBuilder.AllocParameters(); 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 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); } } }