You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb Rob.Srinivasiah #jira UE-148569 #preflight 627143035e6ce673f442a3dc #review @Robert.Srinivasiah [CL 20026126 by Arciel Rekman in ue5-main branch]
1490 lines
66 KiB
C++
1490 lines
66 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
TranslucentLighting.cpp: Translucent lighting implementation.
|
|
=============================================================================*/
|
|
|
|
#include "TranslucentLighting.h"
|
|
#include "CoreMinimal.h"
|
|
#include "Stats/Stats.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "EngineDefines.h"
|
|
#include "RHI.h"
|
|
#include "RenderResource.h"
|
|
#include "HitProxies.h"
|
|
#include "FinalPostProcessSettings.h"
|
|
#include "ShaderParameters.h"
|
|
#include "RendererInterface.h"
|
|
#include "PrimitiveViewRelevance.h"
|
|
#include "Shader.h"
|
|
#include "StaticBoundShaderState.h"
|
|
#include "SceneUtils.h"
|
|
#include "RHIStaticStates.h"
|
|
#include "SceneManagement.h"
|
|
#include "Engine/MapBuildDataRegistry.h"
|
|
#include "Components/LightComponent.h"
|
|
#include "Materials/Material.h"
|
|
#include "PostProcess/SceneRenderTargets.h"
|
|
#include "LightSceneInfo.h"
|
|
#include "GlobalShader.h"
|
|
#include "MaterialShaderType.h"
|
|
#include "MaterialShader.h"
|
|
#include "MeshMaterialShaderType.h"
|
|
#include "MeshMaterialShader.h"
|
|
#include "ShadowRendering.h"
|
|
#include "SceneRendering.h"
|
|
#include "DeferredShadingRenderer.h"
|
|
#include "TranslucentRendering.h"
|
|
#include "ClearQuad.h"
|
|
#include "ScenePrivate.h"
|
|
#include "OneColorShader.h"
|
|
#include "LightRendering.h"
|
|
#include "ScreenRendering.h"
|
|
#include "AmbientCubemapParameters.h"
|
|
#include "VolumeRendering.h"
|
|
#include "VolumeLighting.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "VisualizeTexture.h"
|
|
#include "MeshPassProcessor.inl"
|
|
#include "SkyAtmosphereRendering.h"
|
|
#include "VolumetricCloudRendering.h"
|
|
|
|
class FMaterial;
|
|
|
|
/** Whether to allow rendering translucency shadow depths. */
|
|
bool GUseTranslucencyShadowDepths = true;
|
|
|
|
DECLARE_GPU_STAT_NAMED(TranslucentLighting, TEXT("Translucent Lighting"));
|
|
|
|
int32 GUseTranslucentLightingVolumes = 1;
|
|
FAutoConsoleVariableRef CVarUseTranslucentLightingVolumes(
|
|
TEXT("r.TranslucentLightingVolume"),
|
|
GUseTranslucentLightingVolumes,
|
|
TEXT("Whether to allow updating the translucent lighting volumes.\n")
|
|
TEXT("0:off, otherwise on, default is 1"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
float GTranslucentVolumeMinFOV = 45;
|
|
static FAutoConsoleVariableRef CVarTranslucentVolumeMinFOV(
|
|
TEXT("r.TranslucentVolumeMinFOV"),
|
|
GTranslucentVolumeMinFOV,
|
|
TEXT("Minimum FOV for translucent lighting volume. Prevents popping in lighting when zooming in."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
float GTranslucentVolumeFOVSnapFactor = 10;
|
|
static FAutoConsoleVariableRef CTranslucentVolumeFOVSnapFactor(
|
|
TEXT("r.TranslucentVolumeFOVSnapFactor"),
|
|
GTranslucentVolumeFOVSnapFactor,
|
|
TEXT("FOV will be snapped to a factor of this before computing volume bounds."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
int32 GUseTranslucencyVolumeBlur = 1;
|
|
FAutoConsoleVariableRef CVarUseTranslucentLightingVolumeBlur(
|
|
TEXT("r.TranslucencyVolumeBlur"),
|
|
GUseTranslucencyVolumeBlur,
|
|
TEXT("Whether to blur the translucent lighting volumes.\n")
|
|
TEXT("0:off, otherwise on, default is 1"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
int32 GTranslucencyLightingVolumeDim = 64;
|
|
FAutoConsoleVariableRef CVarTranslucencyLightingVolumeDim(
|
|
TEXT("r.TranslucencyLightingVolumeDim"),
|
|
GTranslucencyLightingVolumeDim,
|
|
TEXT("Dimensions of the volume textures used for translucency lighting. Larger textures result in higher resolution but lower performance."),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarTranslucencyLightingVolumeInnerDistance(
|
|
TEXT("r.TranslucencyLightingVolumeInnerDistance"),
|
|
1500.0f,
|
|
TEXT("Distance from the camera that the first volume cascade should end"),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<float> CVarTranslucencyLightingVolumeOuterDistance(
|
|
TEXT("r.TranslucencyLightingVolumeOuterDistance"),
|
|
5000.0f,
|
|
TEXT("Distance from the camera that the second volume cascade should end"),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
/** Function returning current translucency lighting volume dimensions. */
|
|
int32 GetTranslucencyLightingVolumeDim()
|
|
{
|
|
extern int32 GTranslucencyLightingVolumeDim;
|
|
return FMath::Clamp(GTranslucencyLightingVolumeDim, 4, 2048);
|
|
}
|
|
|
|
void FViewInfo::CalcTranslucencyLightingVolumeBounds(FBox* InOutCascadeBoundsArray, int32 NumCascades) const
|
|
{
|
|
for (int32 CascadeIndex = 0; CascadeIndex < NumCascades; CascadeIndex++)
|
|
{
|
|
double InnerDistance = CVarTranslucencyLightingVolumeInnerDistance.GetValueOnRenderThread();
|
|
double OuterDistance = CVarTranslucencyLightingVolumeOuterDistance.GetValueOnRenderThread();
|
|
|
|
const double FrustumStartDistance = CascadeIndex == 0 ? 0 : InnerDistance;
|
|
const double FrustumEndDistance = CascadeIndex == 0 ? InnerDistance : OuterDistance;
|
|
|
|
double FieldOfView = DOUBLE_PI / 4.0;
|
|
double AspectRatio = 1.0;
|
|
|
|
if (IsPerspectiveProjection())
|
|
{
|
|
// Derive FOV and aspect ratio from the perspective projection matrix
|
|
FieldOfView = FMath::Atan(1.0 / ShadowViewMatrices.GetProjectionMatrix().M[0][0]);
|
|
// Clamp to prevent shimmering when zooming in
|
|
FieldOfView = FMath::Max(FieldOfView, GTranslucentVolumeMinFOV * DOUBLE_PI / 180.0);
|
|
const double RoundFactorRadians = GTranslucentVolumeFOVSnapFactor * DOUBLE_PI / 180.0;
|
|
// Round up to a fixed factor
|
|
// This causes the volume lighting to make discreet jumps as the FOV animates, instead of slowly crawling over a long period
|
|
FieldOfView = FieldOfView + RoundFactorRadians - FMath::Fmod(FieldOfView, RoundFactorRadians);
|
|
AspectRatio = ShadowViewMatrices.GetProjectionMatrix().M[1][1] / ShadowViewMatrices.GetProjectionMatrix().M[0][0];
|
|
}
|
|
|
|
const double StartHorizontalLength = FrustumStartDistance * FMath::Tan(FieldOfView);
|
|
const FVector StartCameraRightOffset = ShadowViewMatrices.GetViewMatrix().GetColumn(0) * StartHorizontalLength;
|
|
const double StartVerticalLength = StartHorizontalLength / AspectRatio;
|
|
const FVector StartCameraUpOffset = ShadowViewMatrices.GetViewMatrix().GetColumn(1) * StartVerticalLength;
|
|
|
|
const double EndHorizontalLength = FrustumEndDistance * FMath::Tan(FieldOfView);
|
|
const FVector EndCameraRightOffset = ShadowViewMatrices.GetViewMatrix().GetColumn(0) * EndHorizontalLength;
|
|
const double EndVerticalLength = EndHorizontalLength / AspectRatio;
|
|
const FVector EndCameraUpOffset = ShadowViewMatrices.GetViewMatrix().GetColumn(1) * EndVerticalLength;
|
|
|
|
FVector SplitVertices[8];
|
|
const FVector ShadowViewOrigin = ShadowViewMatrices.GetViewOrigin();
|
|
|
|
SplitVertices[0] = ShadowViewOrigin + GetViewDirection() * FrustumStartDistance + StartCameraRightOffset + StartCameraUpOffset;
|
|
SplitVertices[1] = ShadowViewOrigin + GetViewDirection() * FrustumStartDistance + StartCameraRightOffset - StartCameraUpOffset;
|
|
SplitVertices[2] = ShadowViewOrigin + GetViewDirection() * FrustumStartDistance - StartCameraRightOffset + StartCameraUpOffset;
|
|
SplitVertices[3] = ShadowViewOrigin + GetViewDirection() * FrustumStartDistance - StartCameraRightOffset - StartCameraUpOffset;
|
|
|
|
SplitVertices[4] = ShadowViewOrigin + GetViewDirection() * FrustumEndDistance + EndCameraRightOffset + EndCameraUpOffset;
|
|
SplitVertices[5] = ShadowViewOrigin + GetViewDirection() * FrustumEndDistance + EndCameraRightOffset - EndCameraUpOffset;
|
|
SplitVertices[6] = ShadowViewOrigin + GetViewDirection() * FrustumEndDistance - EndCameraRightOffset + EndCameraUpOffset;
|
|
SplitVertices[7] = ShadowViewOrigin + GetViewDirection() * FrustumEndDistance - EndCameraRightOffset - EndCameraUpOffset;
|
|
|
|
FVector Center(0,0,0);
|
|
// Weight the far vertices more so that the bounding sphere will be further from the camera
|
|
// This minimizes wasted shadowmap space behind the viewer
|
|
const double FarVertexWeightScale = 10.0;
|
|
for (int32 VertexIndex = 0; VertexIndex < 8; VertexIndex++)
|
|
{
|
|
const double Weight = VertexIndex > 3 ? 1 / (4.0 + 4.0 / FarVertexWeightScale) : 1 / (4.0 + 4.0 * FarVertexWeightScale);
|
|
Center += SplitVertices[VertexIndex] * Weight;
|
|
}
|
|
|
|
double RadiusSquared = 0;
|
|
for (int32 VertexIndex = 0; VertexIndex < 8; VertexIndex++)
|
|
{
|
|
RadiusSquared = FMath::Max(RadiusSquared, (Center - SplitVertices[VertexIndex]).SizeSquared());
|
|
}
|
|
|
|
FSphere SphereBounds(Center, FMath::Sqrt(RadiusSquared));
|
|
|
|
// Snap the center to a multiple of the volume dimension for stability
|
|
const int32 TranslucencyLightingVolumeDim = GetTranslucencyLightingVolumeDim();
|
|
SphereBounds.Center.X = SphereBounds.Center.X - FMath::Fmod(SphereBounds.Center.X, SphereBounds.W * 2 / TranslucencyLightingVolumeDim);
|
|
SphereBounds.Center.Y = SphereBounds.Center.Y - FMath::Fmod(SphereBounds.Center.Y, SphereBounds.W * 2 / TranslucencyLightingVolumeDim);
|
|
SphereBounds.Center.Z = SphereBounds.Center.Z - FMath::Fmod(SphereBounds.Center.Z, SphereBounds.W * 2 / TranslucencyLightingVolumeDim);
|
|
|
|
InOutCascadeBoundsArray[CascadeIndex] = FBox(SphereBounds.Center - SphereBounds.W, SphereBounds.Center + SphereBounds.W);
|
|
}
|
|
}
|
|
|
|
class FTranslucencyDepthShaderElementData : public FMeshMaterialShaderElementData
|
|
{
|
|
public:
|
|
float TranslucentShadowStartOffset;
|
|
};
|
|
|
|
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FTranslucencyDepthPassUniformParameters,)
|
|
SHADER_PARAMETER_STRUCT(FSceneTextureUniformParameters, SceneTextures)
|
|
SHADER_PARAMETER(FMatrix44f, ProjectionMatrix)
|
|
SHADER_PARAMETER(float, bClampToNearPlane)
|
|
SHADER_PARAMETER(float, InvMaxSubjectDepth)
|
|
SHADER_PARAMETER_STRUCT(FTranslucentSelfShadowUniformParameters, TranslucentSelfShadow)
|
|
END_GLOBAL_SHADER_PARAMETER_STRUCT()
|
|
|
|
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FTranslucencyDepthPassUniformParameters, "TranslucentDepthPass", SceneTextures);
|
|
|
|
void SetupTranslucencyDepthPassUniformBuffer(
|
|
const FProjectedShadowInfo* ShadowInfo,
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
FTranslucencyDepthPassUniformParameters& TranslucencyDepthPassParameters)
|
|
{
|
|
// Note - scene depth can be bound by the material for use in depth fades
|
|
// This is incorrect when rendering a shadowmap as it's not from the camera's POV
|
|
// Set the scene depth texture to something safe when rendering shadow depths
|
|
SetupSceneTextureUniformParameters(GraphBuilder, View.GetSceneTexturesChecked(), View.FeatureLevel, ESceneTextureSetupMode::None, TranslucencyDepthPassParameters.SceneTextures);
|
|
|
|
TranslucencyDepthPassParameters.ProjectionMatrix = FTranslationMatrix44f(FVector3f(ShadowInfo->PreShadowTranslation - View.ViewMatrices.GetPreViewTranslation())) * ShadowInfo->TranslatedWorldToClipInnerMatrix;
|
|
|
|
// Only clamp vertices to the near plane when rendering whole scene directional light shadow depths or preshadows from directional lights
|
|
const bool bClampToNearPlaneValue = ShadowInfo->IsWholeSceneDirectionalShadow() || (ShadowInfo->bPreShadow && ShadowInfo->bDirectionalLight);
|
|
TranslucencyDepthPassParameters.bClampToNearPlane = bClampToNearPlaneValue ? 1.0f : 0.0f;
|
|
|
|
TranslucencyDepthPassParameters.InvMaxSubjectDepth = ShadowInfo->InvMaxSubjectDepth;
|
|
|
|
SetupTranslucentSelfShadowUniformParameters(ShadowInfo, TranslucencyDepthPassParameters.TranslucentSelfShadow);
|
|
}
|
|
|
|
/**
|
|
* Vertex shader used to render shadow maps for translucency.
|
|
*/
|
|
class FTranslucencyShadowDepthVS : public FMeshMaterialShader
|
|
{
|
|
DECLARE_INLINE_TYPE_LAYOUT(FTranslucencyShadowDepthVS, NonVirtual);
|
|
public:
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsTranslucentBlendMode(Parameters.MaterialParameters.BlendMode) && IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
FTranslucencyShadowDepthVS() {}
|
|
FTranslucencyShadowDepthVS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer) :
|
|
FMeshMaterialShader(Initializer)
|
|
{}
|
|
};
|
|
|
|
enum ETranslucencyShadowDepthShaderMode
|
|
{
|
|
TranslucencyShadowDepth_PerspectiveCorrect,
|
|
TranslucencyShadowDepth_Standard,
|
|
};
|
|
|
|
template <ETranslucencyShadowDepthShaderMode ShaderMode>
|
|
class TTranslucencyShadowDepthVS : public FTranslucencyShadowDepthVS
|
|
{
|
|
DECLARE_SHADER_TYPE(TTranslucencyShadowDepthVS, MeshMaterial);
|
|
public:
|
|
|
|
TTranslucencyShadowDepthVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
|
|
FTranslucencyShadowDepthVS(Initializer)
|
|
{}
|
|
|
|
TTranslucencyShadowDepthVS() {}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FTranslucencyShadowDepthVS::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("PERSPECTIVE_CORRECT_DEPTH"), (uint32)(ShaderMode == TranslucencyShadowDepth_PerspectiveCorrect ? 1 : 0));
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthVS<TranslucencyShadowDepth_PerspectiveCorrect>,TEXT("/Engine/Private/TranslucentShadowDepthShaders.usf"),TEXT("MainVS"),SF_Vertex);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthVS<TranslucencyShadowDepth_Standard>,TEXT("/Engine/Private/TranslucentShadowDepthShaders.usf"),TEXT("MainVS"),SF_Vertex);
|
|
|
|
/**
|
|
* Pixel shader used for accumulating translucency layer densities
|
|
*/
|
|
class FTranslucencyShadowDepthPS : public FMeshMaterialShader
|
|
{
|
|
DECLARE_INLINE_TYPE_LAYOUT(FTranslucencyShadowDepthPS, NonVirtual);
|
|
public:
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsTranslucentBlendMode(Parameters.MaterialParameters.BlendMode) && IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
FTranslucencyShadowDepthPS() = default;
|
|
FTranslucencyShadowDepthPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
|
|
FMeshMaterialShader(Initializer)
|
|
{
|
|
TranslucentShadowStartOffset.Bind(Initializer.ParameterMap, TEXT("TranslucentShadowStartOffset"));
|
|
}
|
|
|
|
void GetShaderBindings(
|
|
const FScene* Scene,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
const FMeshPassProcessorRenderState& DrawRenderState,
|
|
const FTranslucencyDepthShaderElementData& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings) const
|
|
{
|
|
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, DrawRenderState, ShaderElementData, ShaderBindings);
|
|
|
|
ShaderBindings.Add(TranslucentShadowStartOffset, ShaderElementData.TranslucentShadowStartOffset);
|
|
}
|
|
|
|
private:
|
|
LAYOUT_FIELD(FShaderParameter, TranslucentShadowStartOffset);
|
|
};
|
|
|
|
template <ETranslucencyShadowDepthShaderMode ShaderMode>
|
|
class TTranslucencyShadowDepthPS : public FTranslucencyShadowDepthPS
|
|
{
|
|
public:
|
|
DECLARE_SHADER_TYPE(TTranslucencyShadowDepthPS, MeshMaterial);
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FTranslucencyShadowDepthPS::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("PERSPECTIVE_CORRECT_DEPTH"), (uint32)(ShaderMode == TranslucencyShadowDepth_PerspectiveCorrect ? 1 : 0));
|
|
}
|
|
|
|
TTranslucencyShadowDepthPS() = default;
|
|
TTranslucencyShadowDepthPS(const ShaderMetaType::CompiledShaderInitializerType & Initializer) :
|
|
FTranslucencyShadowDepthPS(Initializer)
|
|
{}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthPS<TranslucencyShadowDepth_PerspectiveCorrect>,TEXT("/Engine/Private/TranslucentShadowDepthShaders.usf"),TEXT("MainOpacityPS"),SF_Pixel);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthPS<TranslucencyShadowDepth_Standard>,TEXT("/Engine/Private/TranslucentShadowDepthShaders.usf"),TEXT("MainOpacityPS"),SF_Pixel);
|
|
|
|
class FTranslucencyDepthPassMeshProcessor : public FMeshPassProcessor
|
|
{
|
|
public:
|
|
FTranslucencyDepthPassMeshProcessor(const FScene* Scene,
|
|
const FSceneView* InViewIfDynamicMeshCommand,
|
|
const FMeshPassProcessorRenderState& InPassDrawRenderState,
|
|
const FProjectedShadowInfo* InShadowInfo,
|
|
FMeshPassDrawListContext* InDrawListContext);
|
|
|
|
virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final;
|
|
|
|
private:
|
|
bool TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material);
|
|
|
|
template<ETranslucencyShadowDepthShaderMode ShaderMode>
|
|
bool Process(
|
|
const FMeshBatch& MeshBatch,
|
|
uint64 BatchElementMask,
|
|
int32 StaticMeshId,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
float MaterialTranslucentShadowStartOffset,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode);
|
|
|
|
FMeshPassProcessorRenderState PassDrawRenderState;
|
|
const FProjectedShadowInfo* ShadowInfo;
|
|
FShadowDepthType ShadowDepthType;
|
|
const bool bDirectionalLight;
|
|
};
|
|
|
|
FTranslucencyDepthPassMeshProcessor::FTranslucencyDepthPassMeshProcessor(const FScene* Scene,
|
|
const FSceneView* InViewIfDynamicMeshCommand,
|
|
const FMeshPassProcessorRenderState& InPassDrawRenderState,
|
|
const FProjectedShadowInfo* InShadowInfo,
|
|
FMeshPassDrawListContext* InDrawListContext)
|
|
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
|
|
, PassDrawRenderState(InPassDrawRenderState)
|
|
, ShadowInfo(InShadowInfo)
|
|
, ShadowDepthType(InShadowInfo->GetShadowDepthType())
|
|
, bDirectionalLight(InShadowInfo->bDirectionalLight)
|
|
{
|
|
}
|
|
|
|
bool FTranslucencyDepthPassMeshProcessor::TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material)
|
|
{
|
|
// Determine the mesh's material and blend mode.
|
|
const EBlendMode BlendMode = Material.GetBlendMode();
|
|
const float MaterialTranslucentShadowStartOffset = Material.GetTranslucentShadowStartOffset();
|
|
const bool MaterialCastDynamicShadowAsMasked = Material.GetCastDynamicShadowAsMasked();
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, Material, OverrideSettings);
|
|
const bool bIsTranslucent = IsTranslucentBlendMode(BlendMode);
|
|
|
|
// Only render translucent meshes into the Fourier opacity maps
|
|
if (bIsTranslucent && ShouldIncludeDomainInMeshPass(Material.GetMaterialDomain()) && !MaterialCastDynamicShadowAsMasked)
|
|
{
|
|
if (bDirectionalLight)
|
|
{
|
|
return Process<TranslucencyShadowDepth_Standard>(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, MaterialTranslucentShadowStartOffset, MeshFillMode, MeshCullMode);
|
|
}
|
|
else
|
|
{
|
|
return Process<TranslucencyShadowDepth_PerspectiveCorrect>(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, MaterialTranslucentShadowStartOffset, MeshFillMode, MeshCullMode);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template<ETranslucencyShadowDepthShaderMode ShaderMode>
|
|
bool FTranslucencyDepthPassMeshProcessor::Process(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
int32 StaticMeshId,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
float MaterialTranslucentShadowStartOffset,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode)
|
|
{
|
|
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
|
|
|
|
TMeshProcessorShaders<
|
|
TTranslucencyShadowDepthVS<ShaderMode>,
|
|
TTranslucencyShadowDepthPS<ShaderMode>> PassShaders;
|
|
|
|
FMaterialShaderTypes ShaderTypes;
|
|
ShaderTypes.AddShaderType<TTranslucencyShadowDepthVS<ShaderMode>>();
|
|
ShaderTypes.AddShaderType<TTranslucencyShadowDepthPS<ShaderMode>>();
|
|
|
|
FVertexFactoryType* VertexFactoryType = VertexFactory->GetType();
|
|
|
|
FMaterialShaders Shaders;
|
|
if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactoryType, Shaders))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Shaders.TryGetVertexShader(PassShaders.VertexShader);
|
|
Shaders.TryGetPixelShader(PassShaders.PixelShader);
|
|
|
|
FMeshPassProcessorRenderState DrawRenderState(PassDrawRenderState);
|
|
|
|
FTranslucencyDepthShaderElementData ShaderElementData;
|
|
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
|
|
|
|
const float LocalToWorldScale = ShadowInfo->GetParentSceneInfo()->Proxy->GetLocalToWorld().GetScaleVector().GetMax();
|
|
const float TranslucentShadowStartOffsetValue = MaterialTranslucentShadowStartOffset * LocalToWorldScale;
|
|
ShaderElementData.TranslucentShadowStartOffset = TranslucentShadowStartOffsetValue / (ShadowInfo->MaxSubjectZ - ShadowInfo->MinSubjectZ);
|
|
|
|
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(PassShaders.VertexShader, PassShaders.PixelShader);
|
|
|
|
BuildMeshDrawCommands(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
MaterialResource,
|
|
DrawRenderState,
|
|
PassShaders,
|
|
MeshFillMode,
|
|
MeshCullMode,
|
|
SortKey,
|
|
EMeshPassFeatures::Default,
|
|
ShaderElementData);
|
|
|
|
return true;
|
|
}
|
|
|
|
void FTranslucencyDepthPassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId)
|
|
{
|
|
if (MeshBatch.CastShadow)
|
|
{
|
|
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
|
|
while (MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
|
|
if (Material && Material->GetRenderingThreadShaderMap())
|
|
{
|
|
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
|
|
}
|
|
}
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FTranslucencyDepthPassParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FTranslucencyDepthPassUniformParameters, PassUniformBuffer)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FProjectedShadowInfo::RenderTranslucencyDepths(FRDGBuilder& GraphBuilder, FSceneRenderer* SceneRenderer, const FRenderTargetBindingSlots& InRenderTargets, FInstanceCullingManager& InstanceCullingManager)
|
|
{
|
|
check(IsInRenderingThread());
|
|
checkSlow(!bWholeSceneShadow);
|
|
SCOPE_CYCLE_COUNTER(STAT_RenderPerObjectShadowDepthsTime);
|
|
|
|
BeginRenderView(GraphBuilder, SceneRenderer->Scene);
|
|
|
|
auto* TranslucencyDepthPassParameters = GraphBuilder.AllocParameters<FTranslucencyDepthPassUniformParameters>();
|
|
SetupTranslucencyDepthPassUniformBuffer(this, GraphBuilder, *ShadowDepthView, *TranslucencyDepthPassParameters);
|
|
TRDGUniformBufferRef<FTranslucencyDepthPassUniformParameters> PassUniformBuffer = GraphBuilder.CreateUniformBuffer(TranslucencyDepthPassParameters);
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FTranslucencyDepthPassParameters>();
|
|
PassParameters->View = ShadowDepthView->ViewUniformBuffer;
|
|
PassParameters->PassUniformBuffer = PassUniformBuffer;
|
|
PassParameters->RenderTargets = InRenderTargets;
|
|
|
|
FSimpleMeshDrawCommandPass* SimpleMeshDrawCommandPass = GraphBuilder.AllocObject<FSimpleMeshDrawCommandPass>(*ShadowDepthView, &InstanceCullingManager);
|
|
|
|
FMeshPassProcessorRenderState DrawRenderState;
|
|
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
DrawRenderState.SetBlendState(TStaticBlendState<
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One,
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI());
|
|
|
|
|
|
FTranslucencyDepthPassMeshProcessor TranslucencyDepthPassMeshProcessor(
|
|
SceneRenderer->Scene,
|
|
ShadowDepthView,
|
|
DrawRenderState,
|
|
this,
|
|
SimpleMeshDrawCommandPass->GetDynamicPassMeshDrawListContext());
|
|
|
|
for (int32 MeshBatchIndex = 0; MeshBatchIndex < DynamicSubjectTranslucentMeshElements.Num(); MeshBatchIndex++)
|
|
{
|
|
const FMeshBatchAndRelevance& MeshAndRelevance = DynamicSubjectTranslucentMeshElements[MeshBatchIndex];
|
|
const uint64 BatchElementMask = ~0ull;
|
|
TranslucencyDepthPassMeshProcessor.AddMeshBatch(*MeshAndRelevance.Mesh, BatchElementMask, MeshAndRelevance.PrimitiveSceneProxy);
|
|
}
|
|
|
|
for (int32 PrimitiveIndex = 0; PrimitiveIndex < SubjectTranslucentPrimitives.Num(); PrimitiveIndex++)
|
|
{
|
|
const FPrimitiveSceneInfo* PrimitiveSceneInfo = SubjectTranslucentPrimitives[PrimitiveIndex];
|
|
int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
|
|
FPrimitiveViewRelevance ViewRelevance = ShadowDepthView->PrimitiveViewRelevanceMap[PrimitiveId];
|
|
|
|
if (!ViewRelevance.bInitializedThisFrame)
|
|
{
|
|
// Compute the subject primitive's view relevance since it wasn't cached
|
|
ViewRelevance = PrimitiveSceneInfo->Proxy->GetViewRelevance(ShadowDepthView);
|
|
}
|
|
|
|
if (ViewRelevance.bDrawRelevance && ViewRelevance.bStaticRelevance)
|
|
{
|
|
int8 MinLOD, MaxLOD;
|
|
PrimitiveSceneInfo->GetStaticMeshesLODRange(MinLOD, MaxLOD);
|
|
// For any primitive, we only render LOD0 meshes since we do not have FSceneView available to use ComputeLODForMeshes.
|
|
for (int32 MeshIndex = 0; MeshIndex < PrimitiveSceneInfo->StaticMeshes.Num(); MeshIndex++)
|
|
{
|
|
const FStaticMeshBatch& StaticMeshBatch = PrimitiveSceneInfo->StaticMeshes[MeshIndex];
|
|
if (StaticMeshBatch.LODIndex != MinLOD)
|
|
{
|
|
continue;
|
|
}
|
|
const uint64 DefaultBatchElementMask = ~0ul;
|
|
TranslucencyDepthPassMeshProcessor.AddMeshBatch(StaticMeshBatch, DefaultBatchElementMask, StaticMeshBatch.PrimitiveSceneInfo->Proxy, StaticMeshBatch.Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
SimpleMeshDrawCommandPass->BuildRenderingCommands(GraphBuilder, *ShadowDepthView, SceneRenderer->Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
|
|
|
|
|
FString EventName;
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
if (GetEmitDrawEvents())
|
|
{
|
|
GetShadowTypeNameForDrawEvent(EventName);
|
|
}
|
|
#endif
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("%s", *EventName),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, SimpleMeshDrawCommandPass, PassParameters](FRHICommandList& RHICmdList)
|
|
{
|
|
FMeshPassProcessorRenderState DrawRenderState;
|
|
|
|
// Clear the shadow and its border
|
|
RHICmdList.SetViewport(
|
|
X,
|
|
Y,
|
|
0.0f,
|
|
(X + BorderSize * 2 + ResolutionX),
|
|
(Y + BorderSize * 2 + ResolutionY),
|
|
1.0f
|
|
);
|
|
|
|
FLinearColor ClearColors[2] = { FLinearColor(0,0,0,0), FLinearColor(0,0,0,0) };
|
|
DrawClearQuadMRT(RHICmdList, true, UE_ARRAY_COUNT(ClearColors), ClearColors, false, 1.0f, false, 0);
|
|
|
|
// Set the viewport for the shadow.
|
|
RHICmdList.SetViewport(
|
|
(X + BorderSize),
|
|
(Y + BorderSize),
|
|
0.0f,
|
|
(X + BorderSize + ResolutionX),
|
|
(Y + BorderSize + ResolutionY),
|
|
1.0f
|
|
);
|
|
SimpleMeshDrawCommandPass->SubmitDraw(RHICmdList, PassParameters->InstanceCullingDrawParams);
|
|
});
|
|
}
|
|
|
|
/** Pixel shader used to filter a single volume lighting cascade. */
|
|
class FFilterTranslucentVolumePS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FFilterTranslucentVolumePS);
|
|
SHADER_USE_PARAMETER_STRUCT(FFilterTranslucentVolumePS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeDirectional)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, TranslucencyLightingVolumeAmbientSampler)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, TranslucencyLightingVolumeDirectionalSampler)
|
|
SHADER_PARAMETER(float, TexelSize)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && (RHISupportsGeometryShaders(Parameters.Platform) || RHISupportsVertexShaderLayer(Parameters.Platform));
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FFilterTranslucentVolumePS, "/Engine/Private/TranslucentLightingShaders.usf", "FilterMainPS", SF_Pixel);
|
|
|
|
/** Shader that adds direct lighting contribution from the given light to the current volume lighting cascade. */
|
|
class FTranslucentLightingInjectPS : public FMaterialShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FTranslucentLightingInjectPS, Material);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FDeferredLightUniformStruct, DeferredLight)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FVolumeShadowingShaderParameters, VolumeShadowingParameters)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMapSamplingParameters)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FLightCloudTransmittanceParameters, LightCloudTransmittanceParameters)
|
|
SHADER_PARAMETER(FMatrix44f, LightFunctionTranslatedWorldToLight)
|
|
SHADER_PARAMETER(FVector4f, LightFunctionParameters)
|
|
SHADER_PARAMETER(float, SpotlightMask)
|
|
SHADER_PARAMETER(uint32, VolumeCascadeIndex)
|
|
SHADER_PARAMETER(int32, VirtualShadowMapId)
|
|
SHADER_PARAMETER(uint32, AtmospherePerPixelTransmittanceEnabled)
|
|
SHADER_PARAMETER(uint32, VolumetricCloudShadowEnabled)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FRadialAttenuation : SHADER_PERMUTATION_BOOL("RADIAL_ATTENUATION");
|
|
class FDynamicallyShadowed : SHADER_PERMUTATION_BOOL("DYNAMICALLY_SHADOWED");
|
|
class FLightFunction : SHADER_PERMUTATION_BOOL("APPLY_LIGHT_FUNCTION");
|
|
class FVirtualShadowMap : SHADER_PERMUTATION_BOOL("VIRTUAL_SHADOW_MAP");
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<
|
|
FRadialAttenuation,
|
|
FDynamicallyShadowed,
|
|
FLightFunction,
|
|
FVirtualShadowMap >;
|
|
|
|
public:
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment )
|
|
{
|
|
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
|
|
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("INJECTION_PIXEL_SHADER"), 1);
|
|
}
|
|
|
|
/**
|
|
* Makes sure only shaders for materials that are explicitly flagged
|
|
* as 'UsedAsLightFunction' in the Material Editor gets compiled into
|
|
* the shader cache.
|
|
*/
|
|
static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return (Parameters.MaterialParameters.MaterialDomain == MD_LightFunction || Parameters.MaterialParameters.bIsSpecialEngineMaterial) &&
|
|
(IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) &&
|
|
(RHISupportsGeometryShaders(Parameters.Platform) || RHISupportsVertexShaderLayer(Parameters.Platform)));
|
|
}
|
|
|
|
FTranslucentLightingInjectPS(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);
|
|
}
|
|
|
|
FTranslucentLightingInjectPS() {}
|
|
|
|
void SetParameters(
|
|
FRHICommandList& RHICmdList,
|
|
const FViewInfo& View,
|
|
const FMaterialRenderProxy* MaterialProxy)
|
|
{
|
|
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
|
|
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy);
|
|
FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialProxy, Material, View);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(,FTranslucentLightingInjectPS, TEXT("/Engine/Private/TranslucentLightInjectionShaders.usf"), TEXT("InjectMainPS"), SF_Pixel);
|
|
|
|
class FClearTranslucentLightingVolumeCS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FClearTranslucentLightingVolumeCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FClearTranslucentLightingVolumeCS, FGlobalShader)
|
|
|
|
static const int32 CLEAR_BLOCK_SIZE = 4;
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D<float4>, RWAmbient0)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D<float4>, RWDirectional0)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D<float4>, RWAmbient1)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D<float4>, RWDirectional1)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("CLEAR_COMPUTE_SHADER"), 1);
|
|
OutEnvironment.SetDefine(TEXT("CLEAR_BLOCK_SIZE"), CLEAR_BLOCK_SIZE);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FClearTranslucentLightingVolumeCS, "/Engine/Private/TranslucentLightInjectionShaders.usf", "ClearTranslucentLightingVolumeCS", SF_Compute);
|
|
|
|
int32 FTranslucencyLightingVolumeTextures::GetIndex(const FViewInfo& View, int32 CascadeIndex) const
|
|
{
|
|
// if we only have one view or one stereo pair we can just use primary index
|
|
if (Directional.Num() == TVC_MAX)
|
|
{
|
|
return (View.PrimaryViewIndex * TVC_MAX) + CascadeIndex;
|
|
}
|
|
else
|
|
{
|
|
// support uncommon but possible (in theory) situations, like a stereo pair and also multiple views
|
|
return (ViewsToTexturePairs[View.PrimaryViewIndex] * TVC_MAX) + CascadeIndex;
|
|
}
|
|
}
|
|
|
|
void FTranslucencyLightingVolumeTextures::Init(FRDGBuilder& GraphBuilder, TArrayView<const FViewInfo> Views, ERDGPassFlags PassFlags)
|
|
{
|
|
check(PassFlags == ERDGPassFlags::Compute || PassFlags == ERDGPassFlags::AsyncCompute);
|
|
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, TranslucentLighting);
|
|
|
|
VolumeDim = GetTranslucencyLightingVolumeDim();
|
|
const FIntVector TranslucencyLightingVolumeDim(VolumeDim);
|
|
|
|
// calculate the number of textures needed given that for each stereo pair the primary view's textures will be shared between the "eyes"
|
|
const int32 ViewCount = Views.Num();
|
|
uint32 NumViewsWithTextures = 0;
|
|
ViewsToTexturePairs.SetNumZeroed(Views.Num());
|
|
for (int32 ViewIndex = 0, NumViews = Views.Num(); ViewIndex < NumViews; ++ViewIndex)
|
|
{
|
|
ViewsToTexturePairs[ViewIndex] = NumViewsWithTextures;
|
|
NumViewsWithTextures += (ViewIndex == Views[ViewIndex].PrimaryViewIndex) ? 1 : 0; // this will add 0 for those views who aren't primary
|
|
}
|
|
check(NumViewsWithTextures > 0);
|
|
{
|
|
// TODO: We can skip the and TLV allocations when rendering in forward shading mode
|
|
const ETextureCreateFlags TranslucencyTargetFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_ReduceMemoryWithTilingMode | TexCreate_UAV;
|
|
|
|
Ambient.SetNum(NumViewsWithTextures * TVC_MAX);
|
|
Directional.SetNum(NumViewsWithTextures * TVC_MAX);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < ViewCount; ++ViewIndex)
|
|
{
|
|
for (int32 CascadeIndex = 0; CascadeIndex < TVC_MAX; ++CascadeIndex)
|
|
{
|
|
const uint32 TextureIndex = FTranslucencyLightingVolumeTextures::GetIndex(Views[ViewIndex], CascadeIndex);
|
|
check(TextureIndex <= NumViewsWithTextures * TVC_MAX);
|
|
|
|
const FRDGEventName& AmbientName = *GraphBuilder.AllocObject<FRDGEventName>(RDG_EVENT_NAME("TranslucentVolumeAmbient%d", TextureIndex));
|
|
const FRDGEventName& DirectionalName = *GraphBuilder.AllocObject<FRDGEventName>(RDG_EVENT_NAME("TranslucentVolumeDirectional%d", TextureIndex));
|
|
|
|
FRDGTextureRef AmbientTexture = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create3D(
|
|
TranslucencyLightingVolumeDim,
|
|
PF_FloatRGBA,
|
|
FClearValueBinding::Transparent,
|
|
TranslucencyTargetFlags),
|
|
AmbientName.GetTCHAR());
|
|
|
|
FRDGTextureRef DirectionalTexture = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create3D(
|
|
TranslucencyLightingVolumeDim,
|
|
PF_FloatRGBA,
|
|
FClearValueBinding::Transparent,
|
|
TranslucencyTargetFlags),
|
|
DirectionalName.GetTCHAR());
|
|
|
|
Ambient[TextureIndex] = AmbientTexture;
|
|
Directional[TextureIndex] = DirectionalTexture;
|
|
}
|
|
}
|
|
}
|
|
|
|
const FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(TranslucencyLightingVolumeDim, FClearTranslucentLightingVolumeCS::CLEAR_BLOCK_SIZE);
|
|
|
|
TShaderMapRef<FClearTranslucentLightingVolumeCS> ComputeShader(Views[0].ShaderMap);
|
|
|
|
for (uint32 TexturePairIndex = 0; TexturePairIndex < NumViewsWithTextures; ++TexturePairIndex)
|
|
{
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FClearTranslucentLightingVolumeCS::FParameters>();
|
|
PassParameters->RWAmbient0 = GraphBuilder.CreateUAV(Ambient[TexturePairIndex * TVC_MAX]);
|
|
PassParameters->RWAmbient1 = GraphBuilder.CreateUAV(Ambient[TexturePairIndex * TVC_MAX + 1]);
|
|
PassParameters->RWDirectional0 = GraphBuilder.CreateUAV(Directional[TexturePairIndex * TVC_MAX]);
|
|
PassParameters->RWDirectional1 = GraphBuilder.CreateUAV(Directional[TexturePairIndex * TVC_MAX + 1]);
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("ClearTranslucencyLightingVolumeCompute %d", VolumeDim),
|
|
PassFlags,
|
|
ComputeShader,
|
|
PassParameters,
|
|
GroupCount);
|
|
}
|
|
}
|
|
|
|
FTranslucencyLightingVolumeParameters GetTranslucencyLightingVolumeParameters(FRDGBuilder& GraphBuilder, const FTranslucencyLightingVolumeTextures& Textures, const FViewInfo& View)
|
|
{
|
|
FTranslucencyLightingVolumeParameters Parameters;
|
|
if (Textures.IsValid())
|
|
{
|
|
const uint32 InnerIndex = Textures.GetIndex(View, TVC_Inner);
|
|
const uint32 OuterIndex = Textures.GetIndex(View, TVC_Outer);
|
|
|
|
Parameters.TranslucencyLightingVolumeAmbientInner = Textures.Ambient[InnerIndex];
|
|
Parameters.TranslucencyLightingVolumeAmbientOuter = Textures.Ambient[OuterIndex];
|
|
Parameters.TranslucencyLightingVolumeDirectionalInner = Textures.Directional[InnerIndex];
|
|
Parameters.TranslucencyLightingVolumeDirectionalOuter = Textures.Directional[OuterIndex];
|
|
}
|
|
else
|
|
{
|
|
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder);
|
|
Parameters.TranslucencyLightingVolumeAmbientInner = SystemTextures.VolumetricBlack;
|
|
Parameters.TranslucencyLightingVolumeAmbientOuter = SystemTextures.VolumetricBlack;
|
|
Parameters.TranslucencyLightingVolumeDirectionalInner = SystemTextures.VolumetricBlack;
|
|
Parameters.TranslucencyLightingVolumeDirectionalOuter = SystemTextures.VolumetricBlack;
|
|
}
|
|
return Parameters;
|
|
}
|
|
|
|
class FInjectAmbientCubemapPS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FInjectAmbientCubemapPS);
|
|
SHADER_USE_PARAMETER_STRUCT(FInjectAmbientCubemapPS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FAmbientCubemapParameters, AmbientCubemap)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FInjectAmbientCubemapPS, "/Engine/Private/TranslucentLightingShaders.usf", "InjectAmbientCubemapMainPS", SF_Pixel);
|
|
|
|
void InjectTranslucencyLightingVolumeAmbientCubemap(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TArrayView<const FViewInfo> Views,
|
|
const FTranslucencyLightingVolumeTextures& Textures)
|
|
{
|
|
if (!GUseTranslucentLightingVolumes || !GSupportsVolumeTextureRendering)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "InjectAmbientCubemapTranslucentVolumeLighting");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, TranslucentLighting);
|
|
|
|
const int32 TranslucencyLightingVolumeDim = Textures.VolumeDim;
|
|
const FVolumeBounds VolumeBounds(TranslucencyLightingVolumeDim);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
|
|
|
for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; ++VolumeCascadeIndex)
|
|
{
|
|
FRDGTextureRef VolumeAmbientTexture = Textures.GetAmbientTexture(View, VolumeCascadeIndex);
|
|
|
|
for (const FFinalPostProcessSettings::FCubemapEntry& CubemapEntry : View.FinalPostProcessSettings.ContributingCubemaps)
|
|
{
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FInjectAmbientCubemapPS::FParameters>();
|
|
SetupAmbientCubemapParameters(CubemapEntry, &PassParameters->AmbientCubemap);
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(VolumeAmbientTexture, ERenderTargetLoadAction::ELoad);
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("Cascade %d", VolumeCascadeIndex),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[&View, PassParameters, VolumeBounds, TranslucencyLightingVolumeDim](FRHICommandList& RHICmdList)
|
|
{
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(View.ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
|
|
TShaderMapRef<FInjectAmbientCubemapPS> PixelShader(View.ShaderMap);
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.SetGeometryShader(GeometryShader.GetGeometryShader());
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, FIntVector(TranslucencyLightingVolumeDim));
|
|
if (GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds.MinZ);
|
|
}
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Calculates volume texture bounds for the given light in the given translucent lighting volume cascade. */
|
|
FVolumeBounds CalculateLightVolumeBounds(const FSphere& LightBounds, const FViewInfo& View, uint32 VolumeCascadeIndex, bool bDirectionalLight)
|
|
{
|
|
const int32 TranslucencyLightingVolumeDim = GetTranslucencyLightingVolumeDim();
|
|
|
|
FVolumeBounds VolumeBounds;
|
|
|
|
if (bDirectionalLight)
|
|
{
|
|
VolumeBounds = FVolumeBounds(TranslucencyLightingVolumeDim);
|
|
}
|
|
else
|
|
{
|
|
// Determine extents in the volume texture
|
|
const FVector MinPosition = (LightBounds.Center - LightBounds.W - View.TranslucencyLightingVolumeMin[VolumeCascadeIndex]) / View.TranslucencyVolumeVoxelSize[VolumeCascadeIndex];
|
|
const FVector MaxPosition = (LightBounds.Center + LightBounds.W - View.TranslucencyLightingVolumeMin[VolumeCascadeIndex]) / View.TranslucencyVolumeVoxelSize[VolumeCascadeIndex];
|
|
|
|
VolumeBounds.MinX = FMath::Max(FMath::TruncToInt(MinPosition.X), 0);
|
|
VolumeBounds.MinY = FMath::Max(FMath::TruncToInt(MinPosition.Y), 0);
|
|
VolumeBounds.MinZ = FMath::Max(FMath::TruncToInt(MinPosition.Z), 0);
|
|
|
|
VolumeBounds.MaxX = FMath::Min(FMath::TruncToInt(MaxPosition.X) + 1, TranslucencyLightingVolumeDim);
|
|
VolumeBounds.MaxY = FMath::Min(FMath::TruncToInt(MaxPosition.Y) + 1, TranslucencyLightingVolumeDim);
|
|
VolumeBounds.MaxZ = FMath::Min(FMath::TruncToInt(MaxPosition.Z) + 1, TranslucencyLightingVolumeDim);
|
|
}
|
|
|
|
return VolumeBounds;
|
|
}
|
|
|
|
/**
|
|
* Information about a light to be injected.
|
|
* Cached in this struct to avoid recomputing multiple times (multiple cascades).
|
|
*/
|
|
struct FTranslucentLightInjectionData
|
|
{
|
|
// must not be 0
|
|
const FLightSceneInfo* LightSceneInfo;
|
|
// can be 0
|
|
const FProjectedShadowInfo* ProjectedShadowInfo;
|
|
//
|
|
bool bApplyLightFunction;
|
|
// must not be 0
|
|
const FMaterialRenderProxy* LightFunctionMaterialProxy;
|
|
};
|
|
|
|
/**
|
|
* Adds a light to LightInjectionData if it should be injected into the translucent volume, and caches relevant information in a FTranslucentLightInjectionData.
|
|
* @param InProjectedShadowInfo is 0 for unshadowed lights
|
|
*/
|
|
static void AddLightForInjection(
|
|
const FViewInfo& View,
|
|
TArrayView<const FVisibleLightInfo> VisibleLightInfos,
|
|
const FLightSceneInfo& LightSceneInfo,
|
|
const FProjectedShadowInfo* InProjectedShadowInfo,
|
|
TArray<FTranslucentLightInjectionData, SceneRenderingAllocator>& LightInjectionData)
|
|
{
|
|
if (LightSceneInfo.Proxy->AffectsTranslucentLighting())
|
|
{
|
|
const FVisibleLightInfo& VisibleLightInfo = VisibleLightInfos[LightSceneInfo.Id];
|
|
const ERHIFeatureLevel::Type FeatureLevel = View.FeatureLevel;
|
|
|
|
const bool bApplyLightFunction = (View.Family->EngineShowFlags.LightFunctions &&
|
|
LightSceneInfo.Proxy->GetLightFunctionMaterial() &&
|
|
LightSceneInfo.Proxy->GetLightFunctionMaterial()->GetIncompleteMaterialWithFallback(FeatureLevel).IsLightFunction());
|
|
|
|
const FMaterialRenderProxy* MaterialProxy = bApplyLightFunction ?
|
|
LightSceneInfo.Proxy->GetLightFunctionMaterial() :
|
|
UMaterial::GetDefaultMaterial(MD_LightFunction)->GetRenderProxy();
|
|
|
|
// Skip rendering if the DefaultLightFunctionMaterial isn't compiled yet
|
|
if (MaterialProxy->GetIncompleteMaterialWithFallback(FeatureLevel).IsLightFunction())
|
|
{
|
|
FTranslucentLightInjectionData InjectionData;
|
|
InjectionData.LightSceneInfo = &LightSceneInfo;
|
|
InjectionData.ProjectedShadowInfo = InProjectedShadowInfo;
|
|
InjectionData.bApplyLightFunction = bApplyLightFunction;
|
|
InjectionData.LightFunctionMaterialProxy = MaterialProxy;
|
|
LightInjectionData.Add(InjectionData);
|
|
}
|
|
}
|
|
}
|
|
|
|
static FRDGTextureRef GetSkyTransmittanceLutTexture(FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View)
|
|
{
|
|
FRDGTextureRef TransmittanceLutTexture = nullptr;
|
|
if (ShouldRenderSkyAtmosphere(Scene, View.Family->EngineShowFlags))
|
|
{
|
|
if (const FSkyAtmosphereRenderSceneInfo* SkyInfo = Scene->GetSkyAtmosphereSceneInfo())
|
|
{
|
|
TransmittanceLutTexture = SkyInfo->GetTransmittanceLutTexture(GraphBuilder);
|
|
}
|
|
}
|
|
return TransmittanceLutTexture;
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FInjectTranslucentLightArrayParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FTranslucentLightingInjectPS::FParameters, PS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FVolumetricCloudShadowAOParameters, CloudShadowAO)
|
|
RDG_TEXTURE_ACCESS(TransmittanceLutTexture, ERHIAccess::SRVGraphics)
|
|
RDG_TEXTURE_ACCESS(ShadowDepthTexture, ERHIAccess::SRVGraphics)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
/** Injects all the lights in LightInjectionData into the translucent lighting volume textures. */
|
|
static void InjectTranslucentLightArray(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const uint32 ViewIndex,
|
|
const FScene* Scene,
|
|
const FSceneRenderer& Renderer,
|
|
const FTranslucencyLightingVolumeTextures& Textures,
|
|
TArrayView<const FTranslucentLightInjectionData> LightInjectionData)
|
|
{
|
|
INC_DWORD_STAT_BY(STAT_NumLightsInjectedIntoTranslucency, LightInjectionData.Num());
|
|
|
|
const FVolumetricCloudShadowAOParameters CloudShadowAOParameters = GetCloudShadowAOParameters(GraphBuilder, View, Scene->GetVolumetricCloudSceneInfo());
|
|
|
|
FRDGTextureRef TransmittanceLutTexture = GetSkyTransmittanceLutTexture(GraphBuilder, Scene, View);
|
|
|
|
// Inject into each volume cascade. Operate on one cascade at a time to reduce render target switches.
|
|
for (uint32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++)
|
|
{
|
|
// for stereo case, using PrimaryViewIndex essentially shares the lighting volume textures
|
|
const uint32 TextureIndex = Textures.GetIndex(View, VolumeCascadeIndex);
|
|
FRDGTextureRef VolumeAmbientTexture = Textures.Ambient[TextureIndex];
|
|
FRDGTextureRef VolumeDirectionalTexture = Textures.Directional[TextureIndex];
|
|
|
|
for (int32 LightIndex = 0; LightIndex < LightInjectionData.Num(); LightIndex++)
|
|
{
|
|
const FTranslucentLightInjectionData& InjectionData = LightInjectionData[LightIndex];
|
|
const FLightSceneInfo* const LightSceneInfo = InjectionData.LightSceneInfo;
|
|
const FVisibleLightInfo& VisibleLightInfo = Renderer.ActiveViewFamily->VisibleLightInfos[LightSceneInfo->Id];
|
|
const bool bInverseSquared = LightSceneInfo->Proxy->IsInverseSquared();
|
|
const bool bDirectionalLight = LightSceneInfo->Proxy->GetLightType() == LightType_Directional;
|
|
const bool bUseVSM = Renderer.ActiveViewFamily->VirtualShadowMapArray.IsAllocated();
|
|
|
|
const FVolumeBounds VolumeBounds = CalculateLightVolumeBounds(LightSceneInfo->Proxy->GetBoundingSphere(), View, VolumeCascadeIndex, bDirectionalLight);
|
|
if (VolumeBounds.IsValid())
|
|
{
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(View.ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
|
|
|
|
FRDGTextureRef ShadowDepthTexture = nullptr;
|
|
|
|
if (InjectionData.ProjectedShadowInfo)
|
|
{
|
|
ShadowDepthTexture = TryRegisterExternalTexture(GraphBuilder, InjectionData.ProjectedShadowInfo->RenderTargets.DepthTarget);
|
|
}
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters< FInjectTranslucentLightArrayParameters >();
|
|
PassParameters->TransmittanceLutTexture = TransmittanceLutTexture;
|
|
PassParameters->ShadowDepthTexture = ShadowDepthTexture;
|
|
PassParameters->CloudShadowAO = CloudShadowAOParameters;
|
|
PassParameters->PS.VirtualShadowMapSamplingParameters = Renderer.ActiveViewFamily->VirtualShadowMapArray.GetSamplingParameters(GraphBuilder);
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(VolumeAmbientTexture, ERenderTargetLoadAction::ELoad);
|
|
PassParameters->RenderTargets[1] = FRenderTargetBinding(VolumeDirectionalTexture, ERenderTargetLoadAction::ELoad);
|
|
|
|
PassParameters->PS.ViewUniformBuffer = View.ViewUniformBuffer;
|
|
|
|
FDeferredLightUniformStruct* DeferredLightStruct = GraphBuilder.AllocParameters<FDeferredLightUniformStruct>();
|
|
*DeferredLightStruct = GetDeferredLightParameters(View, *LightSceneInfo);
|
|
PassParameters->PS.DeferredLight = GraphBuilder.CreateUniformBuffer(DeferredLightStruct);
|
|
|
|
GetVolumeShadowingShaderParameters(GraphBuilder, View, LightSceneInfo, InjectionData.ProjectedShadowInfo, PassParameters->PS.VolumeShadowingParameters);
|
|
|
|
PassParameters->PS.VirtualShadowMapId = Renderer.ActiveViewFamily->VisibleLightInfos[LightSceneInfo->Id].GetVirtualShadowMapId(&View);
|
|
PassParameters->PS.LightFunctionParameters = FLightFunctionSharedParameters::GetLightFunctionSharedParameters(LightSceneInfo, 1.0f);
|
|
PassParameters->PS.VolumeCascadeIndex = VolumeCascadeIndex;
|
|
|
|
bool bIsSpotlight = LightSceneInfo->Proxy->GetLightType() == LightType_Spot;
|
|
PassParameters->PS.SpotlightMask = bIsSpotlight ? 1.0f : 0.0f; //@todo - needs to be a permutation to reduce shadow filtering work
|
|
|
|
{
|
|
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(InverseScale);
|
|
const FMatrix TranslatedWorldToWorld = FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation());
|
|
|
|
PassParameters->PS.LightFunctionTranslatedWorldToLight = FMatrix44f(TranslatedWorldToWorld * WorldToLight);
|
|
}
|
|
|
|
const bool bCloudShadowEnabled = SetupLightCloudTransmittanceParameters(GraphBuilder, Scene, View, LightSceneInfo, PassParameters->PS.LightCloudTransmittanceParameters);
|
|
PassParameters->PS.VolumetricCloudShadowEnabled = bCloudShadowEnabled ? 1 : 0;
|
|
|
|
PassParameters->PS.AtmospherePerPixelTransmittanceEnabled = IsLightAtmospherePerPixelTransmittanceEnabled(Scene, View, LightSceneInfo);
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("InjectTranslucentLightArray"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[PassParameters, VertexShader, GeometryShader, &View, &Renderer, &InjectionData, LightSceneInfo, bDirectionalLight, bUseVSM, VolumeBounds, VolumeCascadeIndex](FRHICommandList& RHICmdList)
|
|
{
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
|
|
if (bDirectionalLight)
|
|
{
|
|
// Accumulate the contribution of multiple lights
|
|
// Directional lights write their shadowing into alpha of the ambient texture
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One,
|
|
CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
|
|
}
|
|
else
|
|
{
|
|
// Accumulate the contribution of multiple lights
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<
|
|
CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One,
|
|
CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One>::GetRHI();
|
|
}
|
|
|
|
const FMaterialRenderProxy* MaterialProxy = InjectionData.LightFunctionMaterialProxy;
|
|
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback( View.GetFeatureLevel(), MaterialProxy );
|
|
const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap();
|
|
|
|
FTranslucentLightingInjectPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FRadialAttenuation >( !bDirectionalLight );
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FDynamicallyShadowed >( InjectionData.ProjectedShadowInfo != nullptr );
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FLightFunction >( InjectionData.bApplyLightFunction );
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FVirtualShadowMap >( bUseVSM );
|
|
|
|
auto PixelShader = MaterialShaderMap->GetShader< FTranslucentLightingInjectPS >( PermutationVector );
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.SetGeometryShader(GeometryShader.GetGeometryShader());
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
const int32 TranslucencyLightingVolumeDim = GetTranslucencyLightingVolumeDim();
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, FIntVector(TranslucencyLightingVolumeDim));
|
|
if (GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds.MinZ);
|
|
}
|
|
|
|
PixelShader->SetParameters(RHICmdList, View, InjectionData.LightFunctionMaterialProxy);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InjectTranslucencyLightingVolume(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const uint32 ViewIndex,
|
|
const FScene* Scene,
|
|
const FSceneRenderer& Renderer,
|
|
const FTranslucencyLightingVolumeTextures& Textures,
|
|
TArrayView<const FVisibleLightInfo> VisibleLightInfos,
|
|
const FLightSceneInfo& LightSceneInfo,
|
|
const FProjectedShadowInfo* ProjectedShadowInfo)
|
|
{
|
|
if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime);
|
|
|
|
auto& LightInjectionData = *GraphBuilder.AllocObject<TArray<FTranslucentLightInjectionData, SceneRenderingAllocator>>();
|
|
AddLightForInjection(View, VisibleLightInfos, LightSceneInfo, ProjectedShadowInfo, LightInjectionData);
|
|
InjectTranslucentLightArray(GraphBuilder, View, ViewIndex, Scene, Renderer, Textures, LightInjectionData);
|
|
}
|
|
}
|
|
|
|
void InjectTranslucencyLightingVolumeArray(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TArrayView<const FViewInfo> Views,
|
|
const FScene* Scene,
|
|
const FSceneRenderer& Renderer,
|
|
const FTranslucencyLightingVolumeTextures& Textures,
|
|
const TArrayView<const FVisibleLightInfo> VisibleLightInfos,
|
|
TArrayView<const FSortedLightSceneInfo> SortedLights,
|
|
TInterval<int32> SortedLightInterval)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime);
|
|
|
|
using FLightInjectionData = TArray<TArray<FTranslucentLightInjectionData, SceneRenderingAllocator>>;
|
|
auto& LightInjectionData = *GraphBuilder.AllocObject<FLightInjectionData>();
|
|
LightInjectionData.SetNum(Views.Num());
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < LightInjectionData.Num(); ++ViewIndex)
|
|
{
|
|
LightInjectionData[ViewIndex].Reserve(SortedLightInterval.Size());
|
|
}
|
|
|
|
for (int32 LightIndex = SortedLightInterval.Min; LightIndex < SortedLightInterval.Max; LightIndex++)
|
|
{
|
|
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
|
|
const FLightSceneInfo* const LightSceneInfo = SortedLightInfo.LightSceneInfo;
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
if (LightSceneInfo->ShouldRenderLight(View))
|
|
{
|
|
AddLightForInjection(View, VisibleLightInfos, *LightSceneInfo, nullptr, LightInjectionData[ViewIndex]);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
// non-shadowed, non-light function lights
|
|
InjectTranslucentLightArray(GraphBuilder, View, ViewIndex, Scene, Renderer, Textures, LightInjectionData[ViewIndex]);
|
|
}
|
|
}
|
|
|
|
class FSimpleLightTranslucentLightingInjectPS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FSimpleLightTranslucentLightingInjectPS);
|
|
SHADER_USE_PARAMETER_STRUCT(FSimpleLightTranslucentLightingInjectPS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER(FVector4f, SimpleLightPositionAndRadius)
|
|
SHADER_PARAMETER(FVector4f, SimpleLightColorAndExponent)
|
|
SHADER_PARAMETER(uint32, VolumeCascadeIndex)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && (RHISupportsGeometryShaders(Parameters.Platform) || RHISupportsVertexShaderLayer(Parameters.Platform));
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FSimpleLightTranslucentLightingInjectPS, "/Engine/Private/TranslucentLightInjectionShaders.usf", "SimpleLightInjectMainPS", SF_Pixel);
|
|
|
|
void InjectSimpleTranslucencyLightingVolumeArray(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const uint32 ViewIndex,
|
|
const uint32 ViewCount,
|
|
const FTranslucencyLightingVolumeTextures& Textures,
|
|
const FSimpleLightArray& SimpleLights)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime);
|
|
|
|
int32 NumLightsToInject = 0;
|
|
|
|
for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++)
|
|
{
|
|
if (SimpleLights.InstanceData[LightIndex].bAffectTranslucency)
|
|
{
|
|
NumLightsToInject++;
|
|
}
|
|
}
|
|
|
|
if (NumLightsToInject > 0)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "InjectSimpleTranslucentLightArray");
|
|
|
|
INC_DWORD_STAT_BY(STAT_NumLightsInjectedIntoTranslucency, NumLightsToInject);
|
|
|
|
const int32 TranslucencyLightingVolumeDim = GetTranslucencyLightingVolumeDim();
|
|
|
|
// Inject into each volume cascade
|
|
// Operate on one cascade at a time to reduce render target switches
|
|
for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++)
|
|
{
|
|
const uint32 TextureIndex = Textures.GetIndex(View, VolumeCascadeIndex);
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "Cascade%d", VolumeCascadeIndex);
|
|
FRDGTextureRef VolumeAmbientTexture = Textures.Ambient[TextureIndex];
|
|
FRDGTextureRef VolumeDirectionalTexture = Textures.Directional[TextureIndex];
|
|
|
|
for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++)
|
|
{
|
|
const FSimpleLightEntry& SimpleLight = SimpleLights.InstanceData[LightIndex];
|
|
const FSimpleLightPerViewEntry& SimpleLightPerViewData = SimpleLights.GetViewDependentData(LightIndex, ViewIndex, ViewCount);
|
|
|
|
if (SimpleLight.bAffectTranslucency)
|
|
{
|
|
const FSphere LightBounds(SimpleLightPerViewData.Position, SimpleLight.Radius);
|
|
const FVolumeBounds VolumeBounds = CalculateLightVolumeBounds(LightBounds, View, VolumeCascadeIndex, false);
|
|
|
|
if (VolumeBounds.IsValid())
|
|
{
|
|
const FVector3f TranslatedLightPosition = FVector3f(SimpleLightPerViewData.Position + View.ViewMatrices.GetPreViewTranslation());
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FSimpleLightTranslucentLightingInjectPS::FParameters>();
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
PassParameters->VolumeCascadeIndex = VolumeCascadeIndex;
|
|
PassParameters->SimpleLightPositionAndRadius = FVector4f(TranslatedLightPosition, SimpleLight.Radius);
|
|
PassParameters->SimpleLightColorAndExponent = FVector4f((FVector3f)SimpleLight.Color, SimpleLight.Exponent);
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(VolumeAmbientTexture, ERenderTargetLoadAction::ELoad);
|
|
PassParameters->RenderTargets[1] = FRenderTargetBinding(VolumeDirectionalTexture, ERenderTargetLoadAction::ELoad);
|
|
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(View.ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
|
|
TShaderMapRef<FSimpleLightTranslucentLightingInjectPS> PixelShader(View.ShaderMap);
|
|
|
|
GraphBuilder.AddPass(
|
|
{},
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[VertexShader, GeometryShader, PixelShader, PassParameters, VolumeBounds, TranslucencyLightingVolumeDim](FRHICommandList& RHICmdList)
|
|
{
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
// Accumulate the contribution of multiple lights
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<
|
|
CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One,
|
|
CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One>::GetRHI();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.SetGeometryShader(GeometryShader.GetGeometryShader());
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, FIntVector(TranslucencyLightingVolumeDim));
|
|
if (GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds.MinZ);
|
|
}
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FilterTranslucencyLightingVolume(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TArrayView<const FViewInfo> Views,
|
|
FTranslucencyLightingVolumeTextures& Textures)
|
|
{
|
|
if (!GUseTranslucentLightingVolumes || !GSupportsVolumeTextureRendering || !GUseTranslucencyVolumeBlur)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FRHISamplerState* SamplerStateRHI = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
|
|
const int32 TranslucencyLightingVolumeDim = GetTranslucencyLightingVolumeDim();
|
|
RDG_EVENT_SCOPE(GraphBuilder, "FilterTranslucentVolume %dx%dx%d Cascades:%d", TranslucencyLightingVolumeDim, TranslucencyLightingVolumeDim, TranslucencyLightingVolumeDim, TVC_MAX);
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, TranslucentLighting);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
|
|
|
for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++)
|
|
{
|
|
const uint32 TextureIndex = Textures.GetIndex(View, VolumeCascadeIndex);
|
|
|
|
FRDGTextureRef InputVolumeAmbientTexture = Textures.Ambient[TextureIndex];
|
|
FRDGTextureRef InputVolumeDirectionalTexture = Textures.Directional[TextureIndex];
|
|
|
|
FRDGTextureRef OutputVolumeAmbientTexture = GraphBuilder.CreateTexture(InputVolumeAmbientTexture->Desc, InputVolumeAmbientTexture->Name);
|
|
FRDGTextureRef OutputVolumeDirectionalTexture = GraphBuilder.CreateTexture(InputVolumeDirectionalTexture->Desc, InputVolumeDirectionalTexture->Name);
|
|
|
|
Textures.Ambient[TextureIndex] = OutputVolumeAmbientTexture;
|
|
Textures.Directional[TextureIndex] = OutputVolumeDirectionalTexture;
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FFilterTranslucentVolumePS::FParameters>();
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
PassParameters->TexelSize = 1.0f / TranslucencyLightingVolumeDim;
|
|
PassParameters->TranslucencyLightingVolumeAmbient = InputVolumeAmbientTexture;
|
|
PassParameters->TranslucencyLightingVolumeDirectional = InputVolumeDirectionalTexture;
|
|
PassParameters->TranslucencyLightingVolumeAmbientSampler = SamplerStateRHI;
|
|
PassParameters->TranslucencyLightingVolumeDirectionalSampler = SamplerStateRHI;
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(OutputVolumeAmbientTexture, ERenderTargetLoadAction::ENoAction);
|
|
PassParameters->RenderTargets[1] = FRenderTargetBinding(OutputVolumeDirectionalTexture, ERenderTargetLoadAction::ENoAction);
|
|
|
|
const FVolumeBounds VolumeBounds(TranslucencyLightingVolumeDim);
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(View.ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
|
|
TShaderMapRef<FFilterTranslucentVolumePS> PixelShader(View.ShaderMap);
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("Cascade%d", VolumeCascadeIndex),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[VertexShader, GeometryShader, PixelShader, PassParameters, VolumeBounds, TranslucencyLightingVolumeDim](FRHICommandList& RHICmdList)
|
|
{
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.SetGeometryShader(GeometryShader.GetGeometryShader());
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, FIntVector(TranslucencyLightingVolumeDim));
|
|
if (GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds.MinZ);
|
|
}
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
});
|
|
}
|
|
}
|
|
}
|