// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. /*============================================================================= FogRendering.cpp: Fog rendering implementation. =============================================================================*/ #include "RendererPrivate.h" #include "ScenePrivate.h" #include "ScreenRendering.h" #include "SceneFilterRendering.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/PostProcessTemporalAA.h" #include "SceneUtils.h" /** Tweaked values from UE3 implementation **/ const float PointLightFadeDistanceIncrease = 200; const float PointLightRadiusFadeFactor = 5; int32 GLightShafts = 1; static FAutoConsoleVariableRef CVarLightShafts( TEXT("r.LightShafts"), GLightShafts, TEXT("Whether light shafts are allowed to be rendered, defaults to 1."), ECVF_RenderThreadSafe ); int32 GLightShaftDownsampleFactor = 2; static FAutoConsoleVariableRef CVarCacheLightShaftDownsampleFactor( TEXT("r.LightShaftDownSampleFactor"), GLightShaftDownsampleFactor, TEXT("Downsample factor for light shafts. range: 1..8"), ECVF_RenderThreadSafe ); int32 GetLightShaftDownsampleFactor() { return FMath::Clamp(GLightShaftDownsampleFactor, 1, 8); } 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 ); /*----------------------------------------------------------------------------- FLightShaftPixelShaderParameters -----------------------------------------------------------------------------*/ /** Light shaft parameters that are shared between multiple pixel shaders. */ class FLightShaftPixelShaderParameters { public: void Bind(const FShaderParameterMap& ParameterMap) { TextureSpaceBlurOriginParameter.Bind(ParameterMap,TEXT("TextureSpaceBlurOrigin")); WorldSpaceBlurOriginAndRadiusParameter.Bind(ParameterMap,TEXT("WorldSpaceBlurOriginAndRadius")); LightSourceRadius.Bind(ParameterMap,TEXT("LightSourceRadius")); WorldSpaceSpotDirectionParameter.Bind(ParameterMap,TEXT("WorldSpaceSpotDirection")); SpotAnglesParameter.Bind(ParameterMap, TEXT("SpotAngles")); WorldSpaceCameraPositionParameter.Bind(ParameterMap,TEXT("WorldSpaceCameraPositionAndDistance")); UVMinMaxParameter.Bind(ParameterMap, TEXT("UVMinMax")); AspectRatioAndInvAspectRatioParameter.Bind(ParameterMap,TEXT("AspectRatioAndInvAspectRatio")); LightShaftParameters.Bind(ParameterMap, TEXT("LightShaftParameters")); BloomTintAndThresholdParameter.Bind(ParameterMap,TEXT("BloomTintAndThreshold")); DistanceFadeParameter.Bind(ParameterMap, TEXT("DistanceFade")); SourceTextureParameter.Bind(ParameterMap, TEXT("SourceTexture")); SourceTextureSamplerParameter.Bind(ParameterMap, TEXT("SourceTextureSampler")); } friend FArchive& operator<<(FArchive& Ar,FLightShaftPixelShaderParameters& Parameters) { Ar << Parameters.TextureSpaceBlurOriginParameter; Ar << Parameters.WorldSpaceBlurOriginAndRadiusParameter; Ar << Parameters.LightSourceRadius; Ar << Parameters.SpotAnglesParameter; Ar << Parameters.WorldSpaceSpotDirectionParameter; Ar << Parameters.WorldSpaceCameraPositionParameter; Ar << Parameters.UVMinMaxParameter; Ar << Parameters.AspectRatioAndInvAspectRatioParameter; Ar << Parameters.LightShaftParameters; Ar << Parameters.BloomTintAndThresholdParameter; Ar << Parameters.DistanceFadeParameter; Ar << Parameters.SourceTextureParameter; Ar << Parameters.SourceTextureSamplerParameter; return Ar; } template void SetParameters(FRHICommandList& RHICmdList, const ShaderRHIParamRef Shader, const FLightSceneInfo* LightSceneInfo, const FSceneView& View, TRefCountPtr& PassSource) { const uint32 DownsampleFactor = GetLightShaftDownsampleFactor(); FIntPoint DownSampledViewSize(FMath::FloorToInt(View.ViewRect.Width() / DownsampleFactor), FMath::FloorToInt(View.ViewRect.Height() / DownsampleFactor)); const FIntPoint FilterBufferSize = GSceneRenderTargets.GetBufferSizeXY() / DownsampleFactor; const FVector2D ViewRatioOfBuffer((float)DownSampledViewSize.X / FilterBufferSize.X, (float)DownSampledViewSize.Y / FilterBufferSize.Y); const FVector4 AspectRatioAndInvAspectRatio( ViewRatioOfBuffer.X, (float)FilterBufferSize.X * ViewRatioOfBuffer.Y / FilterBufferSize.Y, 1.0f / ViewRatioOfBuffer.X, (float)FilterBufferSize.Y / (FilterBufferSize.X * ViewRatioOfBuffer.Y)); SetShaderValue(RHICmdList, Shader, AspectRatioAndInvAspectRatioParameter, AspectRatioAndInvAspectRatio); const FVector WorldSpaceBlurOrigin = LightSceneInfo->Proxy->GetLightPositionForLightShafts(View.ViewMatrices.ViewOrigin); // Transform into texture coordinates FVector4 ProjectedBlurOrigin = View.WorldToScreen(WorldSpaceBlurOrigin); const FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); const float InvBufferSizeX = 1.0f / BufferSize.X; const float InvBufferSizeY = 1.0f / BufferSize.Y; FVector2D ScreenSpaceBlurOrigin; verify(View.ScreenToPixel(ProjectedBlurOrigin, ScreenSpaceBlurOrigin)); ScreenSpaceBlurOrigin.X *= InvBufferSizeX; ScreenSpaceBlurOrigin.Y *= InvBufferSizeY; FVector2D TextureSpaceBlurOrigin(ScreenSpaceBlurOrigin * FVector2D(AspectRatioAndInvAspectRatio.Z, AspectRatioAndInvAspectRatio.W)); SetShaderValue(RHICmdList, Shader, TextureSpaceBlurOriginParameter, TextureSpaceBlurOrigin); SetShaderValue(RHICmdList, Shader, WorldSpaceBlurOriginAndRadiusParameter, FVector4(WorldSpaceBlurOrigin, LightSceneInfo->Proxy->GetRadius())); SetShaderValue(RHICmdList, Shader, LightSourceRadius, LightSceneInfo->Proxy->GetSourceRadius()); const bool bIsSpotLight = LightSceneInfo->Proxy->GetLightType() == LightType_Spot; if (bIsSpotLight) { SetShaderValue(RHICmdList, Shader, WorldSpaceSpotDirectionParameter, LightSceneInfo->Proxy->GetDirection()); SetShaderValue(RHICmdList, Shader, SpotAnglesParameter, LightSceneInfo->Proxy->GetLightShaftConeParams()); } const float DistanceFromLight = (View.ViewMatrices.ViewOrigin - WorldSpaceBlurOrigin).Size() + PointLightFadeDistanceIncrease; SetShaderValue(RHICmdList, Shader, WorldSpaceCameraPositionParameter, FVector4(View.ViewMatrices.ViewOrigin, DistanceFromLight)); const FIntPoint DownSampledXY = View.ViewRect.Min / DownsampleFactor; const uint32 DownsampledSizeX = View.ViewRect.Width() / DownsampleFactor; const uint32 DownsampledSizeY = View.ViewRect.Height() / DownsampleFactor; // Limits for where the pixel shader is allowed to sample // Prevents reading from outside the valid region of a render target // Clamp to 1 less than the actual max, // Since the bottom-right row/column of texels will contain some unwanted values if the size of scene color is not a factor of the downsample factor float MinU, MinV, MaxU, MaxV; { MinU = DownSampledXY.X / (float)FilterBufferSize.X; MinV = DownSampledXY.Y / (float)FilterBufferSize.Y; MaxU = (float(DownSampledXY.X) + DownsampledSizeX - 1) / (float)FilterBufferSize.X; MaxV = (float(DownSampledXY.Y) + DownsampledSizeY - 1) / (float)FilterBufferSize.Y; } FVector4 UVMinMax( MinU, MinV, MaxU, MaxV ); SetShaderValue(RHICmdList, Shader, UVMinMaxParameter, UVMinMax); const FLinearColor BloomTint = LightSceneInfo->BloomTint; SetShaderValue(RHICmdList, Shader, BloomTintAndThresholdParameter, FVector4(BloomTint.R, BloomTint.G, BloomTint.B, LightSceneInfo->BloomThreshold)); float OcclusionMaskDarkness; float OcclusionDepthRange; LightSceneInfo->Proxy->GetLightShaftOcclusionParameters(OcclusionMaskDarkness, OcclusionDepthRange); const FVector4 LightShaftParameterValues(1.0f / OcclusionDepthRange, LightSceneInfo->BloomScale, 1, OcclusionMaskDarkness); SetShaderValue(RHICmdList, Shader, LightShaftParameters, LightShaftParameterValues); float DistanceFade = 0.0f; if (LightSceneInfo->Proxy->GetLightType() != LightType_Directional) { DistanceFade = FMath::Clamp(DistanceFromLight / (LightSceneInfo->Proxy->GetRadius() * PointLightRadiusFadeFactor), 0.0f, 1.0f); } SetShaderValue(RHICmdList, Shader, DistanceFadeParameter, DistanceFade); if (IsValidRef(PassSource)) { SetTextureParameter( RHICmdList, Shader, SourceTextureParameter, SourceTextureSamplerParameter, TStaticSamplerState::GetRHI(), PassSource->GetRenderTargetItem().ShaderResourceTexture ); } } private: FShaderParameter TextureSpaceBlurOriginParameter; FShaderParameter WorldSpaceBlurOriginAndRadiusParameter; FShaderParameter LightSourceRadius; FShaderParameter SpotAnglesParameter; FShaderParameter WorldSpaceSpotDirectionParameter; FShaderParameter WorldSpaceCameraPositionParameter; FShaderParameter UVMinMaxParameter; FShaderParameter AspectRatioAndInvAspectRatioParameter; FShaderParameter LightShaftParameters; FShaderParameter BloomTintAndThresholdParameter; FShaderParameter DistanceFadeParameter; FShaderResourceParameter SourceTextureParameter; FShaderResourceParameter SourceTextureSamplerParameter; }; /*----------------------------------------------------------------------------- FDownsampleLightShaftsVertexShader -----------------------------------------------------------------------------*/ class FDownsampleLightShaftsVertexShader : public FGlobalShader { DECLARE_SHADER_TYPE(FDownsampleLightShaftsVertexShader,Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } /** Default constructor. */ FDownsampleLightShaftsVertexShader() {} /** Initialization constructor. */ FDownsampleLightShaftsVertexShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { } /** Sets shader parameter values */ void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View) { FGlobalShader::SetParameters(RHICmdList, GetVertexShader(), View); } }; IMPLEMENT_SHADER_TYPE(,FDownsampleLightShaftsVertexShader,TEXT("LightShaftShader"),TEXT("DownsampleLightShaftsVertexMain"),SF_Vertex); /*----------------------------------------------------------------------------- TDownsampleLightShaftsPixelShader -----------------------------------------------------------------------------*/ template class TDownsampleLightShaftsPixelShader : public FGlobalShader { DECLARE_SHADER_TYPE(TDownsampleLightShaftsPixelShader,Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("POINT_LIGHT_SHAFTS"), (LightType == LightType_Point || LightType == LightType_Spot) ? TEXT("1") : TEXT("0")); OutEnvironment.SetDefine(TEXT("SPOT_LIGHT_SHAFTS"), (LightType == LightType_Spot) ? TEXT("1") : TEXT("0")); OutEnvironment.SetDefine(TEXT("POINT_LIGHT_RADIUS_FADE_FACTOR"), PointLightRadiusFadeFactor); OutEnvironment.SetDefine(TEXT("OCCLUSION_TERM"), (uint32)bOcclusionTerm); } /** Default constructor. */ TDownsampleLightShaftsPixelShader() {} /** Initialization constructor. */ TDownsampleLightShaftsPixelShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { LightShaftParameters.Bind(Initializer.ParameterMap); SampleOffsetsParameter.Bind(Initializer.ParameterMap,TEXT("SampleOffsets")); SceneTextureParams.Bind(Initializer.ParameterMap); } /** Serializer */ virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); Ar << LightShaftParameters; Ar << SampleOffsetsParameter; Ar << SceneTextureParams; return bShaderHasOutdatedParameters; } /** Sets shader parameter values */ void SetParameters(FRHICommandList& RHICmdList, const FLightSceneInfo* LightSceneInfo, const FViewInfo& View, TRefCountPtr& PassSource) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View); LightShaftParameters.SetParameters(RHICmdList, GetPixelShader(), LightSceneInfo, View, PassSource); const FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); FVector2D SampleOffsets(1.0f / BufferSize.X, 1.0f / BufferSize.Y); SetShaderValue(RHICmdList, GetPixelShader(),SampleOffsetsParameter,SampleOffsets); SceneTextureParams.Set(RHICmdList, GetPixelShader(), View); } private: FLightShaftPixelShaderParameters LightShaftParameters; FShaderParameter SampleOffsetsParameter; FSceneTextureShaderParameters SceneTextureParams; }; #define IMPLEMENT_LSDOWNSAMPLE_PIXELSHADER_TYPE(LightType,DownsampleValue) \ typedef TDownsampleLightShaftsPixelShader TDownsampleLightShaftsPixelShader##LightType##DownsampleValue; \ IMPLEMENT_SHADER_TYPE(template<>,TDownsampleLightShaftsPixelShader##LightType##DownsampleValue,TEXT("LightShaftShader"),TEXT("DownsampleLightShaftsPixelMain"),SF_Pixel); IMPLEMENT_LSDOWNSAMPLE_PIXELSHADER_TYPE(LightType_Point, true); IMPLEMENT_LSDOWNSAMPLE_PIXELSHADER_TYPE(LightType_Spot, true); IMPLEMENT_LSDOWNSAMPLE_PIXELSHADER_TYPE(LightType_Directional, true); IMPLEMENT_LSDOWNSAMPLE_PIXELSHADER_TYPE(LightType_Point, false); IMPLEMENT_LSDOWNSAMPLE_PIXELSHADER_TYPE(LightType_Spot, false); IMPLEMENT_LSDOWNSAMPLE_PIXELSHADER_TYPE(LightType_Directional, false); /*----------------------------------------------------------------------------- FBlurLightShaftsPixelShader -----------------------------------------------------------------------------*/ class FBlurLightShaftsPixelShader : public FGlobalShader { DECLARE_SHADER_TYPE(FBlurLightShaftsPixelShader,Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("NUM_SAMPLES"), GLightShaftBlurNumSamples); } /** Default constructor. */ FBlurLightShaftsPixelShader() {} /** Initialization constructor. */ FBlurLightShaftsPixelShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { RadialBlurParameters.Bind(Initializer.ParameterMap, TEXT("RadialBlurParameters")); LightShaftParameters.Bind(Initializer.ParameterMap); } /** Serializer */ virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); Ar << RadialBlurParameters; Ar << LightShaftParameters; return bShaderHasOutdatedParameters; } /** Sets shader parameter values */ void SetParameters(FRHICommandList& RHICmdList, const FLightSceneInfo* LightSceneInfo, const FViewInfo& View, int32 PassIndex, TRefCountPtr& PassSource) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View); LightShaftParameters.SetParameters(RHICmdList, GetPixelShader(), LightSceneInfo, View, PassSource); const FVector4 Parameters(GLightShaftBlurNumSamples, GLightShaftFirstPassDistance, PassIndex); SetShaderValue(RHICmdList, GetPixelShader(), RadialBlurParameters, Parameters); } private: FShaderParameter RadialBlurParameters; FLightShaftPixelShaderParameters LightShaftParameters; }; IMPLEMENT_SHADER_TYPE(,FBlurLightShaftsPixelShader,TEXT("LightShaftShader"),TEXT("BlurLightShaftsMain"),SF_Pixel); /*----------------------------------------------------------------------------- FFinishOcclusionPixelShader -----------------------------------------------------------------------------*/ class FFinishOcclusionPixelShader : public FGlobalShader { DECLARE_SHADER_TYPE(FFinishOcclusionPixelShader,Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } /** Default constructor. */ FFinishOcclusionPixelShader() {} /** Initialization constructor. */ FFinishOcclusionPixelShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { LightShaftParameters.Bind(Initializer.ParameterMap); } /** Serializer */ virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); Ar << LightShaftParameters; return bShaderHasOutdatedParameters; } /** Sets shader parameter values */ void SetParameters(FRHICommandList& RHICmdList, const FLightSceneInfo* LightSceneInfo, const FViewInfo& View, TRefCountPtr& PassSource) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View); LightShaftParameters.SetParameters(RHICmdList, GetPixelShader(), LightSceneInfo, View, PassSource); } private: FLightShaftPixelShaderParameters LightShaftParameters; }; IMPLEMENT_SHADER_TYPE(,FFinishOcclusionPixelShader,TEXT("LightShaftShader"),TEXT("FinishOcclusionMain"),SF_Pixel); FGlobalBoundShaderState DownsamplePointLightShaftsBoundShaderState[2]; FGlobalBoundShaderState DownsampleSpotLightShaftsBoundShaderState[2]; FGlobalBoundShaderState DownsampleDirectionalLightShaftsBoundShaderState[2]; FGlobalBoundShaderState BlurLightShaftsBoundShaderState; FGlobalBoundShaderState AccumulateTermBoundShaderState; FGlobalBoundShaderState ApplyLightShaftsBoundShaderState; void AllocateOrReuseLightShaftRenderTarget(FRHICommandListImmediate& RHICmdList, TRefCountPtr& Target, const TCHAR* Name) { if (!Target) { EPixelFormat LightShaftFilterBufferFormat = PF_FloatRGB; const FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); FIntPoint LightShaftSize(FMath::Max(BufferSize.X / GetLightShaftDownsampleFactor(), 1), FMath::Max(BufferSize.Y / GetLightShaftDownsampleFactor(), 1)); FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(LightShaftSize, LightShaftFilterBufferFormat, TexCreate_None, TexCreate_RenderTargetable, false)); GRenderTargetPool.FindFreeElement(Desc, Target, Name); SetRenderTarget(RHICmdList, Target->GetRenderTargetItem().TargetableTexture, FTextureRHIRef()); RHICmdList.Clear(true, FLinearColor(0, 0, 0, 0), false, 1.0f, false, 0, FIntRect()); } } /** Generates the downsampled light shaft mask for either occlusion or bloom. This swaps input and output before returning. */ template void DownsamplePass(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, TRefCountPtr& LightShaftsSource, TRefCountPtr& LightShaftsDest) { const FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); const uint32 DownsampleFactor = GetLightShaftDownsampleFactor(); const FIntPoint FilterBufferSize = GSceneRenderTargets.GetBufferSizeXY() / DownsampleFactor; const FIntPoint DownSampledXY = View.ViewRect.Min / DownsampleFactor; const uint32 DownsampledSizeX = View.ViewRect.Width() / DownsampleFactor; const uint32 DownsampledSizeY = View.ViewRect.Height() / DownsampleFactor; SetRenderTarget(RHICmdList, LightShaftsDest->GetRenderTargetItem().TargetableTexture, FTextureRHIRef()); RHICmdList.SetViewport(DownSampledXY.X, DownSampledXY.Y, 0.0f, DownSampledXY.X + DownsampledSizeX, DownSampledXY.Y + DownsampledSizeY, 1.0f); // Set shaders and texture TShaderMapRef DownsampleLightShaftsVertexShader(View.ShaderMap); TRefCountPtr UnusedRT; switch(LightSceneInfo->Proxy->GetLightType()) { case LightType_Directional: { TShaderMapRef > DownsampleLightShaftsPixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), DownsampleDirectionalLightShaftsBoundShaderState[bDownsampleOcclusion], GFilterVertexDeclaration.VertexDeclarationRHI, *DownsampleLightShaftsVertexShader, *DownsampleLightShaftsPixelShader); DownsampleLightShaftsPixelShader->SetParameters(RHICmdList, LightSceneInfo, View, UnusedRT); } break; case LightType_Spot: { TShaderMapRef > DownsampleLightShaftsPixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), DownsampleSpotLightShaftsBoundShaderState[bDownsampleOcclusion], GFilterVertexDeclaration.VertexDeclarationRHI, *DownsampleLightShaftsVertexShader, *DownsampleLightShaftsPixelShader); DownsampleLightShaftsPixelShader->SetParameters(RHICmdList, LightSceneInfo, View, UnusedRT); } break; default: case LightType_Point: { TShaderMapRef > DownsampleLightShaftsPixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), DownsamplePointLightShaftsBoundShaderState[bDownsampleOcclusion], GFilterVertexDeclaration.VertexDeclarationRHI, *DownsampleLightShaftsVertexShader, *DownsampleLightShaftsPixelShader); DownsampleLightShaftsPixelShader->SetParameters(RHICmdList, LightSceneInfo, View, UnusedRT); } break; } DownsampleLightShaftsVertexShader->SetParameters(RHICmdList, View); // No depth tests, no backface culling. RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); // Downsample scene color and depth, and convert them into a bloom term and an occlusion masking term DrawRectangle( RHICmdList, 0, 0, DownsampledSizeX, DownsampledSizeY, View.ViewRect.Min.X, View.ViewRect.Min.Y, View.ViewRect.Width(), View.ViewRect.Height(), FIntPoint(DownsampledSizeX, DownsampledSizeY), BufferSize, *DownsampleLightShaftsVertexShader, EDRF_UseTriangleOptimization); RHICmdList.CopyToResolveTarget(LightShaftsDest->GetRenderTargetItem().TargetableTexture, LightShaftsDest->GetRenderTargetItem().ShaderResourceTexture, false, FResolveParams()); Swap(LightShaftsSource, LightShaftsDest); } /** Applies Temporal AA to the light shaft source. */ void ApplyTemporalAA( FRHICommandListImmediate& RHICmdList, FViewInfo& View, const TCHAR* HistoryRTName, /** Contains last frame's history, if non-NULL. This will be updated with the new frame's history. */ TRefCountPtr* HistoryState, /** Source mask (for either occlusion or bloom). */ TRefCountPtr& LightShaftsSource, /** Output of Temporal AA for the next step in the pipeline. */ TRefCountPtr& HistoryOutput) { if (View.FinalPostProcessSettings.AntiAliasingMethod == AAM_TemporalAA && HistoryState) { if (*HistoryState && !View.bCameraCut) { FMemMark Mark(FMemStack::Get()); FRenderingCompositePassContext CompositeContext(RHICmdList, View); FPostprocessContext Context(CompositeContext.Graph, View); // Nodes for input render targets FRenderingCompositePass* LightShaftSetup = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessInput( LightShaftsSource ) ); FRenderingCompositePass* HistoryInput = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessInput( *HistoryState ) ); // Temporal AA node FRenderingCompositePass* NodeTemporalAA = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessLightShaftTemporalAA ); // Setup inputs on Temporal AA node as the shader expects NodeTemporalAA->SetInput( ePId_Input0, LightShaftSetup ); NodeTemporalAA->SetInput( ePId_Input1, FRenderingCompositeOutputRef( HistoryInput ) ); NodeTemporalAA->SetInput( ePId_Input2, FRenderingCompositeOutputRef( HistoryInput ) ); // Reuse a render target from the pool with a consistent name, for vis purposes TRefCountPtr NewHistory; AllocateOrReuseLightShaftRenderTarget(RHICmdList, NewHistory, HistoryRTName); // Setup the output to write to the new history render target Context.FinalOutput = FRenderingCompositeOutputRef(NodeTemporalAA); Context.FinalOutput.GetOutput()->RenderTargetDesc = NewHistory->GetDesc(); Context.FinalOutput.GetOutput()->PooledRenderTarget = NewHistory; CompositeContext.Root->AddDependency(Context.FinalOutput); // Execute Temporal AA CompositeContext.Process(TEXT("LightShaftTemporalAA")); // Update the view state's render target reference with the new history *HistoryState = NewHistory; HistoryOutput = NewHistory; } else { // Use the current frame's mask for next frame's history, without invoking the Temporal AA shader *HistoryState = LightShaftsSource; HistoryOutput = LightShaftsSource; LightShaftsSource = NULL; AllocateOrReuseLightShaftRenderTarget(RHICmdList, LightShaftsSource, HistoryRTName); } } else { // Temporal AA is disabled or there is no view state - pass through HistoryOutput = LightShaftsSource; } } /** Applies screen space radial blur passes. */ void ApplyRadialBlurPasses( FRHICommandListImmediate& RHICmdList, const FViewInfo& View, const FLightSceneInfo* const LightSceneInfo, /** First pass source - this will not be overwritten. */ TRefCountPtr& FirstPassSource, /** Subsequent pass source, will also contain the final result. */ TRefCountPtr& LightShaftsSource, /** First pass dest. */ TRefCountPtr& LightShaftsDest) { TShaderMapRef ScreenVertexShader(View.ShaderMap); const FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); const uint32 DownsampleFactor = GetLightShaftDownsampleFactor(); const FIntPoint FilterBufferSize = GSceneRenderTargets.GetBufferSizeXY() / DownsampleFactor; const FIntPoint DownSampledXY = View.ViewRect.Min / DownsampleFactor; const uint32 DownsampledSizeX = View.ViewRect.Width() / DownsampleFactor; const uint32 DownsampledSizeY = View.ViewRect.Height() / DownsampleFactor; const uint32 NumPasses = FMath::Max(GLightShaftBlurPasses, 0); for (uint32 PassIndex = 0; PassIndex < NumPasses; PassIndex++) { SetRenderTarget(RHICmdList, LightShaftsDest->GetRenderTargetItem().TargetableTexture, FTextureRHIRef()); RHICmdList.SetViewport(0, 0, 0.0f, FilterBufferSize.X, FilterBufferSize.Y, 1.0f); RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); TShaderMapRef BlurLightShaftsPixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), BlurLightShaftsBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *ScreenVertexShader, *BlurLightShaftsPixelShader); TRefCountPtr& EffectiveSource = PassIndex == 0 ? FirstPassSource : LightShaftsSource; /// ? BlurLightShaftsPixelShader->SetParameters(RHICmdList, LightSceneInfo, View, PassIndex, EffectiveSource); { // Apply a radial blur to the bloom and occlusion mask DrawRectangle( RHICmdList, DownSampledXY.X, DownSampledXY.Y, DownsampledSizeX, DownsampledSizeY, DownSampledXY.X, DownSampledXY.Y, DownsampledSizeX, DownsampledSizeY, FilterBufferSize, FilterBufferSize, *ScreenVertexShader, EDRF_UseTriangleOptimization); } RHICmdList.CopyToResolveTarget(LightShaftsDest->GetRenderTargetItem().TargetableTexture, LightShaftsDest->GetRenderTargetItem().ShaderResourceTexture, false, FResolveParams()); // Swap input and output for the next pass Swap(LightShaftsSource, LightShaftsDest); } } void FinishOcclusionTerm(FRHICommandList& RHICmdList, const FViewInfo& View, const FLightSceneInfo* const LightSceneInfo, TRefCountPtr& LightShaftsSource, TRefCountPtr& LightShaftsDest) { TShaderMapRef ScreenVertexShader(View.ShaderMap); const FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); const uint32 DownsampleFactor = GetLightShaftDownsampleFactor(); const FIntPoint FilterBufferSize = GSceneRenderTargets.GetBufferSizeXY() / DownsampleFactor; const FIntPoint DownSampledXY = View.ViewRect.Min / DownsampleFactor; const uint32 DownsampledSizeX = View.ViewRect.Width() / DownsampleFactor; const uint32 DownsampledSizeY = View.ViewRect.Height() / DownsampleFactor; SetRenderTarget(RHICmdList, LightShaftsDest->GetRenderTargetItem().TargetableTexture, FTextureRHIRef()); RHICmdList.SetViewport(0, 0, 0.0f, FilterBufferSize.X, FilterBufferSize.Y, 1.0f); RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); TShaderMapRef MaskOcclusionTermPixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), AccumulateTermBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *ScreenVertexShader, *MaskOcclusionTermPixelShader); /// ? MaskOcclusionTermPixelShader->SetParameters(RHICmdList, LightSceneInfo, View, LightShaftsSource); { // Apply a radial blur to the bloom and occlusion mask DrawRectangle( RHICmdList, DownSampledXY.X, DownSampledXY.Y, DownsampledSizeX, DownsampledSizeY, DownSampledXY.X, DownSampledXY.Y, DownsampledSizeX, DownsampledSizeY, FilterBufferSize, FilterBufferSize, *ScreenVertexShader, EDRF_UseTriangleOptimization); } RHICmdList.CopyToResolveTarget(LightShaftsDest->GetRenderTargetItem().TargetableTexture, LightShaftsDest->GetRenderTargetItem().ShaderResourceTexture, false, FResolveParams()); } bool DoesViewFamilyAllowLightShafts(const FSceneViewFamily& ViewFamily) { return GLightShafts && ViewFamily.EngineShowFlags.LightShafts && ViewFamily.EngineShowFlags.Lighting && !(ViewFamily.EngineShowFlags.ShaderComplexity) && !(ViewFamily.EngineShowFlags.VisualizeAdaptiveDOF) && !(ViewFamily.EngineShowFlags.VisualizeDOF) && !(ViewFamily.EngineShowFlags.VisualizeBuffer) && !(ViewFamily.EngineShowFlags.VisualizeHDR) && !(ViewFamily.EngineShowFlags.VisualizeMotionBlur); } bool ShouldRenderLightShaftsForLight(const FViewInfo& View, const FLightSceneInfo* LightSceneInfo) { const FVector WorldSpaceBlurOrigin = LightSceneInfo->Proxy->GetLightPositionForLightShafts(View.ViewMatrices.ViewOrigin); // Transform into post projection space FVector4 ProjectedBlurOrigin = View.ViewProjectionMatrix.TransformPosition(WorldSpaceBlurOrigin); const float DistanceToBlurOrigin = (View.ViewMatrices.ViewOrigin - WorldSpaceBlurOrigin).Size() + PointLightFadeDistanceIncrease; // Don't render if the light's origin is behind the view return ProjectedBlurOrigin.W > 0.0f // Don't render point lights that have completely faded out && (LightSceneInfo->Proxy->GetLightType() == LightType_Directional || DistanceToBlurOrigin < LightSceneInfo->Proxy->GetRadius() * PointLightRadiusFadeFactor); } /** Renders light shafts. */ FLightShaftsOutput FDeferredShadingSceneRenderer::RenderLightShaftOcclusion(FRHICommandListImmediate& RHICmdList) { FLightShaftsOutput Output; if (DoesViewFamilyAllowLightShafts(ViewFamily)) { TRefCountPtr LightShafts0; TRefCountPtr LightShafts1; for (TSparseArray::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt) { const FLightSceneInfo* const LightSceneInfo = LightIt->LightSceneInfo; float OcclusionMaskDarkness; float OcclusionDepthRange; const bool bEnableOcclusion = LightSceneInfo->Proxy->GetLightShaftOcclusionParameters(OcclusionMaskDarkness, OcclusionDepthRange); if (bEnableOcclusion && LightSceneInfo->Proxy->GetLightType() == LightType_Directional) { SCOPED_DRAW_EVENT(RHICmdList, RenderLightShaftOcclusion); // Allocate light shaft render targets on demand, using the pool // Need two targets to ping pong between AllocateOrReuseLightShaftRenderTarget(RHICmdList, LightShafts0, TEXT("LightShafts0")); AllocateOrReuseLightShaftRenderTarget(RHICmdList, LightShafts1, TEXT("LightShafts1")); for (int ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) { FViewInfo& View = Views[ViewIndex]; if (ShouldRenderLightShaftsForLight(View, LightSceneInfo)) { INC_DWORD_STAT(STAT_LightShaftsLights); // Create a downsampled occlusion mask from scene depth, result will be in LightShafts0 DownsamplePass(RHICmdList, View, LightSceneInfo, LightShafts0, LightShafts1); FSceneViewState* ViewState = (FSceneViewState*)View.State; // Find the previous frame's occlusion mask TRefCountPtr* HistoryState = ViewState ? &ViewState->LightShaftOcclusionHistoryRT : NULL; TRefCountPtr HistoryOutput; // Apply temporal AA to the occlusion mask // Result will be in HistoryOutput ApplyTemporalAA(RHICmdList, View, TEXT("LSOcclusionHistory"), HistoryState, LightShafts0, HistoryOutput); // Apply radial blur passes // Send HistoryOutput in as the first pass input only, so it will not be overwritten by any subsequent passes, since it is needed for next frame ApplyRadialBlurPasses(RHICmdList, View, LightSceneInfo, HistoryOutput, LightShafts0, LightShafts1); // Apply post-blur masking FinishOcclusionTerm(RHICmdList, View, LightSceneInfo, LightShafts0, LightShafts1); //@todo - different views could have different result render targets Output.LightShaftOcclusion = LightShafts1; Output.bRendered = true; } } } } } return Output; } /*----------------------------------------------------------------------------- FApplyLightShaftsPixelShader -----------------------------------------------------------------------------*/ class FApplyLightShaftsPixelShader : public FGlobalShader { DECLARE_SHADER_TYPE(FApplyLightShaftsPixelShader,Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } /** Default constructor. */ FApplyLightShaftsPixelShader() {} /** Initialization constructor. */ FApplyLightShaftsPixelShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { SourceTextureParameter.Bind(Initializer.ParameterMap, TEXT("SourceTexture")); SourceTextureSamplerParameter.Bind(Initializer.ParameterMap, TEXT("SourceTextureSampler")); } /** Serializer */ virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); Ar << SourceTextureParameter; Ar << SourceTextureSamplerParameter; return bShaderHasOutdatedParameters; } /** Sets shader parameter values */ void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, TRefCountPtr& LightShaftOcclusion) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View); SetTextureParameter( RHICmdList, GetPixelShader(), SourceTextureParameter, SourceTextureSamplerParameter, TStaticSamplerState::GetRHI(), LightShaftOcclusion->GetRenderTargetItem().ShaderResourceTexture ); } private: FShaderResourceParameter SourceTextureParameter; FShaderResourceParameter SourceTextureSamplerParameter; }; IMPLEMENT_SHADER_TYPE(,FApplyLightShaftsPixelShader,TEXT("LightShaftShader"),TEXT("ApplyLightShaftsPixelMain"),SF_Pixel); void ApplyLightShaftBloom(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, const FLightSceneInfo* const LightSceneInfo, TRefCountPtr& LightShaftsSource) { GSceneRenderTargets.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EUninitializedColorExistingDepth, FExclusiveDepthStencil::DepthRead_StencilWrite); RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); RHICmdList.SetBlendState(TStaticBlendState::GetRHI()); RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); TShaderMapRef ScreenVertexShader(View.ShaderMap); TShaderMapRef ApplyLightShaftsPixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), ApplyLightShaftsBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *ScreenVertexShader, *ApplyLightShaftsPixelShader); /// ? ApplyLightShaftsPixelShader->SetParameters(RHICmdList, View, LightShaftsSource); const FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY(); const uint32 DownsampleFactor = GetLightShaftDownsampleFactor(); const FIntPoint FilterBufferSize = GSceneRenderTargets.GetBufferSizeXY() / DownsampleFactor; const FIntPoint DownSampledXY = View.ViewRect.Min / DownsampleFactor; const uint32 DownsampledSizeX = View.ViewRect.Width() / DownsampleFactor; const uint32 DownsampledSizeY = View.ViewRect.Height() / DownsampleFactor; DrawRectangle( RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), DownSampledXY.X, DownSampledXY.Y, DownsampledSizeX, DownsampledSizeY, FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()), FilterBufferSize, *ScreenVertexShader, EDRF_UseTriangleOptimization); GSceneRenderTargets.FinishRenderingSceneColor(RHICmdList, false); } void FSceneViewState::TrimHistoryRenderTargets(const FScene* Scene) { for (TMap >::TIterator It(LightShaftBloomHistoryRTs); It; ++It) { bool bLightIsUsed = false; for (TSparseArray::TConstIterator LightIt(Scene->Lights); 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(); } } } void FDeferredShadingSceneRenderer::RenderLightShaftBloom(FRHICommandListImmediate& RHICmdList) { if (DoesViewFamilyAllowLightShafts(ViewFamily)) { TRefCountPtr LightShafts0; TRefCountPtr LightShafts1; for (TSparseArray::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt) { const FLightSceneInfo* const LightSceneInfo = LightIt->LightSceneInfo; if (LightSceneInfo->bEnableLightShaftBloom) { SCOPED_DRAW_EVENT(RHICmdList, RenderLightShaftBloom); // Allocate light shaft render targets on demand, using the pool AllocateOrReuseLightShaftRenderTarget(RHICmdList, LightShafts0, TEXT("LightShafts0")); AllocateOrReuseLightShaftRenderTarget(RHICmdList, LightShafts1, TEXT("LightShafts1")); for (int ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) { FViewInfo& View = Views[ViewIndex]; if (ShouldRenderLightShaftsForLight(View, LightSceneInfo)) { INC_DWORD_STAT(STAT_LightShaftsLights); // Generate the bloom source from scene color, masked by depth and downsampled DownsamplePass(RHICmdList, View, LightSceneInfo, LightShafts0, LightShafts1); FSceneViewState* ViewState = (FSceneViewState*)View.State; TRefCountPtr* HistoryState = NULL; if (ViewState) { // Find the previous frame's bloom source for this light HistoryState = &ViewState->LightShaftBloomHistoryRTs.FindOrAdd(LightSceneInfo->Proxy->GetLightComponent()); } TRefCountPtr HistoryOutput; // Apply temporal AA to the occlusion mask // Result will be in HistoryOutput ApplyTemporalAA(RHICmdList, View, TEXT("LSBloomHistory"), HistoryState, LightShafts0, HistoryOutput); // Apply radial blur passes // Send HistoryOutput in as the first pass input only, so it will not be overwritten by any subsequent passes, since it is needed for next frame ApplyRadialBlurPasses(RHICmdList, View, LightSceneInfo, HistoryOutput, LightShafts0, LightShafts1); // Add light shaft bloom to scene color in full res ApplyLightShaftBloom(RHICmdList, View, LightSceneInfo, LightShafts0); } } } } } }