Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/VolumetricFogLightFunction.cpp
jason hoerner b19bb6be2f UE5_MAIN: Multi-view-family scene renderer refactor, part 1. Major structural change to allow scene renderer to accept multiple view families, with otherwise negligible changes in internal behavior.
* Added "BeginRenderingViewFamilies" render interface call that accepts multiple view families.  Original "BeginRenderingViewFamily" falls through to this.
* FSceneRenderer modified to include an array of view families, plus an active view family and the Views for that family.
* Swap ViewFamily to ActiveViewFamily.
* Swap Views array from TArray<FViewInfo> to TArrayView<FViewInfo>, including where the Views array is passed to functions.
* FSceneRenderer iterates over the view families, rendering each one at a time, as separate render graph executions.
* Some frame setup and cleanup logic outside the render graph runs once.
* Moved stateful FSceneRenderer members to FViewFamilyInfo, to preserve existing one-at-a-time view family rendering behavior.
* Display Cluster (Virtual Production) uses new API.

Next step will push everything into one render graph, which requires handling per-family external resources and cleaning up singletons (like FSceneTextures and FSceneTexturesConfig).  Once that's done, we'll be in a position to further interleave rendering, properly handle once per frame work, and solve artifacts in various systems.

#jira none
#rnx
#rb zach.bethel
#preflight 625df821b21bb49791d377c9

[CL 19813996 by jason hoerner in ue5-main branch]
2022-04-19 14:45:26 -04:00

