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

692 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LightShaftRendering.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "LightSceneProxy.h"
#include "ScreenPass.h"
#include "PipelineStateCache.h"
#include "PostProcess/SceneRenderTargets.h"
#include "PostProcess/TemporalAA.h"
#include "TranslucentRendering.h"
#include "SceneTextureParameters.h"
#include "ScenePrivate.h"
#include "DeferredShadingRenderer.h"
#include "RenderCore.h"
int32 GLightShafts = 1;
static FAutoConsoleVariableRef CVarLightShaftQuality(
TEXT("r.LightShaftQuality"),
GLightShafts,
TEXT("Defines the light shaft quality (mobile and non mobile).\n")
TEXT(" 0: off\n")
TEXT(" 1: on (default)"),
ECVF_Scalability | ECVF_RenderThreadSafe);
int32 GLightShaftAllowTAA = 1;
static FAutoConsoleVariableRef CVarLightAllowTAA(
TEXT("r.LightShaftAllowTAA"),
GLightShaftAllowTAA,
TEXT("Allows temporal filtering for lightshafts.\n")
TEXT(" 0: off\n")
TEXT(" 1: on (default)"),
ECVF_Scalability | ECVF_RenderThreadSafe);
int32 GLightShaftDownsampleFactor = 2;
static FAutoConsoleVariableRef CVarCacheLightShaftDownsampleFactor(
TEXT("r.LightShaftDownSampleFactor"),
GLightShaftDownsampleFactor,
TEXT("Downsample factor for light shafts. range: 1..8"),
ECVF_RenderThreadSafe);
int32 GLightShaftRenderAfterDOF = 0;
static FAutoConsoleVariableRef CVarRenderLightshaftsAfterDOF(
TEXT("r.LightShaftRenderToSeparateTranslucency"),
GLightShaftRenderAfterDOF,
TEXT("If enabled, light shafts will be rendered to the separate translucency buffer.\n")
TEXT("This ensures postprocess materials with BL_BeforeTranslucnecy are applied before light shafts"),
ECVF_RenderThreadSafe);
int32 GLightShaftBlurPasses = 3;
static FAutoConsoleVariableRef CVarCacheLightShaftBlurPasses(
TEXT("r.LightShaftBlurPasses"),
GLightShaftBlurPasses,
TEXT("Number of light shaft blur passes."),
ECVF_RenderThreadSafe);
float GLightShaftFirstPassDistance = .1f;
static FAutoConsoleVariableRef CVarCacheLightShaftFirstPassDistance(
TEXT("r.LightShaftFirstPassDistance"),
GLightShaftFirstPassDistance,
TEXT("Fraction of the distance to the light to blur on the first radial blur pass."),
ECVF_RenderThreadSafe);
// Must touch LightShaftShader.usf to propagate a change
int32 GLightShaftBlurNumSamples = 12;
static FAutoConsoleVariableRef CVarCacheLightShaftNumSamples(
TEXT("r.LightShaftNumSamples"),
GLightShaftBlurNumSamples,
TEXT("Number of samples per light shaft radial blur pass. Also affects how quickly the blur distance increases with each pass."),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
int32 GetLightShaftDownsampleFactor()
{
return FMath::Clamp(GLightShaftDownsampleFactor, 1, 8);
}
FVector4f GetLightScreenPosition(const FViewInfo& View, const FLightSceneProxy& LightSceneProxy)
{
return (FVector4f)View.WorldToScreen(LightSceneProxy.GetLightPositionForLightShafts(View.ViewMatrices.GetViewOrigin()));
}
FMobileLightShaftInfo GetMobileLightShaftInfo(const FViewInfo& View, const FLightSceneInfo& LightSceneInfo)
{
const FLightSceneProxy& LightSceneProxy = *LightSceneInfo.Proxy;
const FLinearColor BloomScale(LightSceneInfo.BloomScale, LightSceneInfo.BloomScale, LightSceneInfo.BloomScale, 1.0f);
const FVector4f LightScreenPosition = GetLightScreenPosition(View, LightSceneProxy);
FMobileLightShaftInfo LightShaft;
LightShaft.Center = FVector2D(LightScreenPosition.X / LightScreenPosition.W, LightScreenPosition.Y / LightScreenPosition.W);
LightShaft.ColorMask = LightSceneInfo.BloomTint;
LightShaft.ColorApply = LightSceneInfo.BloomTint;
LightShaft.ColorMask *= BloomScale;
LightShaft.ColorApply *= BloomScale;
LightShaft.BloomMaxBrightness = LightSceneInfo.BloomMaxBrightness;
return LightShaft;
}
enum class ELightShaftBloomOutput
{
SceneColor,
SeparateTranslucency
};
ELightShaftBloomOutput GetLightShaftBloomOutput(const FSceneViewFamily& ViewFamily)
{
return (ViewFamily.AllowTranslucencyAfterDOF() && GLightShaftRenderAfterDOF) ? ELightShaftBloomOutput::SeparateTranslucency : ELightShaftBloomOutput::SceneColor;
}
bool ShouldRenderLightShafts(const FSceneViewFamily& ViewFamily)
{
return GLightShafts
&& ViewFamily.EngineShowFlags.LightShafts
&& ViewFamily.EngineShowFlags.Lighting
&& !ViewFamily.UseDebugViewPS()
&& !ViewFamily.EngineShowFlags.VisualizeDOF
&& !ViewFamily.EngineShowFlags.VisualizeBuffer
&& !ViewFamily.EngineShowFlags.VisualizeHDR
&& !ViewFamily.EngineShowFlags.VisualizeMotionBlur;
}
bool ShouldRenderLightShaftsForLight(const FViewInfo& View, const FLightSceneProxy& LightSceneProxy)
{
if (LightSceneProxy.GetLightType() == LightType_Directional)
{
// Don't render if the light's origin is behind the view.
return GetLightScreenPosition(View, LightSceneProxy).W > 0.0f;
}
return false;
}
enum class ELightShaftTechnique
{
Occlusion,
Bloom
};
BEGIN_SHADER_PARAMETER_STRUCT(FLightShaftPixelShaderParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FVector4f, UVMinMax)
SHADER_PARAMETER(FVector4f, AspectRatioAndInvAspectRatio)
SHADER_PARAMETER(FVector4f, LightShaftParameters)
SHADER_PARAMETER(FVector4f, BloomTintAndThreshold)
SHADER_PARAMETER(FVector2f, TextureSpaceBlurOrigin)
SHADER_PARAMETER(float, BloomMaxBrightness)
END_SHADER_PARAMETER_STRUCT()
FLightShaftPixelShaderParameters GetLightShaftParameters(
const FViewInfo& View,
const FLightSceneInfo& LightSceneInfo,
const FScreenPassTextureViewport& SceneViewport,
const FScreenPassTextureViewport& LightShaftViewport)
{
const FLightSceneProxy& LightSceneProxy = *LightSceneInfo.Proxy;
const FScreenPassTextureViewportParameters SceneViewportParameters = GetScreenPassTextureViewportParameters(SceneViewport);
const FScreenPassTextureViewportParameters LightShaftParameters = GetScreenPassTextureViewportParameters(LightShaftViewport);
const FVector2D LightShaftRectToExtentRatio = LightShaftViewport.GetRectToExtentRatio();
const FVector2f LightShaftAspectRatio(LightShaftRectToExtentRatio.X, (float)LightShaftViewport.Extent.X * LightShaftRectToExtentRatio.Y / LightShaftViewport.Extent.Y);
const FVector2f LightShaftAspectRatioInverse = FVector2f(1.0f) / LightShaftAspectRatio;
FLightShaftPixelShaderParameters Parameters;
Parameters.View = View.ViewUniformBuffer;
Parameters.AspectRatioAndInvAspectRatio = FVector4f(LightShaftAspectRatio, LightShaftAspectRatioInverse);
{
const FVector4f LightScreenPosition = GetLightScreenPosition(View, LightSceneProxy);
const float InvW = 1.0f / LightScreenPosition.W;
const float Y = (GProjectionSignY > 0.0f) ? LightScreenPosition.Y : 1.0f - LightScreenPosition.Y;
const FVector2f ScreenSpaceBlurOrigin(
View.ViewRect.Min.X + (0.5f + LightScreenPosition.X * 0.5f * InvW) * View.ViewRect.Width(),
View.ViewRect.Min.Y + (0.5f - Y * 0.5f * InvW) * View.ViewRect.Height());
Parameters.TextureSpaceBlurOrigin = ScreenSpaceBlurOrigin * SceneViewportParameters.ExtentInverse * LightShaftAspectRatioInverse;
}
Parameters.UVMinMax = FVector4f(LightShaftParameters.UVViewportBilinearMin, LightShaftParameters.UVViewportBilinearMax);
const FLinearColor BloomTint = LightSceneInfo.BloomTint;
Parameters.BloomTintAndThreshold = FVector4f(BloomTint.R, BloomTint.G, BloomTint.B, LightSceneInfo.BloomThreshold);
Parameters.BloomMaxBrightness = LightSceneInfo.BloomMaxBrightness;
float OcclusionMaskDarkness;
float OcclusionDepthRange;
LightSceneProxy.GetLightShaftOcclusionParameters(OcclusionMaskDarkness, OcclusionDepthRange);
Parameters.LightShaftParameters = FVector4f(1.0f / OcclusionDepthRange, LightSceneInfo.BloomScale, 1, OcclusionMaskDarkness);
return Parameters;
}
class FDownsampleLightShaftsVertexShader : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FDownsampleLightShaftsVertexShader);
SHADER_USE_PARAMETER_STRUCT_WITH_LEGACY_BASE(FDownsampleLightShaftsVertexShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
};
IMPLEMENT_GLOBAL_SHADER(FDownsampleLightShaftsVertexShader, "/Engine/Private/LightShaftShader.usf", "DownsampleLightShaftsVertexMain", SF_Vertex);
class FDownsampleLightShaftsPixelShader : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FDownsampleLightShaftsPixelShader);
SHADER_USE_PARAMETER_STRUCT(FDownsampleLightShaftsPixelShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FLightShaftPixelShaderParameters, LightShafts)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
class FOcclusionTerm : SHADER_PERMUTATION_BOOL("OCCLUSION_TERM");
using FPermutationDomain = TShaderPermutationDomain<FOcclusionTerm>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
};
IMPLEMENT_GLOBAL_SHADER(FDownsampleLightShaftsPixelShader, "/Engine/Private/LightShaftShader.usf", "DownsampleLightShaftsPixelMain", SF_Pixel);
class FBlurLightShaftsPixelShader : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FBlurLightShaftsPixelShader);
SHADER_USE_PARAMETER_STRUCT(FBlurLightShaftsPixelShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FLightShaftPixelShaderParameters, LightShafts)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceTexture)
SHADER_PARAMETER(FVector4f, RadialBlurParameters)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("NUM_SAMPLES"), GLightShaftBlurNumSamples);
}
};
IMPLEMENT_GLOBAL_SHADER(FBlurLightShaftsPixelShader, "/Engine/Private/LightShaftShader.usf", "BlurLightShaftsMain", SF_Pixel);
class FFinishOcclusionPixelShader : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FFinishOcclusionPixelShader);
SHADER_USE_PARAMETER_STRUCT(FFinishOcclusionPixelShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FLightShaftPixelShaderParameters, LightShafts)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceTexture)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
};
IMPLEMENT_GLOBAL_SHADER(FFinishOcclusionPixelShader, "/Engine/Private/LightShaftShader.usf", "FinishOcclusionMain", SF_Pixel);
class FApplyLightShaftsPixelShader : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FApplyLightShaftsPixelShader);
SHADER_USE_PARAMETER_STRUCT(FApplyLightShaftsPixelShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FLightShaftPixelShaderParameters, LightShafts)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SourceTexture)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
};
IMPLEMENT_GLOBAL_SHADER(FApplyLightShaftsPixelShader, "/Engine/Private/LightShaftShader.usf", "ApplyLightShaftsPixelMain", SF_Pixel);
FScreenPassRenderTarget CreateLightShaftTexture(FRDGBuilder& GraphBuilder, FScreenPassTextureViewport Viewport, const TCHAR* Name)
{
const FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(Viewport.Extent, PF_FloatRGB, FClearValueBinding::White, TexCreate_ShaderResource | TexCreate_RenderTargetable);
return FScreenPassRenderTarget(GraphBuilder.CreateTexture(Desc, Name), Viewport.Rect, ERenderTargetLoadAction::ENoAction);
}
FScreenPassTexture AddDownsamplePass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FLightShaftPixelShaderParameters& LightShaftParameters,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures,
FScreenPassTextureViewport SceneViewport,
FScreenPassTextureViewport OutputViewport,
ELightComponentType LightComponentType,
ELightShaftTechnique LightShaftTechnique)
{
const FScreenPassRenderTarget Output = CreateLightShaftTexture(GraphBuilder, OutputViewport, TEXT("LightShaftDownsample"));
auto* PassParameters = GraphBuilder.AllocParameters<FDownsampleLightShaftsPixelShader::FParameters>();
PassParameters->LightShafts = LightShaftParameters;
PassParameters->SceneTextures = SceneTextures;
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
FDownsampleLightShaftsPixelShader::FPermutationDomain PixelPermutationVector;
PixelPermutationVector.Set<FDownsampleLightShaftsPixelShader::FOcclusionTerm>(LightShaftTechnique == ELightShaftTechnique::Occlusion);
TShaderMapRef<FDownsampleLightShaftsPixelShader> PixelShader(View.ShaderMap, PixelPermutationVector);
TShaderMapRef<FDownsampleLightShaftsVertexShader> VertexShader(View.ShaderMap);
AddDrawScreenPass(
GraphBuilder,
RDG_EVENT_NAME("Downsample %dx%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()),
View,
OutputViewport,
SceneViewport,
FScreenPassPipelineState(VertexShader, PixelShader),
PassParameters,
[VertexShader, PixelShader, PassParameters](FRHICommandList& RHICmdList)
{
FDownsampleLightShaftsVertexShader::FParameters VertexParameters;
VertexParameters.View = PassParameters->LightShafts.View;
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), VertexParameters);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
});
return FScreenPassTexture(Output);
}
FScreenPassTexture AddTemporalAAPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures,
FTemporalAAHistory* HistoryState,
FScreenPassTexture LightShafts)
{
if (IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && HistoryState && GLightShaftAllowTAA)
{
const FSceneTextureParameters SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, SceneTextures);
FTAAPassParameters TAAParameters(View);
TAAParameters.Pass = ETAAPassConfig::LightShaft;
TAAParameters.SceneDepthTexture = SceneTextureParameters.SceneDepthTexture;
TAAParameters.SceneVelocityTexture = SceneTextureParameters.GBufferVelocityTexture;
TAAParameters.SetupViewRect(View, GetLightShaftDownsampleFactor());
TAAParameters.SceneColorInput = LightShafts.Texture;
LightShafts.Texture = AddTemporalAAPass(GraphBuilder, View, TAAParameters, *HistoryState, HistoryState).SceneColor;
}
return LightShafts;
}
FScreenPassTexture AddRadialBlurPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FLightShaftPixelShaderParameters& LightShaftParameters,
FScreenPassTexture LightShafts)
{
const uint32 NumPasses = FMath::Max(GLightShaftBlurPasses, 0);
for (uint32 PassIndex = 0; PassIndex < NumPasses; PassIndex++)
{
const FScreenPassRenderTarget Output = CreateLightShaftTexture(GraphBuilder, FScreenPassTextureViewport(LightShafts), TEXT("LightShaftBlur"));
auto* PassParameters = GraphBuilder.AllocParameters<FBlurLightShaftsPixelShader::FParameters>();
PassParameters->LightShafts = LightShaftParameters;
PassParameters->SourceTexture = LightShafts.Texture;
PassParameters->RadialBlurParameters = FVector4f(GLightShaftBlurNumSamples, GLightShaftFirstPassDistance, PassIndex);
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
TShaderMapRef<FBlurLightShaftsPixelShader> PixelShader(View.ShaderMap);
AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("RadialBlur(%d)", PassIndex), View, FScreenPassTextureViewport(Output), FScreenPassTextureViewport(LightShafts), PixelShader, PassParameters);
LightShafts = Output;
}
return LightShafts;
}
FScreenPassTexture AddLightShaftSetupPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FLightShaftPixelShaderParameters& LightShaftParameters,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures,
FScreenPassTextureViewport SceneViewport,
FScreenPassTextureViewport LightShaftViewport,
FTemporalAAHistory* LightShaftTemporalHistory,
ELightComponentType LightComponentType,
ELightShaftTechnique LightShaftTechnique)
{
FScreenPassTexture LightShafts;
LightShafts = AddDownsamplePass(GraphBuilder, View, LightShaftParameters, SceneTextures, SceneViewport, LightShaftViewport, LightComponentType, LightShaftTechnique);
LightShafts = AddTemporalAAPass(GraphBuilder, View, SceneTextures, LightShaftTemporalHistory, LightShafts);
LightShafts = AddRadialBlurPass(GraphBuilder, View, LightShaftParameters, LightShafts);
return LightShafts;
}
void AddOcclusionTermPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FLightShaftPixelShaderParameters& LightShaftParameters,
FScreenPassTexture LightShafts,
FScreenPassRenderTarget Output)
{
auto* PassParameters = GraphBuilder.AllocParameters<FFinishOcclusionPixelShader::FParameters>();
PassParameters->LightShafts = LightShaftParameters;
PassParameters->SourceTexture = LightShafts.Texture;
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
TShaderMapRef<FFinishOcclusionPixelShader> PixelShader(View.ShaderMap);
AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("FinishOcclusionTerm"), View, FScreenPassTextureViewport(Output), FScreenPassTextureViewport(LightShafts), PixelShader, PassParameters);
}
FRDGTextureRef FDeferredShadingSceneRenderer::RenderLightShaftOcclusion(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures)
{
FScreenPassRenderTarget Output;
if (ShouldRenderLightShafts(ViewFamily))
{
RDG_EVENT_SCOPE(GraphBuilder, "LightShafts (Occlusion)");
for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt)
{
const FLightSceneInfo& LightSceneInfo = *LightIt->LightSceneInfo;
const FLightSceneProxy& LightSceneProxy = *LightSceneInfo.Proxy;
const ELightComponentType LightComponentType = static_cast<ELightComponentType>(LightSceneProxy.GetLightType());
if (LightComponentType != LightType_Directional)
{
continue;
}
float OcclusionMaskDarkness;
float OcclusionDepthRange;
const bool bEnableOcclusion = LightSceneProxy.GetLightShaftOcclusionParameters(OcclusionMaskDarkness, OcclusionDepthRange);
if (bEnableOcclusion)
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (ShouldRenderLightShaftsForLight(View, LightSceneProxy))
{
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
INC_DWORD_STAT(STAT_LightShaftsLights);
const FScreenPassTextureViewport SceneViewport(SceneTextures.Config.Extent, View.ViewRect);
const FScreenPassTextureViewport OutputViewport(GetDownscaledViewport(SceneViewport, GetLightShaftDownsampleFactor()));
const FLightShaftPixelShaderParameters LightShaftParameters = GetLightShaftParameters(View, LightSceneInfo, SceneViewport, OutputViewport);
FTemporalAAHistory* TemporalHistory = nullptr;
if (!Output.IsValid())
{
Output = CreateLightShaftTexture(GraphBuilder, OutputViewport, TEXT("LightShaftOcclusion"));
// If there are multiple views, we want to clear the texture in case some of them are not going to render any occlusion this frame
// See ShouldRenderLightShaftsForLight for the skipping logic.
Output.LoadAction = Views.Num() > 1 ? ERenderTargetLoadAction::EClear : Output.LoadAction;
}
else
{
Output.ViewRect = OutputViewport.Rect;
Output.UpdateVisualizeTextureExtent();
}
if (View.State)
{
TemporalHistory = &static_cast<FSceneViewState*>(View.State)->LightShaftOcclusionHistory;
}
FScreenPassTexture LightShafts = AddLightShaftSetupPass(
GraphBuilder,
View,
LightShaftParameters,
SceneTextures.UniformBuffer,
SceneViewport,
OutputViewport,
TemporalHistory,
LightComponentType,
ELightShaftTechnique::Occlusion);
AddOcclusionTermPass(GraphBuilder, View, LightShaftParameters, LightShafts, Output);
// Subsequent views are composited into the same output target.
Output.LoadAction = ERenderTargetLoadAction::ELoad;
}
}
}
}
}
return Output.Texture;
}
void AddLightShaftBloomPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FLightShaftPixelShaderParameters& LightShaftParameters,
FScreenPassTexture LightShafts,
FScreenPassTextureViewport OutputViewport,
FRenderTargetBinding OutputBinding)
{
auto* PassParameters = GraphBuilder.AllocParameters<FApplyLightShaftsPixelShader::FParameters>();
PassParameters->LightShafts = LightShaftParameters;
PassParameters->SourceTexture = LightShafts.Texture;
PassParameters->RenderTargets[0] = OutputBinding;
PassParameters->RenderTargets.ResolveRect = FResolveRect(OutputViewport.Rect);
const FScreenPassTextureViewport InputViewport(LightShafts);
TShaderMapRef<FScreenPassVS> VertexShader(View.ShaderMap);
TShaderMapRef<FApplyLightShaftsPixelShader> PixelShader(View.ShaderMap);
FRHIBlendState* BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One>::GetRHI();
AddDrawScreenPass(GraphBuilder, RDG_EVENT_NAME("Bloom"), View, OutputViewport, InputViewport, VertexShader, PixelShader, BlendState, PassParameters);
}
void FDeferredShadingSceneRenderer::RenderLightShaftBloom(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
FTranslucencyPassResourcesMap& OutTranslucencyResourceMap)
{
if (ShouldRenderLightShafts(ViewFamily))
{
RDG_EVENT_SCOPE(GraphBuilder, "LightShafts (Bloom)");
TBitArray<TInlineAllocator<1, SceneRenderingBitArrayAllocator>> ViewsToRender(false, Views.Num());
for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt)
{
const FLightSceneInfo& LightSceneInfo = *LightIt->LightSceneInfo;
const FLightSceneProxy& LightSceneProxy = *LightSceneInfo.Proxy;
const ELightComponentType LightComponentType = static_cast<ELightComponentType>(LightSceneProxy.GetLightType());
if (LightSceneInfo.bEnableLightShaftBloom)
{
bool bWillRenderLightShafts = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const bool bRenderLight = ShouldRenderLightShaftsForLight(View, LightSceneProxy);
ViewsToRender[ViewIndex] = bRenderLight;
bWillRenderLightShafts |= bRenderLight;
}
if (bWillRenderLightShafts)
{
// Default to scene color output.
FRDGTextureMSAA OutputTexture(SceneTextures.Color.Target);
ERenderTargetLoadAction OutputLoadAction = ERenderTargetLoadAction::ELoad;
float OutputViewRectScale = 1.0f;
// Render to separate translucency buffer instead of scene color if requested.
const ELightShaftBloomOutput BloomOutput = GetLightShaftBloomOutput(ViewFamily);
bool bUpdateViewsSeparateTranslucency = false;
if (BloomOutput == ELightShaftBloomOutput::SeparateTranslucency)
{
FTranslucencyPassResources& TranslucencyPassResources = OutTranslucencyResourceMap.Get(/* ViewIndex = */ 0, ETranslucencyPass::TPT_TranslucencyAfterDOF);
if (!TranslucencyPassResources.IsValid())
{
OutputLoadAction = ERenderTargetLoadAction::EClear;
const bool bIsModulate = false;
OutputTexture = CreatePostDOFTranslucentTexture(GraphBuilder, ETranslucencyPass::TPT_TranslucencyAfterDOF, SeparateTranslucencyDimensions, bIsModulate, ShaderPlatform);
// We will need to update views separate transluceny buffers if we have just created them.
bUpdateViewsSeparateTranslucency = true;
}
else
{
OutputTexture = TranslucencyPassResources.ColorTexture;
}
OutputViewRectScale = SeparateTranslucencyDimensions.Scale;
}
const FRenderTargetBinding OutputBinding(OutputTexture.Target, OutputTexture.Resolve, OutputLoadAction);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (ViewsToRender[ViewIndex])
{
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
INC_DWORD_STAT(STAT_LightShaftsLights);
const FScreenPassTextureViewport SceneViewport(SceneTextures.Config.Extent, View.ViewRect);
const FScreenPassTextureViewport LightShaftViewport(GetDownscaledViewport(SceneViewport, GetLightShaftDownsampleFactor()));
const FScreenPassTextureViewport OutputViewport(OutputTexture.Target, GetScaledRect(View.ViewRect, OutputViewRectScale));
const FLightShaftPixelShaderParameters LightShaftParameters = GetLightShaftParameters(View, LightSceneInfo, SceneViewport, LightShaftViewport);
FTemporalAAHistory* TemporalHistory = nullptr;
if (View.State)
{
FSceneViewState* ViewState = static_cast<FSceneViewState*>(View.State);
TUniquePtr<FTemporalAAHistory>* Entry = ViewState->LightShaftBloomHistoryRTs.Find(LightSceneProxy.GetLightComponent());
if (Entry == nullptr)
{
TemporalHistory = new FTemporalAAHistory;
ViewState->LightShaftBloomHistoryRTs.Emplace(LightSceneProxy.GetLightComponent(), TemporalHistory);
}
else
{
TemporalHistory = Entry->Get();
}
}
FScreenPassTexture LightShafts = AddLightShaftSetupPass(
GraphBuilder,
View,
LightShaftParameters,
SceneTextures.UniformBuffer,
SceneViewport,
LightShaftViewport,
TemporalHistory,
LightComponentType,
ELightShaftTechnique::Bloom);
AddLightShaftBloomPass(GraphBuilder, View, LightShaftParameters, LightShafts, OutputViewport, OutputBinding);
OutputLoadAction = ERenderTargetLoadAction::ELoad;
FTranslucencyPassResources& TranslucencyPassResources = OutTranslucencyResourceMap.Get(ViewIndex, ETranslucencyPass::TPT_TranslucencyAfterDOF);
if (bUpdateViewsSeparateTranslucency)
{
TranslucencyPassResources.ViewRect = OutputViewport.Rect;
TranslucencyPassResources.ColorTexture = OutputTexture;
}
else if(TranslucencyPassResources.IsValid())
{
if (BloomOutput == ELightShaftBloomOutput::SeparateTranslucency)
{
ensure(TranslucencyPassResources.ViewRect == OutputViewport.Rect);
}
else
{
ensure(View.ViewRect == OutputViewport.Rect);
}
}
}
}
}
}
}
}
}
// InScene is passed in, as the Scene pointer in the class itself may be null, if it was allocated without a scene.
void FSceneViewState::TrimHistoryRenderTargets(const FScene* InScene)
{
for (TMap<const ULightComponent*, TUniquePtr<FTemporalAAHistory>>::TIterator It(LightShaftBloomHistoryRTs); It; ++It)
{
bool bLightIsUsed = false;
for (auto LightIt = InScene->Lights.CreateConstIterator(); LightIt; ++LightIt)
{
const FLightSceneInfo* const LightSceneInfo = LightIt->LightSceneInfo;
if (LightSceneInfo->Proxy->GetLightComponent() == It.Key())
{
bLightIsUsed = true;
break;
}
}
if (!bLightIsUsed)
{
// Remove references to render targets for lights that are no longer in the scene
// This has to be done every frame instead of at light deregister time because the view states are not known by FScene
It.RemoveCurrent();
}
}
}