524 lines
24 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VolumetricFogLightFunction.cpp
=============================================================================*/
#include "VolumetricFog.h"
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "SceneUtils.h"
#include "LightRendering.h"
#include "SceneFilterRendering.h"
#include "PostProcessing.h"
#include "RHI/Public/PipelineStateCache.h"
int GVolumetricFogLightFunctionResolution = 128;
FAutoConsoleVariableRef CVarVolumetricFogLightFunctionResolution(
TEXT("r.VolumetricFog.LightFunction.Resolution"),
GVolumetricFogLightFunctionResolution,
TEXT("The resolution of all light functions generated to be sampled when rendering volumetric fog."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int GVolumetricFogLightFunctionCount = 16;
FAutoConsoleVariableRef CVarVolumetricFogLightFunctionCount(
TEXT("r.VolumetricFog.LightFunction.LightFunctionCount"),
GVolumetricFogLightFunctionCount,
TEXT("The maximum light function that can be rendered per frame."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GVolumetricFogLightFunctionDirectionalLightSupersampleScale = 2.0f;
FAutoConsoleVariableRef CVarVolumetricFogLightFunctionSupersampleScale(
TEXT("r.VolumetricFog.LightFunction.DirectionalLightSupersampleScale"),
GVolumetricFogLightFunctionDirectionalLightSupersampleScale,
TEXT("Scales the slice depth distribution."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
extern int GVolumetricFogLightFunction;
static bool inline LocalLightLighFunctionsEnabled()
{
return GVolumetricFogLightFunction > 0;
}
static int32 GetVolumetricFogLightFunctionResolution()
{
const int32 VolumetricFogLightFunctionResolution = FMath::Clamp(GVolumetricFogLightFunctionResolution, 32, 256); // Clamp to reasonable values
return VolumetricFogLightFunctionResolution;
}
class FVolumetricFogLightFunctionPS : public FMaterialShader
{
DECLARE_SHADER_TYPE(FVolumetricFogLightFunctionPS, Material);
class FLightType : SHADER_PERMUTATION_INT("LIGHT_TYPE", 3);
using FPermutationDomain = TShaderPermutationDomain< FLightType >;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FMatrix44f, LightFunctionTranslatedWorldToLight)
SHADER_PARAMETER(FMatrix44f, ShadowToTranslatedWorld)
SHADER_PARAMETER(FVector4f, LightFunctionParameters)
SHADER_PARAMETER(FVector3f, LightFunctionParameters2)
SHADER_PARAMETER(FVector3f, LightTranslatedWorldPosition)
SHADER_PARAMETER(FVector2f, LightFunctionTexelSize)
END_SHADER_PARAMETER_STRUCT()
FVolumetricFogLightFunctionPS() {}
FVolumetricFogLightFunctionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FMaterialShader(Initializer)
{
Bindings.BindForLegacyShaderParameters(
this,
Initializer.PermutationId,
Initializer.ParameterMap,
*FParameters::FTypeInfo::GetStructMetadata(),
// Don't require full bindings, we use FMaterialShader::SetParameters
false);
}
static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters)
{
return Parameters.MaterialParameters.MaterialDomain == MD_LightFunction && DoesPlatformSupportVolumetricFog(Parameters.Platform);
}
void SetParameters(
FRHICommandList& RHICmdList,
FParameters& PS,
const FSceneView& View,
const FLightSceneInfo* LightSceneInfo,
const FMaterialRenderProxy* MaterialProxy,
FVector2D LightFunctionTexelSizeValue,
const FMatrix44f& ShadowToTranslatedWorldValue)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy);
FMaterialShader::SetViewParameters(RHICmdList, ShaderRHI, View, View.ViewUniformBuffer);
FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialProxy, Material, View);
PS.LightFunctionParameters = FLightFunctionSharedParameters::GetLightFunctionSharedParameters(LightSceneInfo, 1.0f);
PS.LightFunctionParameters2 = FVector3f(
LightSceneInfo->Proxy->GetLightFunctionFadeDistance(),
LightSceneInfo->Proxy->GetLightFunctionDisabledBrightness(),
0.0f);
{
const FVector Scale = LightSceneInfo->Proxy->GetLightFunctionScale();
// Switch x and z so that z of the user specified scale affects the distance along the light direction
const FVector InverseScale = FVector(1.f / Scale.Z, 1.f / Scale.Y, 1.f / Scale.X);
const FMatrix WorldToLight = LightSceneInfo->Proxy->GetWorldToLight() * FScaleMatrix(FVector(InverseScale));
const FMatrix TranslatedWorldToWorld = FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation());
PS.LightFunctionTranslatedWorldToLight = FMatrix44f(TranslatedWorldToWorld * WorldToLight);
}
PS.LightFunctionTexelSize = FVector2f(LightFunctionTexelSizeValue);
PS.ShadowToTranslatedWorld = ShadowToTranslatedWorldValue;
PS.LightTranslatedWorldPosition = FVector4f(LightSceneInfo->Proxy->GetPosition() + View.ViewMatrices.GetPreViewTranslation());
}
};
IMPLEMENT_MATERIAL_SHADER_TYPE(, FVolumetricFogLightFunctionPS, TEXT("/Engine/Private/VolumetricFogLightFunction.usf"), TEXT("Main"), SF_Pixel);
BEGIN_SHADER_PARAMETER_STRUCT(FVolumetricFogLightFunctionParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
void FDeferredShadingSceneRenderer::RenderLightFunctionForVolumetricFog(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
const FSceneTextures& SceneTextures,
FIntVector VolumetricFogGridSize,
float VolumetricFogMaxDistance,
FMatrix44f& OutDirectionalLightFunctionTranslatedWorldToShadow,
FRDGTexture*& OutDirectionalLightFunctionTexture,
bool& bOutUseDirectionalLightShadowing)
{
TMap<FLightSceneInfo*, FVolumetricFogLocalLightFunctionInfo>& LocalLightFunctionData = View.VolumetricFogResources.LocalLightFunctionData;
LocalLightFunctionData.Reset();
TArray<FLightSceneInfo*> LocalLightsToEvaluate;
// The only directional light we can accept in the volumetric fog because we use the forward lighting data in the Scattering compute shader.
const FLightSceneProxy* SelectedForwardDirectionalLightProxy = View.ForwardLightingResources.SelectedForwardDirectionalLightProxy;
// Default directional light properties
OutDirectionalLightFunctionTranslatedWorldToShadow = FMatrix44f::Identity;
bOutUseDirectionalLightShadowing = false;
FLightSceneInfo* DirectionalLightSceneInfo = NULL;
// Gather lights that need to evaluate light functions
for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
if (ActiveViewFamily->EngineShowFlags.LightFunctions
&& LightSceneInfo
&& LightSceneInfo->Proxy
// Band-aid fix for extremely rare case that light scene proxy contains NaNs.
&& !LightSceneInfo->Proxy->GetDirection().ContainsNaN()
&& LightSceneInfo->ShouldRenderLightViewIndependent()
&& LightSceneInfo->ShouldRenderLight(View))
{
if (DirectionalLightSceneInfo == NULL && LightSceneInfo->Proxy == SelectedForwardDirectionalLightProxy &&
LightSceneInfo->Proxy->GetLightType() == LightType_Directional)
{
// We only take the first directional light into account
bOutUseDirectionalLightShadowing = LightSceneInfo->Proxy->CastsVolumetricShadow();
if (CheckForLightFunction(LightSceneInfo))
{
DirectionalLightSceneInfo = LightSceneInfo;
}
}
else if (LocalLightLighFunctionsEnabled() && CheckForLightFunction(LightSceneInfo))
{
const FMaterialRenderProxy* MaterialProxy = LightSceneInfo->Proxy->GetLightFunctionMaterial();
if (!MaterialProxy || !MaterialProxy->GetIncompleteMaterialWithFallback(Scene->GetFeatureLevel()).IsLightFunction())
{
continue;
}
if (LightSceneInfo->Proxy->GetLightType() == LightType_Spot
|| LightSceneInfo->Proxy->GetLightType() == LightType_Point)
{
LocalLightsToEvaluate.Add(LightSceneInfo);
}
}
}
}
// Now bake the light function into a 2d transient texture for the special single directional light we have selected
if (DirectionalLightSceneInfo)
{
RDG_EVENT_SCOPE(GraphBuilder, "DirectionalLightFunction");
// Estimate the resolution and the projection matrix.
FProjectedShadowInfo ProjectedShadowInfo;
FIntPoint LightFunctionResolution;
{
const FVector ViewForward = View.ViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(2);
const FVector ViewUp = View.ViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(1);
const FVector ViewRight = View.ViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(0);
const FVector LightDirection = DirectionalLightSceneInfo->Proxy->GetDirection().GetSafeNormal();
FVector AxisWeights;
AxisWeights.X = FMath::Abs(LightDirection | ViewRight) * VolumetricFogGridSize.X;
AxisWeights.Y = FMath::Abs(LightDirection | ViewUp) * VolumetricFogGridSize.Y;
AxisWeights.Z = FMath::Abs(LightDirection | ViewForward) * VolumetricFogGridSize.Z;
const float VolumeResolutionEstimate = FMath::Max(AxisWeights.X, FMath::Max(AxisWeights.Y, AxisWeights.Z)) * GVolumetricFogLightFunctionDirectionalLightSupersampleScale;
LightFunctionResolution = FIntPoint(FMath::TruncToInt(VolumeResolutionEstimate), FMath::TruncToInt(VolumeResolutionEstimate));
// Snap the resolution to allow render target pool hits most of the time
const int32 ResolutionSnapFactor = 32;
LightFunctionResolution.X = FMath::DivideAndRoundUp(LightFunctionResolution.X, ResolutionSnapFactor) * ResolutionSnapFactor;
LightFunctionResolution.Y = FMath::DivideAndRoundUp(LightFunctionResolution.Y, ResolutionSnapFactor) * ResolutionSnapFactor;
// Guard against invalid resolutions
const uint32 LiFuncResXU32 = (uint32)LightFunctionResolution.X;
const uint32 LiFuncResYU32 = (uint32)LightFunctionResolution.Y;
const uint32 MaxTextureResU32 = GMaxTextureDimensions;
if (LiFuncResXU32 > MaxTextureResU32 || LiFuncResYU32 > MaxTextureResU32)
{
#if !(UE_BUILD_SHIPPING)
UE_LOG(LogRenderer, Error,
TEXT("Invalid LightFunctionResolution %dx%d, View={ %s}, LightDirection={ %s }"),
LightFunctionResolution.X,
LightFunctionResolution.Y,
*View.ViewMatrices.GetOverriddenTranslatedViewMatrix().ToString(),
*LightDirection.ToString());
#endif
const uint32 ClampedRes = FMath::Max(FMath::Min3(LiFuncResXU32, LiFuncResYU32, MaxTextureResU32), (uint32)ResolutionSnapFactor);
LightFunctionResolution.X = ClampedRes;
LightFunctionResolution.Y = ClampedRes;
}
FWholeSceneProjectedShadowInitializer ShadowInitializer;
check(VolumetricFogMaxDistance > 0);
FSphere Bounds = DirectionalLightSceneInfo->Proxy->GetShadowSplitBoundsDepthRange(View, View.ViewMatrices.GetViewOrigin(), 0, VolumetricFogMaxDistance, NULL);
check(Bounds.W > 0);
const float ShadowExtent = Bounds.W / FMath::Sqrt(3.0f);
const FBoxSphereBounds SubjectBounds(Bounds.Center, FVector(ShadowExtent, ShadowExtent, ShadowExtent), Bounds.W);
ShadowInitializer.PreShadowTranslation = -Bounds.Center;
ShadowInitializer.WorldToLight = FInverseRotationMatrix(LightDirection.Rotation());
ShadowInitializer.Scales = FVector2D(1.0f / Bounds.W, 1.0f / Bounds.W);
ShadowInitializer.SubjectBounds = FBoxSphereBounds(FVector::ZeroVector, SubjectBounds.BoxExtent, SubjectBounds.SphereRadius);
ShadowInitializer.WAxis = FVector4(0, 0, 0, 1);
ShadowInitializer.MinLightW = -HALF_WORLD_MAX;
// Reduce casting distance on a directional light
// This is necessary to improve floating point precision in several places, especially when deriving frustum verts from InvReceiverMatrix
ShadowInitializer.MaxDistanceToCastInLightW = HALF_WORLD_MAX / 32.0f;
ShadowInitializer.bRayTracedDistanceField = false;
ShadowInitializer.CascadeSettings.bFarShadowCascade = false;
ProjectedShadowInfo.SetupWholeSceneProjection(
DirectionalLightSceneInfo,
&View,
ShadowInitializer,
LightFunctionResolution.X,
LightFunctionResolution.Y,
LightFunctionResolution.X,
LightFunctionResolution.Y,
0
);
OutDirectionalLightFunctionTranslatedWorldToShadow = ProjectedShadowInfo.TranslatedWorldToClipInnerMatrix;
}
// Now render the texture
{
FRDGTextureDesc LightFunctionTextureDesc = FRDGTextureDesc::Create2D(LightFunctionResolution, PF_G8, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_RenderTargetable);
LightFunctionTextureDesc.Flags |= GFastVRamConfig.VolumetricFog;
FRDGTexture* LightFunctionTexture = GraphBuilder.CreateTexture(LightFunctionTextureDesc, TEXT("VolumetricFog.LightFunction"));
OutDirectionalLightFunctionTexture = LightFunctionTexture;
const FMaterialRenderProxy* MaterialProxyForRendering = DirectionalLightSceneInfo->Proxy->GetLightFunctionMaterial();
const FMaterial& Material = MaterialProxyForRendering->GetMaterialWithFallback(Scene->GetFeatureLevel(), MaterialProxyForRendering);
FVolumetricFogLightFunctionParameters* PassParameters = GraphBuilder.AllocParameters<FVolumetricFogLightFunctionParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(LightFunctionTexture, ERenderTargetLoadAction::ENoAction);
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
FMatrix44f LightFunctionTranslatedWorldToShadowMatrix = OutDirectionalLightFunctionTranslatedWorldToShadow;
GraphBuilder.AddPass(
RDG_EVENT_NAME("LightFunction %ux%u Material=%s", LightFunctionResolution.X, LightFunctionResolution.Y, *(Material.GetFriendlyName())),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, &View, MaterialProxyForRendering, &Material, LightFunctionResolution, DirectionalLightSceneInfo, LightFunctionTranslatedWorldToShadowMatrix](FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(0.f, 0.f, 0.f, LightFunctionResolution.X, LightFunctionResolution.Y, 1.f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap();
TShaderMapRef<FPostProcessVS> VertexShader(View.ShaderMap);
check(DirectionalLightSceneInfo->Proxy->GetLightType() == LightType_Directional)
FVolumetricFogLightFunctionPS::FPermutationDomain PermutationVector;
PermutationVector.Set<FVolumetricFogLightFunctionPS::FLightType>(2);
TShaderRef<FVolumetricFogLightFunctionPS> PixelShader = MaterialShaderMap->GetShader<FVolumetricFogLightFunctionPS>(PermutationVector);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FVolumetricFogLightFunctionPS::FParameters PS;
PixelShader->SetParameters(RHICmdList, PS, View, DirectionalLightSceneInfo, MaterialProxyForRendering,
FVector2D(1.0f / LightFunctionResolution.X, 1.0f / LightFunctionResolution.Y), LightFunctionTranslatedWorldToShadowMatrix.Inverse());
ClearUnusedGraphResources(PixelShader, &PS);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PS);
DrawRectangle(
RHICmdList,
0, 0,
LightFunctionResolution.X, LightFunctionResolution.Y,
0, 0,
LightFunctionResolution.X, LightFunctionResolution.Y,
LightFunctionResolution,
LightFunctionResolution,
VertexShader);
}
);
}
}
if (!LocalLightsToEvaluate.Num())
{
return; // Do not run atlas operation if no light needs it
}
// We are going to render local light LightFunction so we allocate the atlas.
View.VolumetricFogResources.TransientLightFunctionTextureAtlas = new (GraphBuilder.Alloc(sizeof(FTransientLightFunctionTextureAtlas), alignof(FTransientLightFunctionTextureAtlas))) FTransientLightFunctionTextureAtlas(GraphBuilder);
// Conpute all the data required by the lights
const FMatrix TranslatedWorldToWorld = FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation());
for (FLightSceneInfo* LightSceneInfo : LocalLightsToEvaluate)
{
const bool bIsPointLight = LightSceneInfo->Proxy->GetLightType() == LightType_Point;
const FVector LightDirection = LightSceneInfo->Proxy->GetDirection().GetSafeNormal();
const FIntPoint LightFunctionResolution = GetVolumetricFogLightFunctionResolution();
FMatrix WorldToShadowMatrix = FMatrix::Identity;
// Spotlights needs to utilize their shadow projection when rendering the light function
if (!bIsPointLight)
{
TArray<FWholeSceneProjectedShadowInitializer, TInlineAllocator<6>> Initializers;
LightSceneInfo->Proxy->GetWholeSceneProjectedShadowInitializer(*View.Family, Initializers);
FWholeSceneProjectedShadowInitializer ShadowInitializer = Initializers[0];
FProjectedShadowInfo ProjectedShadowInfo;
ProjectedShadowInfo.SetupWholeSceneProjection(
LightSceneInfo,
&View,
ShadowInitializer,
LightFunctionResolution.X,
LightFunctionResolution.Y,
LightFunctionResolution.X,
LightFunctionResolution.Y,
0
);
FVector4f ShadowmapMinMaxValue;
WorldToShadowMatrix = ProjectedShadowInfo.GetWorldToShadowMatrix(ShadowmapMinMaxValue, &LightFunctionResolution);
}
FVolumetricFogLocalLightFunctionInfo& LightFunctionData = LocalLightFunctionData.Add(LightSceneInfo);
LightFunctionData.AtlasTile = View.VolumetricFogResources.TransientLightFunctionTextureAtlas->AllocateAtlasTile();
// Pass the World To Light when light type is Point because we need it instead of the World To Shadow.
const FMatrix WorldToLight = bIsPointLight ? LightSceneInfo->Proxy->GetWorldToLight() : WorldToShadowMatrix;
LightFunctionData.LightFunctionTranslatedWorldToLightMatrix = FMatrix44f(TranslatedWorldToWorld * WorldToLight);
}
// And now process all the local lights, allocated atlas tile and generate the tile as needed
FVolumetricFogLightFunctionParameters* PassParameters = GraphBuilder.AllocParameters<FVolumetricFogLightFunctionParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(View.VolumetricFogResources.TransientLightFunctionTextureAtlas->GetTransientLightFunctionAtlasTexture(), ERenderTargetLoadAction::ENoAction);
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
GraphBuilder.AddPass(
RDG_EVENT_NAME("Local Lights LightFunctions"),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, &View, &Scene = Scene](FRHICommandList& RHICmdList)
{
uint32 AtlasTextureWidth = View.VolumetricFogResources.TransientLightFunctionTextureAtlas->GetAtlasTextureWidth();
uint32 LightFunctionResolution = GetVolumetricFogLightFunctionResolution();
for (auto& Itr : View.VolumetricFogResources.LocalLightFunctionData)
{
FLightSceneInfo* LightSceneInfo = Itr.Key;
FVolumetricFogLocalLightFunctionInfo& LightFunctionInfo = Itr.Value;
if (!LightFunctionInfo.AtlasTile.bIsDefault)
{
const FMaterialRenderProxy* MaterialProxyForRendering = LightSceneInfo->Proxy->GetLightFunctionMaterial();
const FMaterial& Material = MaterialProxyForRendering->GetMaterialWithFallback(Scene->GetFeatureLevel(), MaterialProxyForRendering);
RHICmdList.SetViewport(0.f, 0.f, 0.f, AtlasTextureWidth, AtlasTextureWidth, 1.f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap();
TShaderMapRef<FPostProcessVS> VertexShader(View.ShaderMap);
FVolumetricFogLightFunctionPS::FPermutationDomain PermutationVector;
switch (LightSceneInfo->Proxy->GetLightType())
{
case LightType_Point:
case LightType_Rect:
PermutationVector.Set<FVolumetricFogLightFunctionPS::FLightType>(0);
break;
case LightType_Spot:
PermutationVector.Set<FVolumetricFogLightFunctionPS::FLightType>(1);
break;
default:
check(false)
}
TShaderRef<FVolumetricFogLightFunctionPS> PixelShader = MaterialShaderMap->GetShader<FVolumetricFogLightFunctionPS>(PermutationVector);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FVolumetricFogLightFunctionPS::FParameters PS;
PixelShader->SetParameters(RHICmdList, PS, View, LightSceneInfo, MaterialProxyForRendering,
FVector2D(1.0f / LightFunctionResolution, 1.0f / LightFunctionResolution), LightFunctionInfo.LightFunctionTranslatedWorldToLightMatrix.Inverse());
ClearUnusedGraphResources(PixelShader, &PS);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PS);
FIntPoint RectSize = LightFunctionInfo.AtlasTile.RectBound.Size();
DrawRectangle(
RHICmdList,
LightFunctionInfo.AtlasTile.RectBound.Min.X, LightFunctionInfo.AtlasTile.RectBound.Min.Y,
RectSize.X, RectSize.Y,
LightFunctionInfo.AtlasTile.RectBound.Min.X, LightFunctionInfo.AtlasTile.RectBound.Min.Y,
RectSize.X, RectSize.Y,
AtlasTextureWidth,
LightFunctionResolution,
VertexShader);
}
}
}
);
}
FTransientLightFunctionTextureAtlas::FTransientLightFunctionTextureAtlas(FRDGBuilder& GraphBuilder)
: AtlasItemWidth(0)
, AtlasTextureWidth(0)
, AllocatedAtlasTiles(0)
, HalfTexelSize(0.0f)
{
AtlasItemWidth = FMath::Clamp(FMath::CeilToInt(FMath::Sqrt(float(GVolumetricFogLightFunctionCount))), 2, 16); // Clamp to reasonable values
AtlasTextureWidth = AtlasItemWidth * GetVolumetricFogLightFunctionResolution();
HalfTexelSize = 0.5f / float(AtlasTextureWidth);
FRDGTextureDesc LightFunctionTextureDesc = FRDGTextureDesc::Create2D(FIntPoint(AtlasTextureWidth, AtlasTextureWidth), PF_G8, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_RenderTargetable);
TransientLightFunctionAtlasTexture = GraphBuilder.CreateTexture(LightFunctionTextureDesc, TEXT("TransientLightFunctionAtlasTexture"));
DefaultLightFunctionAtlasItemTexture = GSystemTextures.GetWhiteDummy(GraphBuilder);
}
FTransientLightFunctionTextureAtlas::~FTransientLightFunctionTextureAtlas()
{
//
}
FTransientLightFunctionTextureAtlasTile FTransientLightFunctionTextureAtlas::AllocateAtlasTile()
{
FTransientLightFunctionTextureAtlasTile AtlasTile;
if (AllocatedAtlasTiles < AtlasItemWidth * AtlasItemWidth)
{
const uint32 LightFunctionResolution = GetVolumetricFogLightFunctionResolution();
const FIntPoint TileCoord(AllocatedAtlasTiles % AtlasItemWidth, AllocatedAtlasTiles / AtlasItemWidth);
const FIntPoint PixelCoord = TileCoord * LightFunctionResolution;
AtlasTile.bIsDefault = false;
AtlasTile.Texture = TransientLightFunctionAtlasTexture;
AtlasTile.RectBound = FIntRect(PixelCoord, PixelCoord + FIntPoint(LightFunctionResolution, LightFunctionResolution));
const float InvAtlasTextureWidthFloat = 1.0f / float(AtlasTextureWidth);
const float MinX = float(AtlasTile.RectBound.Min.X) * InvAtlasTextureWidthFloat + HalfTexelSize;
const float MinY = float(AtlasTile.RectBound.Min.Y) * InvAtlasTextureWidthFloat + HalfTexelSize;
const float MaxX = float(AtlasTile.RectBound.Max.X) * InvAtlasTextureWidthFloat - HalfTexelSize;
const float MaxY = float(AtlasTile.RectBound.Max.Y) * InvAtlasTextureWidthFloat - HalfTexelSize;
AtlasTile.MinMaxUvBound = FVector4f(MinX, MinY, MaxX, MaxY);
AllocatedAtlasTiles++;
}
else
{
AtlasTile.bIsDefault = true;
AtlasTile.Texture = DefaultLightFunctionAtlasItemTexture;
AtlasTile.RectBound = FIntRect(0, 0, 1, 1);
AtlasTile.MinMaxUvBound = FVector4f(0.0f, 0.0f, 1.0f, 1.0f);
}
return AtlasTile;
}