2707 lines
117 KiB
C++
2707 lines
117 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 "PrimitiveDrawingUtils.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 "LightFunctionRendering.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"
|
|
#include "TranslucentLightingViewState.h"
|
|
#include "RenderCore.h"
|
|
#include "StaticMeshBatch.h"
|
|
#include "LightFunctionAtlas.h"
|
|
#include "HeterogeneousVolumes/HeterogeneousVolumes.h"
|
|
#include "DeferredShadingRenderer.h"
|
|
#include "BasePassRendering.h"
|
|
#include "LightFunctionAtlas.h"
|
|
#include "BlueNoise.h"
|
|
|
|
using namespace LightFunctionAtlas;
|
|
|
|
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 | ECVF_Scalability
|
|
);
|
|
|
|
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 | ECVF_Scalability
|
|
);
|
|
|
|
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 | ECVF_Scalability
|
|
);
|
|
|
|
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 | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<float> CVarTranslucencyLightingVolumeOuterDistance(
|
|
TEXT("r.TranslucencyLightingVolumeOuterDistance"),
|
|
5000.0f,
|
|
TEXT("Distance from the camera that the second volume cascade should end"),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<float> CVarTranslucencyLightingVolumePositionOffsetRadius(
|
|
TEXT("r.TranslucencyLightingVolume.PositionOffsetRadius"),
|
|
0.0f,
|
|
TEXT("Radius of per-pixel offset applied to position when sampling translucency lighting volume."),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
static TAutoConsoleVariable<bool> CVarTranslucencyLightingVolumeTemporal(
|
|
TEXT("r.TranslucencyLightingVolume.Temporal"),
|
|
false,
|
|
TEXT("Whether to use temporal accumulation instead of spatial filter when updating the translucency lighting volume."),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarTranslucencyLightingVolumeHistoryWeight(
|
|
TEXT("r.TranslucencyLightingVolume.Temporal.HistoryWeight"),
|
|
0.9,
|
|
TEXT("How much the history value should be weighted each frame. This is a tradeoff between visible jittering and responsiveness."),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<bool> CVarTranslucencyLightingVolumeMarkVoxelsSupported(
|
|
TEXT("r.TranslucencyLightingVolume.MarkVoxels.Supported"),
|
|
true,
|
|
TEXT("Whether marking used voxels is supported by the project. Avoids compiling some shaders when disabled.\n")
|
|
TEXT("This setting can't be changed at runtime since it affects cooking."),
|
|
ECVF_ReadOnly
|
|
);
|
|
|
|
static TAutoConsoleVariable<bool> CVarTranslucencyLightingVolumeMarkVoxels(
|
|
TEXT("r.TranslucencyLightingVolume.MarkVoxels"),
|
|
false,
|
|
TEXT("Whether to mark which volume voxels are sampled during rendering and only update those that are."),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarTranslucencyLightingVolumeBatch(
|
|
TEXT("r.TranslucencyLightingVolume.Batch"),
|
|
1,
|
|
TEXT("When enabled, batches supported lights into a single draw call for efficiency"),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarTranslucencyLightingVolumeAccurateRectLights(
|
|
TEXT("r.TranslucencyLightingVolume.AccurateRectLights"),
|
|
1,
|
|
TEXT("When disabled rect lights are approximated as spot lights in the translucency volume.\n")
|
|
TEXT("Only accurate rect lights are included in batching, so it is recommended to enable this when batching is used."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
static TAutoConsoleVariable<int32> CVarTranslucencyLightingVolumeInjectDirectionalLightCSM(
|
|
TEXT("r.TranslucencyLightingVolume.InjectDirectionalLightCSM"),
|
|
1,
|
|
TEXT("Enable sampling of the directional light CSM.\n"),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability);
|
|
|
|
// Adaptation to camera angle is now disabled by default to avoid change on lighting when zoomin in or out.
|
|
// The volume remains around the camera anyway, so the camera angle should not matter when the setup mostly depends on start and end distance for each cascade.
|
|
int32 GTranslucencyLightingVolumeAdaptToPerspective = 0;
|
|
static FAutoConsoleVariableRef CVarTranslucencyLightingVolumeAdaptToPerspective(
|
|
TEXT("r.TranslucencyLightingVolume.AdaptToPerspective"),
|
|
GTranslucencyLightingVolumeAdaptToPerspective,
|
|
TEXT("The translucent volume will adapt to the camera perspective when zooming. This can can result in pops for extreme zoom-in so it can be disabled if needed."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
int32 GTranslucencyLightingVolumeDebug = 0;
|
|
static FAutoConsoleVariableRef CVarTranslucencyLightingVolumeDebug(
|
|
TEXT("r.TranslucencyLightingVolume.Debug"),
|
|
GTranslucencyLightingVolumeDebug,
|
|
TEXT("Debug information for the translucency lighting volume."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static int32 GTranslucencyLightingVolumeMaterialPSOPrecache = 1;
|
|
static FAutoConsoleVariableRef CVarGTranslucencyLightingVolumeMaterialPSOPrecache(
|
|
TEXT("r.PSOPrecache.TranslucencyLightingVolumeMaterial"),
|
|
GTranslucencyLightingVolumeMaterialPSOPrecache,
|
|
TEXT("Precache all possible required Translucency Lighting Volume PSOs for loaded LightMaterials."),
|
|
ECVF_ReadOnly);
|
|
|
|
static const TCHAR* TranslucentLightingMaterialPSOCollectorName = TEXT("TranslucentLightingMaterialPSOCollector");
|
|
|
|
float GetTranslucencyLightingVolumePositionOffsetRadius()
|
|
{
|
|
return FMath::Max(0.0f, CVarTranslucencyLightingVolumePositionOffsetRadius.GetValueOnAnyThread());
|
|
}
|
|
|
|
/** Function returning current translucency lighting volume dimensions. */
|
|
int32 GetTranslucencyLightingVolumeDim()
|
|
{
|
|
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;
|
|
|
|
const FViewMatrices& LocalShadowViewMatrices = GetShadowViewMatrices();
|
|
|
|
if (IsPerspectiveProjection() && GTranslucencyLightingVolumeAdaptToPerspective > 0)
|
|
{
|
|
// Derive FOV and aspect ratio from the perspective projection matrix
|
|
FieldOfView = FMath::Atan(1.0 / LocalShadowViewMatrices.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 = LocalShadowViewMatrices.GetProjectionMatrix().M[1][1] / LocalShadowViewMatrices.GetProjectionMatrix().M[0][0];
|
|
}
|
|
|
|
// Tan of field of view can explode when FieldOfView is close to 180 degree, when using the adaptation to Fov above.
|
|
// Close to this edge case, the volume fitted on the frustum vertices can explode to infinity.
|
|
// In order to fix that, we clamp the value to the length of the diagonal of a cube of size OuterDistance.
|
|
const double MaxTanFieldOfViewLength = FMath::Sqrt(FMath::Max(1.0f, OuterDistance * OuterDistance + OuterDistance * OuterDistance + OuterDistance * OuterDistance));
|
|
const double TanFieldOfView = FMath::Tan(FieldOfView);
|
|
|
|
const double StartHorizontalLength = FMath::Min(MaxTanFieldOfViewLength, FrustumStartDistance * TanFieldOfView);
|
|
const FVector StartCameraRightOffset = LocalShadowViewMatrices.GetViewMatrix().GetColumn(0) * StartHorizontalLength;
|
|
const double StartVerticalLength = StartHorizontalLength / AspectRatio;
|
|
const FVector StartCameraUpOffset = LocalShadowViewMatrices.GetViewMatrix().GetColumn(1) * StartVerticalLength;
|
|
|
|
const double EndHorizontalLength = FMath::Min(MaxTanFieldOfViewLength, FrustumEndDistance * TanFieldOfView);
|
|
const FVector EndCameraRightOffset = LocalShadowViewMatrices.GetViewMatrix().GetColumn(0) * EndHorizontalLength;
|
|
const double EndVerticalLength = EndHorizontalLength / AspectRatio;
|
|
const FVector EndCameraUpOffset = LocalShadowViewMatrices.GetViewMatrix().GetColumn(1) * EndVerticalLength;
|
|
|
|
FVector SplitVertices[8];
|
|
const FVector ShadowViewOrigin = LocalShadowViewMatrices.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());
|
|
}
|
|
|
|
if (RadiusSquared > 0) // Avoid issues with bad cvar usage, e.g. r.TranslucencyLightingVolumeInnerDistance.
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
InOutCascadeBoundsArray[CascadeIndex] = FBox(Center, Center);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 AllowTranslucencyPerObjectShadows(Parameters.Platform) && IsTranslucentBlendMode(Parameters.MaterialParameters);
|
|
}
|
|
|
|
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 AllowTranslucencyPerObjectShadows(Parameters.Platform) && IsTranslucentBlendMode(Parameters.MaterialParameters);
|
|
}
|
|
|
|
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 FTranslucencyDepthShaderElementData& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings) const
|
|
{
|
|
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, 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));
|
|
OutEnvironment.SetDefine(TEXT("SUBSTRATE_INLINE_SHADING"), 1);
|
|
}
|
|
|
|
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;
|
|
const bool bDirectionalLight;
|
|
};
|
|
|
|
FTranslucencyDepthPassMeshProcessor::FTranslucencyDepthPassMeshProcessor(const FScene* Scene,
|
|
const FSceneView* InViewIfDynamicMeshCommand,
|
|
const FMeshPassProcessorRenderState& InPassDrawRenderState,
|
|
const FProjectedShadowInfo* InShadowInfo,
|
|
FMeshPassDrawListContext* InDrawListContext)
|
|
: FMeshPassProcessor(EMeshPass::Num, Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
|
|
, PassDrawRenderState(InPassDrawRenderState)
|
|
, ShadowInfo(InShadowInfo)
|
|
, 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 float MaterialTranslucentShadowStartOffset = Material.GetTranslucentShadowStartOffset();
|
|
const bool MaterialCastDynamicShadowAsMasked = Material.GetCastDynamicShadowAsMasked();
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(Material, OverrideSettings);
|
|
const bool bIsTranslucent = IsTranslucentBlendMode(Material);
|
|
|
|
// 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](FRDGAsyncTask, 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);
|
|
});
|
|
}
|
|
|
|
class FGatherMarkedVoxelsCS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FGatherMarkedVoxelsCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FGatherMarkedVoxelsCS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVoxelAllocator)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVoxelData)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, VolumeMarkTexture)
|
|
|
|
SHADER_PARAMETER(FIntVector, VolumeSize)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static FIntVector GetGroupSize()
|
|
{
|
|
return FIntVector(4, 4, 4);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
const FIntVector GroupSize = GetGroupSize();
|
|
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_X"), GroupSize.X);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Y"), GroupSize.Y);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Z"), GroupSize.Z);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FGatherMarkedVoxelsCS, "/Engine/Private/TranslucentLightingShaders.usf", "GatherMarkedVoxelsCS", SF_Compute);
|
|
|
|
class FInitIndirectArgsCS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FInitIndirectArgsCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FInitIndirectArgsCS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWIndirectArgs)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, VoxelAllocator)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static uint32 GetGroupSize()
|
|
{
|
|
return 64;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FInitIndirectArgsCS, "/Engine/Private/TranslucentLightingShaders.usf", "InitIndirectArgsCS", SF_Compute);
|
|
|
|
/** Compute shader used to filter a single volume lighting cascade. */
|
|
class FFilterTranslucentVolumeCS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FFilterTranslucentVolumeCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FFilterTranslucentVolumeCS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, RWTranslucencyLightingVolumeAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, RWTranslucencyLightingVolumeDirectional)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeDirectional)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, TranslucencyLightingVolumeAmbientSampler)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, TranslucencyLightingVolumeDirectionalSampler)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, HistoryAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, HistoryDirectional)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, HistoryAmbientSampler)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, HistoryDirectionalSampler)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, HistoryMark)
|
|
|
|
SHADER_PARAMETER(FIntVector, VolumeSize)
|
|
SHADER_PARAMETER(float, TexelSize)
|
|
SHADER_PARAMETER(uint32, VolumeCascadeIndex)
|
|
|
|
SHADER_PARAMETER(FVector4f, PrevTranslucencyLightingVolumeMin)
|
|
SHADER_PARAMETER(FVector4f, PrevTranslucencyLightingVolumeInvSize)
|
|
|
|
SHADER_PARAMETER(FVector3f, HistoryTextureBilinearUVMin)
|
|
SHADER_PARAMETER(FVector3f, HistoryTextureBilinearUVMax)
|
|
|
|
SHADER_PARAMETER(float, HistoryWeight)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FUseTemporalReprojection : SHADER_PERMUTATION_BOOL("USE_TEMPORAL_REPROJECTION");
|
|
class FCheckHistoryMark : SHADER_PERMUTATION_BOOL("CHECK_HISTORY_MARK");
|
|
using FPermutationDomain = TShaderPermutationDomain<FUseTemporalReprojection, FCheckHistoryMark>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static FIntVector GetGroupSize()
|
|
{
|
|
return FIntVector(4, 4, 4);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
FIntVector GroupSize = GetGroupSize();
|
|
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_X"), GroupSize.X);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Y"), GroupSize.Y);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Z"), GroupSize.Z);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FFilterTranslucentVolumeCS, "/Engine/Private/TranslucentLightingShaders.usf", "FilterTranslucentVolumeCS", SF_Compute);
|
|
|
|
/** 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_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrint)
|
|
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_RDG_UNIFORM_BUFFER(FAdaptiveVolumetricShadowMapUniformBufferParameters, AVSM)
|
|
SHADER_PARAMETER(FMatrix44f, LightFunctionTranslatedWorldToLight)
|
|
SHADER_PARAMETER(FVector4f, LightFunctionParameters)
|
|
SHADER_PARAMETER(FVector3f, CameraRelativeLightPosition)
|
|
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");
|
|
class FAdaptiveVolumetricShadowMap : SHADER_PERMUTATION_BOOL("ADAPTIVE_VOLUMETRIC_SHADOW_MAP");
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<
|
|
FRadialAttenuation,
|
|
FDynamicallyShadowed,
|
|
FLightFunction,
|
|
FVirtualShadowMap,
|
|
FAdaptiveVolumetricShadowMap >;
|
|
|
|
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)
|
|
{
|
|
FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
if (!DoesPlatformSupportVirtualShadowMaps(Parameters.Platform) && PermutationVector.Get<FVirtualShadowMap>() != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform) && PermutationVector.Get<FAdaptiveVolumetricShadowMap>() != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
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(
|
|
FRHIBatchedShaderParameters& BatchedParameters,
|
|
const FViewInfo& View,
|
|
const FMaterialRenderProxy* MaterialProxy)
|
|
{
|
|
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy);
|
|
FMaterialShader::SetParameters(BatchedParameters, MaterialProxy, Material, View);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(,FTranslucentLightingInjectPS, TEXT("/Engine/Private/TranslucentLightInjectionShaders.usf"), TEXT("InjectMainPS"), SF_Pixel);
|
|
|
|
/** Shader that adds direct lighting contribution from multiple lights. */
|
|
class FTranslucentLightingInjectBatchCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FTranslucentLightingInjectBatchCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FTranslucentLightingInjectBatchCS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FForwardLightUniformParameters, ForwardLightStruct)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMapSamplingParameters)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLightFunctionAtlasGlobalParameters, LightFunctionAtlas)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, BatchedLocalLights)
|
|
SHADER_PARAMETER(uint32, MaxBatchedLocalLights)
|
|
SHADER_PARAMETER(uint32, VolumeCascadeIndex)
|
|
SHADER_PARAMETER(FIntVector, VolumeSize)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, RWTranslucencyLightingVolumeAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, RWTranslucencyLightingVolumeDirectional)
|
|
|
|
// used when UAV typed loads are not supported
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeDirectional)
|
|
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, VoxelAllocator)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, VoxelData)
|
|
|
|
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FVirtualShadowMap : SHADER_PERMUTATION_BOOL("VIRTUAL_SHADOW_MAP");
|
|
class FUseLightFunctionAtlas : SHADER_PERMUTATION_BOOL("USE_LIGHT_FUNCTION_ATLAS");
|
|
class FIndirectVoxelDispatch : SHADER_PERMUTATION_BOOL("INDIRECT_VOXEL_DISPATCH");
|
|
class FUseUAVTypedLoad : SHADER_PERMUTATION_BOOL("USE_UAV_TYPED_LOAD");
|
|
using FPermutationDomain = TShaderPermutationDomain<FVirtualShadowMap, FUseLightFunctionAtlas, FIndirectVoxelDispatch, FUseUAVTypedLoad>;
|
|
|
|
public:
|
|
|
|
static FIntVector GetGroupSize()
|
|
{
|
|
return FIntVector(4, 4, 4);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
|
|
FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment);
|
|
|
|
const FIntVector GroupSize = GetGroupSize();
|
|
|
|
const FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
if (PermutationVector.Get<FIndirectVoxelDispatch>())
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_X"), GroupSize.X * GroupSize.Y * GroupSize.Z);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Y"), 1);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Z"), 1);
|
|
}
|
|
else
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_X"), GroupSize.X);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Y"), GroupSize.Y);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Z"), GroupSize.Z);
|
|
}
|
|
|
|
// This shader must support typed UAV load and we are testing if it is supported at runtime
|
|
// using UE::PixelFormat::HasCapabilities(..., EPixelFormatCapabilities::TypedUAVLoad)
|
|
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
if (!DoesPlatformSupportVirtualShadowMaps(Parameters.Platform) && PermutationVector.Get<FVirtualShadowMap>() != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FTranslucentLightingInjectBatchCS, "/Engine/Private/TranslucentLightInjectionShaders.usf", "InjectBatchMainCS", SF_Compute);
|
|
|
|
|
|
|
|
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);
|
|
|
|
void FTranslucencyLightingVolumeTextures::GetTextureFormatAndCreationFlags(EPixelFormat& OutPixelFormat, ETextureCreateFlags& OutCreationFlags)
|
|
{
|
|
OutPixelFormat = PF_FloatRGBA;
|
|
|
|
// TODO: We can skip the and TLV allocations when rendering in forward shading mode
|
|
OutCreationFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_ReduceMemoryWithTilingMode | TexCreate_UAV;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
// Skip init/clear if disabled
|
|
// GetTranslucencyLightingVolumeParameters will return black system textures
|
|
if (!GUseTranslucentLightingVolumes)
|
|
{
|
|
return;
|
|
}
|
|
|
|
check(PassFlags == ERDGPassFlags::Compute || PassFlags == ERDGPassFlags::AsyncCompute);
|
|
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, TranslucentLighting, "InitTranslucencyLightingVolumeTextures");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, TranslucentLighting);
|
|
|
|
VolumeDim = GetTranslucencyLightingVolumeDim();
|
|
const FIntVector TranslucencyLightingVolumeDim(VolumeDim);
|
|
|
|
EPixelFormat TranslucencyPixelFormat;
|
|
ETextureCreateFlags TranslucencyTargetFlags;
|
|
GetTextureFormatAndCreationFlags(TranslucencyPixelFormat, TranslucencyTargetFlags);
|
|
|
|
// 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);
|
|
{
|
|
Ambient.SetNum(NumViewsWithTextures * TVC_MAX);
|
|
Directional.SetNum(NumViewsWithTextures * TVC_MAX);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < ViewCount; ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
for (int32 CascadeIndex = 0; CascadeIndex < TVC_MAX; ++CascadeIndex)
|
|
{
|
|
const uint32 TextureIndex = FTranslucencyLightingVolumeTextures::GetIndex(View, CascadeIndex);
|
|
check(TextureIndex <= NumViewsWithTextures * TVC_MAX);
|
|
|
|
FRDGTextureRef AmbientTexture = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create3D(
|
|
TranslucencyLightingVolumeDim,
|
|
TranslucencyPixelFormat,
|
|
FClearValueBinding::Transparent,
|
|
TranslucencyTargetFlags),
|
|
TEXT("TranslucentVolumeAmbient"));
|
|
|
|
FRDGTextureRef DirectionalTexture = GraphBuilder.CreateTexture(
|
|
FRDGTextureDesc::Create3D(
|
|
TranslucencyLightingVolumeDim,
|
|
TranslucencyPixelFormat,
|
|
FClearValueBinding::Transparent,
|
|
TranslucencyTargetFlags),
|
|
TEXT("TranslucentVolumeDirectional"));
|
|
|
|
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;
|
|
}
|
|
|
|
Parameters.TranslucencyLightingRandomPositionOffsetRadius = GetTranslucencyLightingVolumePositionOffsetRadius();
|
|
|
|
return Parameters;
|
|
}
|
|
|
|
bool IsTranslucencyLightingVolumeUsingVoxelMarkingSupported()
|
|
{
|
|
return CVarTranslucencyLightingVolumeMarkVoxelsSupported.GetValueOnAnyThread();
|
|
}
|
|
|
|
bool IsTranslucencyLightingVolumeUsingVoxelMarking()
|
|
{
|
|
return GUseTranslucentLightingVolumes && IsTranslucencyLightingVolumeUsingVoxelMarkingSupported() && CVarTranslucencyLightingVolumeMarkVoxels.GetValueOnAnyThread();
|
|
}
|
|
|
|
bool IsTranslucencyLightingVolumeUsingBlueNoise()
|
|
{
|
|
return GUseTranslucentLightingVolumes && GetTranslucencyLightingVolumePositionOffsetRadius();
|
|
}
|
|
|
|
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 || Views.Num() == 0 || !RHISupportsVolumeTextureRendering(Views[0].GetShaderPlatform()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, TranslucentLighting, "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](FRDGAsyncTask, 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);
|
|
|
|
SetShaderParametersLegacyVS(RHICmdList, VertexShader, VolumeBounds, FIntVector(TranslucencyLightingVolumeDim));
|
|
if (GeometryShader.IsValid())
|
|
{
|
|
SetShaderParametersLegacyGS(RHICmdList, GeometryShader, VolumeBounds.MinZ);
|
|
}
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class FInjectMegaLightsCS : public FGlobalShader
|
|
{
|
|
public:
|
|
DECLARE_GLOBAL_SHADER(FInjectMegaLightsCS)
|
|
SHADER_USE_PARAMETER_STRUCT(FInjectMegaLightsCS, FGlobalShader)
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, RWTranslucencyLightingVolumeAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, RWTranslucencyLightingVolumeDirectional)
|
|
|
|
// used when UAV typed loads are not supported
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, TranslucencyLightingVolumeDirectional)
|
|
|
|
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture3D, MegaLightsAmbient)
|
|
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture3D, MegaLightsDirectional)
|
|
|
|
SHADER_PARAMETER(FIntVector, VolumeSize)
|
|
SHADER_PARAMETER(uint32, VolumeCascadeIndex)
|
|
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, VoxelAllocator)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, VoxelData)
|
|
|
|
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
class FIndirectVoxelDispatch : SHADER_PERMUTATION_BOOL("INDIRECT_VOXEL_DISPATCH");
|
|
class FUseUAVTypedLoad : SHADER_PERMUTATION_BOOL("USE_UAV_TYPED_LOAD");
|
|
using FPermutationDomain = TShaderPermutationDomain<FIndirectVoxelDispatch, FUseUAVTypedLoad>;
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static FIntVector GetGroupSize()
|
|
{
|
|
return FIntVector(4, 4, 4);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
const FIntVector GroupSize = GetGroupSize();
|
|
|
|
const FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
if (PermutationVector.Get<FIndirectVoxelDispatch>())
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_X"), GroupSize.X * GroupSize.Y * GroupSize.Z);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Y"), 1);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Z"), 1);
|
|
}
|
|
else
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_X"), GroupSize.X);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Y"), GroupSize.Y);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Z"), GroupSize.Z);
|
|
}
|
|
|
|
// This shader must support typed UAV load and we are testing if it is supported at runtime
|
|
// using UE::PixelFormat::HasCapabilities(..., EPixelFormatCapabilities::TypedUAVLoad)
|
|
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FInjectMegaLightsCS, "/Engine/Private/TranslucentLightInjectionShaders.usf", "InjectMegaLightsCS", SF_Compute);
|
|
|
|
void InjectTranslucencyLightingVolumeMegaLights(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TArrayView<const FViewInfo> Views,
|
|
const FTranslucencyLightingVolumeTextures& Textures)
|
|
{
|
|
if (!GUseTranslucentLightingVolumes || Views.Num() == 0 || !MegaLights::IsEnabled(*Views[0].Family) || !MegaLights::UseTranslucencyVolume())
|
|
{
|
|
return;
|
|
}
|
|
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, TranslucentLighting, "InjectTranslucencyLightingVolumeMegaLights");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, TranslucentLighting);
|
|
|
|
const FIntVector VolumeSize = FIntVector(GetTranslucencyLightingVolumeDim());
|
|
|
|
EPixelFormat TranslucencyPixelFormat;
|
|
ETextureCreateFlags TranslucencyTargetFlags;
|
|
FTranslucencyLightingVolumeTextures::GetTextureFormatAndCreationFlags(TranslucencyPixelFormat, TranslucencyTargetFlags);
|
|
|
|
const bool bUseUAVTypedLoad = UE::PixelFormat::HasCapabilities(TranslucencyPixelFormat, EPixelFormatCapabilities::TypedUAVLoad);
|
|
|
|
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 bool bUseVolumeMarkTexture = IsTranslucencyLightingVolumeUsingVoxelMarking() && View.TranslucencyVolumeMarkData[VolumeCascadeIndex].MarkTexture != nullptr;
|
|
|
|
// 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];
|
|
|
|
FRDGTextureRef MegaLightsAmbient = View.GetMegaLightsVolume().TranslucencyAmbient[VolumeCascadeIndex];
|
|
FRDGTextureRef MegaLightsDirectional = View.GetMegaLightsVolume().TranslucencyDirectional[VolumeCascadeIndex];
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FInjectMegaLightsCS::FParameters>();
|
|
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
PassParameters->VolumeSize = VolumeSize;
|
|
PassParameters->VolumeCascadeIndex = VolumeCascadeIndex;
|
|
|
|
PassParameters->MegaLightsAmbient = MegaLightsAmbient ? GraphBuilder.CreateSRV(MegaLightsAmbient) : nullptr;
|
|
PassParameters->MegaLightsDirectional = MegaLightsDirectional ? GraphBuilder.CreateSRV(MegaLightsDirectional) : nullptr;
|
|
|
|
if (bUseUAVTypedLoad)
|
|
{
|
|
PassParameters->RWTranslucencyLightingVolumeAmbient = GraphBuilder.CreateUAV(VolumeAmbientTexture);
|
|
PassParameters->RWTranslucencyLightingVolumeDirectional = GraphBuilder.CreateUAV(VolumeDirectionalTexture);
|
|
}
|
|
else
|
|
{
|
|
// need to output to auxiliary textures to be able to read from existing volumes
|
|
|
|
FRDGTextureRef OutputVolumeAmbientTexture = GraphBuilder.CreateTexture(VolumeAmbientTexture->Desc, VolumeAmbientTexture->Name);
|
|
FRDGTextureRef OutputVolumeDirectionalTexture = GraphBuilder.CreateTexture(VolumeDirectionalTexture->Desc, VolumeDirectionalTexture->Name);
|
|
|
|
PassParameters->RWTranslucencyLightingVolumeAmbient = GraphBuilder.CreateUAV(OutputVolumeAmbientTexture);
|
|
PassParameters->RWTranslucencyLightingVolumeDirectional = GraphBuilder.CreateUAV(OutputVolumeDirectionalTexture);
|
|
|
|
PassParameters->TranslucencyLightingVolumeAmbient = VolumeAmbientTexture;
|
|
PassParameters->TranslucencyLightingVolumeDirectional = VolumeDirectionalTexture;
|
|
|
|
VolumeAmbientTexture = OutputVolumeAmbientTexture;
|
|
VolumeDirectionalTexture = OutputVolumeDirectionalTexture;
|
|
}
|
|
|
|
FInjectMegaLightsCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FInjectMegaLightsCS::FIndirectVoxelDispatch>(bUseVolumeMarkTexture);
|
|
PermutationVector.Set<FInjectMegaLightsCS::FUseUAVTypedLoad>(bUseUAVTypedLoad);
|
|
|
|
auto ComputeShader = View.ShaderMap->GetShader<FInjectMegaLightsCS>(PermutationVector);
|
|
|
|
if (bUseVolumeMarkTexture)
|
|
{
|
|
PassParameters->VoxelAllocator = GraphBuilder.CreateSRV(View.TranslucencyVolumeMarkData[VolumeCascadeIndex].VoxelAllocator);
|
|
PassParameters->VoxelData = GraphBuilder.CreateSRV(View.TranslucencyVolumeMarkData[VolumeCascadeIndex].VoxelData);
|
|
PassParameters->IndirectArgs = View.TranslucencyVolumeMarkData[VolumeCascadeIndex].VoxelIndirectArgs;
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("InjectMegaLights(VolumeCascade=%d)", VolumeCascadeIndex),
|
|
ComputeShader,
|
|
PassParameters,
|
|
View.TranslucencyVolumeMarkData[VolumeCascadeIndex].VoxelIndirectArgs,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
const FIntVector NumGroups = FComputeShaderUtils::GetGroupCount(VolumeSize, FInjectMegaLightsCS::GetGroupSize());
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("InjectMegaLights(VolumeCascade=%d)", VolumeCascadeIndex),
|
|
ComputeShader,
|
|
PassParameters,
|
|
NumGroups);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** 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::TruncToInt32(MinPosition.X), 0);
|
|
VolumeBounds.MinY = FMath::Max(FMath::TruncToInt32(MinPosition.Y), 0);
|
|
VolumeBounds.MinZ = FMath::Max(FMath::TruncToInt32(MinPosition.Z), 0);
|
|
|
|
VolumeBounds.MaxX = FMath::Min(FMath::TruncToInt32(MaxPosition.X) + 1, TranslucencyLightingVolumeDim);
|
|
VolumeBounds.MaxY = FMath::Min(FMath::TruncToInt32(MaxPosition.Y) + 1, TranslucencyLightingVolumeDim);
|
|
VolumeBounds.MaxZ = FMath::Min(FMath::TruncToInt32(MaxPosition.Z) + 1, TranslucencyLightingVolumeDim);
|
|
}
|
|
|
|
return VolumeBounds;
|
|
}
|
|
|
|
FTranslucentLightInjectionCollector::FTranslucentLightInjectionCollector(
|
|
FRDGBuilder& GraphBuilder,
|
|
TArrayView<const FViewInfo> Views,
|
|
bool bAreLightsInLightGrid)
|
|
// NOTE: This data is directly referenced inside the render pass lamba, so must be allocated in the graph
|
|
: InjectionDataPerView(*GraphBuilder.AllocObject<TArray<FPerViewData, SceneRenderingAllocator>>())
|
|
{
|
|
InjectionDataPerView.SetNum(Views.Num());
|
|
|
|
// Static conditions for supporting batching
|
|
bCollectorSupportsBatching =
|
|
CVarTranslucencyLightingVolumeBatch.GetValueOnRenderThread() != 0 &&
|
|
bAreLightsInLightGrid;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
void FTranslucentLightInjectionCollector::AddLightForInjection(
|
|
const FViewInfo& View,
|
|
const uint32 ViewIndex,
|
|
TArrayView<const FVisibleLightInfo> VisibleLightInfos,
|
|
const FLightSceneInfo& LightSceneInfo,
|
|
const FProjectedShadowInfo* InProjectedShadowInfo)
|
|
{
|
|
if (LightSceneInfo.Proxy->AffectsTranslucentLighting())
|
|
{
|
|
const uint8 LightType = LightSceneInfo.Proxy->GetLightType();
|
|
|
|
FVolumeBounds VolumeBounds[TVC_MAX];
|
|
bool bAnyBoundsValid = false;
|
|
for (uint32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; ++VolumeCascadeIndex)
|
|
{
|
|
VolumeBounds[VolumeCascadeIndex] = CalculateLightVolumeBounds(LightSceneInfo.Proxy->GetBoundingSphere(), View, VolumeCascadeIndex, LightType == LightType_Directional);
|
|
bAnyBoundsValid = bAnyBoundsValid || VolumeBounds[VolumeCascadeIndex].IsValid();
|
|
}
|
|
if (!bAnyBoundsValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FVisibleLightInfo& VisibleLightInfo = VisibleLightInfos[LightSceneInfo.Id];
|
|
const ERHIFeatureLevel::Type FeatureLevel = View.FeatureLevel;
|
|
|
|
bool bLightFunctionUsesAtlas = false;
|
|
bool bApplyLightFunction = false;
|
|
const FMaterialRenderProxy* LightFunctionMaterialRenderProxy = LightSceneInfo.Proxy->GetLightFunctionMaterial();
|
|
if (LightFunctionMaterialRenderProxy && View.Family->EngineShowFlags.LightFunctions)
|
|
{
|
|
const FMaterial& LightFunctionMaterial = LightFunctionMaterialRenderProxy->GetIncompleteMaterialWithFallback(FeatureLevel);
|
|
|
|
bApplyLightFunction = LightFunctionMaterial.IsLightFunction();
|
|
bLightFunctionUsesAtlas = bApplyLightFunction && LightFunctionMaterial.MaterialIsLightFunctionAtlasCompatible_RenderThread();
|
|
}
|
|
|
|
const int32 VirtualShadowMapId = VisibleLightInfo.GetVirtualShadowMapId(&View);
|
|
|
|
const bool bUseAdaptiveVolumetricShadowMap =
|
|
LightSceneInfo.Proxy->CastsVolumetricShadow() &&
|
|
ShouldRenderHeterogeneousVolumesForView(View) &&
|
|
ShouldHeterogeneousVolumesCastShadows();
|
|
|
|
const bool bStaticShadowing =
|
|
LightSceneInfo.Proxy->GetStaticShadowDepthMap() &&
|
|
LightSceneInfo.Proxy->GetStaticShadowDepthMap()->Data;
|
|
|
|
// We only support the "accurate" rect light model through this path since the LightGrid stores them that way
|
|
// Thus if the spot light approximation is used we have to send them through the unbatched path.
|
|
const bool bSupportRectLights = CVarTranslucencyLightingVolumeAccurateRectLights.GetValueOnRenderThread() != 0;
|
|
|
|
// Lights without certain features can be batched into a single draw (loop in shader) which is more efficient
|
|
bool bSupportsBatching =
|
|
bCollectorSupportsBatching &&
|
|
LightType != LightType_Directional &&
|
|
(bSupportRectLights || LightType != LightType_Rect) &&
|
|
!bStaticShadowing &&
|
|
InProjectedShadowInfo == nullptr &&
|
|
(!bApplyLightFunction || bLightFunctionUsesAtlas) &&
|
|
!bUseAdaptiveVolumetricShadowMap;
|
|
|
|
// If it would otherwise be supported, see if we can find the local light index in the light grid
|
|
if (bSupportsBatching && View.ForwardLightingResources.ForwardLightUniformBuffer && View.ViewState)
|
|
{
|
|
const int32* Value = View.ViewState->LightSceneIdToForwardLightIndex.Find(LightSceneInfo.Id);
|
|
if (Value)
|
|
{
|
|
const int32 ForwardLightIndex = *Value;
|
|
|
|
FPerViewData& InjectionData = InjectionDataPerView[ViewIndex];
|
|
// There shouldn't be any duplication here
|
|
++InjectionData.BatchedLocalLightCount;
|
|
|
|
for (uint32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; ++VolumeCascadeIndex)
|
|
{
|
|
if (VolumeBounds[VolumeCascadeIndex].IsValid())
|
|
{
|
|
TBitArray<SceneRenderingAllocator>& Bits = InjectionData.BatchedLocalLights[VolumeCascadeIndex];
|
|
Bits.PadToNum(ForwardLightIndex + 1, false);
|
|
Bits[ForwardLightIndex] = true;
|
|
}
|
|
}
|
|
|
|
if (VirtualShadowMapId != INDEX_NONE)
|
|
{
|
|
// Note if there are any batched lights with VSM for permutation selection later
|
|
InjectionData.bAnyBatchedLightsWithVirtualShadowMaps = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bSupportsBatching = false;
|
|
}
|
|
}
|
|
|
|
if (!bSupportsBatching)
|
|
{
|
|
// Skip rendering if the DefaultLightFunctionMaterial isn't compiled yet
|
|
const FMaterialRenderProxy* MaterialProxy = bApplyLightFunction ?
|
|
LightSceneInfo.Proxy->GetLightFunctionMaterial() :
|
|
UMaterial::GetDefaultMaterial(MD_LightFunction)->GetRenderProxy();
|
|
|
|
if (MaterialProxy->GetIncompleteMaterialWithFallback(FeatureLevel).IsLightFunction())
|
|
{
|
|
FTranslucentLightInjectionCollector::FInjectionData Data;
|
|
Data.LightSceneInfo = &LightSceneInfo;
|
|
Data.ProjectedShadowInfo = InProjectedShadowInfo;
|
|
Data.bApplyLightFunction = bApplyLightFunction;
|
|
Data.LightFunctionMaterialProxy = MaterialProxy;
|
|
Data.VirtualShadowMapId = VirtualShadowMapId;
|
|
Data.bUseAdaptiveVolumetricShadowMap = bUseAdaptiveVolumetricShadowMap;
|
|
for (uint32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; ++VolumeCascadeIndex)
|
|
{
|
|
Data.VolumeBounds[VolumeCascadeIndex] = VolumeBounds[VolumeCascadeIndex];
|
|
}
|
|
InjectionDataPerView[ViewIndex].Unbatched.Add(Data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void SetupPSOStateForVolumeInjection(
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader,
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader,
|
|
FRHIPixelShader* PixelShaderRHI,
|
|
bool bDirectionalLight,
|
|
FGraphicsPipelineStateInitializer &OutGraphicsPSOInit)
|
|
{
|
|
OutGraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
OutGraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
OutGraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
|
|
|
|
// Accumulate the contribution of multiple lights
|
|
if (bDirectionalLight)
|
|
{
|
|
// Directional lights write their shadowing into alpha of the ambient texture
|
|
OutGraphicsPSOInit.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
|
|
{
|
|
OutGraphicsPSOInit.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();
|
|
}
|
|
|
|
OutGraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
|
|
OutGraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
OutGraphicsPSOInit.BoundShaderState.SetGeometryShader(GeometryShader.GetGeometryShader());
|
|
OutGraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShaderRHI;
|
|
}
|
|
|
|
static void SetPSOStateForVolumeInjection(
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader,
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader,
|
|
FRHIPixelShader* PixelShaderRHI,
|
|
FVolumeBounds VolumeBounds,
|
|
bool bDirectionalLight,
|
|
FRHICommandList& RHICmdList,
|
|
FGraphicsPipelineStateInitializer &OutGraphicsPSOInit)
|
|
{
|
|
RHICmdList.ApplyCachedRenderTargets(OutGraphicsPSOInit);
|
|
|
|
SetupPSOStateForVolumeInjection(VertexShader, GeometryShader, PixelShaderRHI, bDirectionalLight, OutGraphicsPSOInit);
|
|
|
|
SetGraphicsPipelineState(RHICmdList, OutGraphicsPSOInit, 0);
|
|
|
|
const int32 TranslucencyLightingVolumeDim = GetTranslucencyLightingVolumeDim();
|
|
|
|
SetShaderParametersLegacyVS(RHICmdList, VertexShader, VolumeBounds, FIntVector(TranslucencyLightingVolumeDim));
|
|
if (GeometryShader.IsValid())
|
|
{
|
|
SetShaderParametersLegacyGS(RHICmdList, GeometryShader, VolumeBounds.MinZ);
|
|
}
|
|
}
|
|
|
|
|
|
static void InjectTranslucencyLightingVolumeBatch(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const uint32 ViewIndex,
|
|
uint32 MaxBatchedLocalLights,
|
|
FRDGBufferRef BatchedLocalLightsRDG,
|
|
bool bSupportVirtualShadowMaps,
|
|
uint32 VolumeCascadeIndex,
|
|
FRDGTextureRef& VolumeAmbientTexture,
|
|
FRDGTextureRef& VolumeDirectionalTexture,
|
|
const FSceneRenderer& Renderer)
|
|
{
|
|
check(MaxBatchedLocalLights > 0);
|
|
check(BatchedLocalLightsRDG);
|
|
|
|
const FIntVector VolumeSize = FIntVector(GetTranslucencyLightingVolumeDim());
|
|
|
|
const FViewInfo::FTranslucencyVolumeMarkData& VolumeMarkData = View.TranslucencyVolumeMarkData[VolumeCascadeIndex];
|
|
const bool bUseVolumeMarkTexture = IsTranslucencyLightingVolumeUsingVoxelMarking() && VolumeMarkData.MarkTexture != nullptr;
|
|
|
|
EPixelFormat TranslucencyPixelFormat;
|
|
ETextureCreateFlags TranslucencyTargetFlags;
|
|
FTranslucencyLightingVolumeTextures::GetTextureFormatAndCreationFlags(TranslucencyPixelFormat, TranslucencyTargetFlags);
|
|
|
|
const bool bUseLightFunctionAtlas = LightFunctionAtlas::IsEnabled(View, ELightFunctionAtlasSystem::DeferredLighting);
|
|
const bool bUseUAVTypedLoad = UE::PixelFormat::HasCapabilities(TranslucencyPixelFormat, EPixelFormatCapabilities::TypedUAVLoad);
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FTranslucentLightingInjectBatchCS::FParameters>();
|
|
if (bSupportVirtualShadowMaps)
|
|
{
|
|
PassParameters->VirtualShadowMapSamplingParameters = Renderer.VirtualShadowMapArray.GetSamplingParameters(GraphBuilder, ViewIndex);
|
|
}
|
|
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
|
|
PassParameters->ForwardLightStruct = View.ForwardLightingResources.ForwardLightUniformBuffer;
|
|
PassParameters->BatchedLocalLights = GraphBuilder.CreateSRV(BatchedLocalLightsRDG);
|
|
PassParameters->LightFunctionAtlas = LightFunctionAtlas::BindGlobalParameters(GraphBuilder, View);;
|
|
PassParameters->MaxBatchedLocalLights = MaxBatchedLocalLights;
|
|
PassParameters->VolumeCascadeIndex = VolumeCascadeIndex;
|
|
PassParameters->VolumeSize = VolumeSize;
|
|
|
|
if (bUseUAVTypedLoad)
|
|
{
|
|
PassParameters->RWTranslucencyLightingVolumeAmbient = GraphBuilder.CreateUAV(VolumeAmbientTexture);
|
|
PassParameters->RWTranslucencyLightingVolumeDirectional = GraphBuilder.CreateUAV(VolumeDirectionalTexture);
|
|
}
|
|
else
|
|
{
|
|
// need to output to auxiliary textures to be able to read from existing volumes
|
|
|
|
FRDGTextureRef OutputVolumeAmbientTexture = GraphBuilder.CreateTexture(VolumeAmbientTexture->Desc, VolumeAmbientTexture->Name);
|
|
FRDGTextureRef OutputVolumeDirectionalTexture = GraphBuilder.CreateTexture(VolumeDirectionalTexture->Desc, VolumeDirectionalTexture->Name);
|
|
|
|
PassParameters->RWTranslucencyLightingVolumeAmbient = GraphBuilder.CreateUAV(OutputVolumeAmbientTexture);
|
|
PassParameters->RWTranslucencyLightingVolumeDirectional = GraphBuilder.CreateUAV(OutputVolumeDirectionalTexture);
|
|
|
|
PassParameters->TranslucencyLightingVolumeAmbient = VolumeAmbientTexture;
|
|
PassParameters->TranslucencyLightingVolumeDirectional = VolumeDirectionalTexture;
|
|
|
|
VolumeAmbientTexture = OutputVolumeAmbientTexture;
|
|
VolumeDirectionalTexture = OutputVolumeDirectionalTexture;
|
|
}
|
|
|
|
FTranslucentLightingInjectBatchCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FTranslucentLightingInjectBatchCS::FVirtualShadowMap>(bSupportVirtualShadowMaps);
|
|
PermutationVector.Set<FTranslucentLightingInjectBatchCS::FUseLightFunctionAtlas>(bUseLightFunctionAtlas);
|
|
PermutationVector.Set<FTranslucentLightingInjectBatchCS::FIndirectVoxelDispatch>(bUseVolumeMarkTexture);
|
|
PermutationVector.Set<FTranslucentLightingInjectBatchCS::FUseUAVTypedLoad>(bUseUAVTypedLoad);
|
|
|
|
auto ComputeShader = View.ShaderMap->GetShader<FTranslucentLightingInjectBatchCS>(PermutationVector);
|
|
|
|
if (bUseVolumeMarkTexture)
|
|
{
|
|
PassParameters->VoxelAllocator = GraphBuilder.CreateSRV(VolumeMarkData.VoxelAllocator);
|
|
PassParameters->VoxelData = GraphBuilder.CreateSRV(VolumeMarkData.VoxelData);
|
|
PassParameters->IndirectArgs = VolumeMarkData.VoxelIndirectArgs;
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("InjectTranslucencyLightingVolumeBatch(VolumeCascade=%d,Max=%d%s)",
|
|
VolumeCascadeIndex, MaxBatchedLocalLights,
|
|
bSupportVirtualShadowMaps ? TEXT(",VirtualShadowMap") : TEXT("")),
|
|
ComputeShader,
|
|
PassParameters,
|
|
VolumeMarkData.VoxelIndirectArgs,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
FIntVector NumGroups = FComputeShaderUtils::GetGroupCount(VolumeSize, FTranslucentLightingInjectBatchCS::GetGroupSize());
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("InjectTranslucencyLightingVolumeBatch(VolumeCascade=%d,Max=%d%s)",
|
|
VolumeCascadeIndex, MaxBatchedLocalLights,
|
|
bSupportVirtualShadowMaps ? TEXT(",VirtualShadowMap") : TEXT("")),
|
|
ComputeShader,
|
|
PassParameters,
|
|
NumGroups);
|
|
}
|
|
}
|
|
|
|
|
|
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. */
|
|
void InjectTranslucencyLightingVolume(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const uint32 ViewIndex,
|
|
const FScene* Scene,
|
|
const FSceneRenderer& Renderer,
|
|
const FTranslucentLightInjectionCollector& Collector,
|
|
FTranslucencyLightingVolumeTextures& Textures)
|
|
{
|
|
if (!GUseTranslucentLightingVolumes || !RHISupportsVolumeTextureRendering(View.GetShaderPlatform()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FTranslucentLightInjectionCollector::FPerViewData& LightInjectionData = Collector.InjectionDataPerView[ViewIndex];
|
|
|
|
INC_DWORD_STAT_BY(STAT_NumLightsInjectedIntoTranslucencyBatched, LightInjectionData.BatchedLocalLightCount);
|
|
INC_DWORD_STAT_BY(STAT_NumLightsInjectedIntoTranslucency, LightInjectionData.Unbatched.Num());
|
|
|
|
const FVolumetricCloudShadowAOParameters CloudShadowAOParameters = GetCloudShadowAOParameters(GraphBuilder, View, Scene->GetVolumetricCloudSceneInfo());
|
|
const bool bUseLightFunctionAtlas = View.LightFunctionAtlasViewData.UsesLightFunctionAtlas(LightFunctionAtlas::ELightFunctionAtlasSystem::DeferredLighting);
|
|
|
|
FRDGTextureRef TransmittanceLutTexture = GetSkyTransmittanceLutTexture(GraphBuilder, Scene, View);
|
|
|
|
// When accurate rect lights is disabled we approximate rect lights as spotlights
|
|
uint32 DeferredLightParameterFlags = CVarTranslucencyLightingVolumeAccurateRectLights.GetValueOnRenderThread() != 0 ?
|
|
0U : ELightShaderParameterFlags::RectAsSpotLight;
|
|
|
|
// 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];
|
|
|
|
// Batched lights
|
|
{
|
|
TBitArray<SceneRenderingAllocator>& BatchedLocalLights = Collector.InjectionDataPerView[ViewIndex].BatchedLocalLights[VolumeCascadeIndex];
|
|
if (BatchedLocalLights.Num() > 0)
|
|
{
|
|
const uint32 NumUint32Elements = FMath::DivideAndRoundUp(BatchedLocalLights.Num(), 32);
|
|
const uint32 InitialDataSize = NumUint32Elements * sizeof(uint32);
|
|
BatchedLocalLights.PadToNum(NumUint32Elements * 32, false);
|
|
|
|
FRDGBufferRef BatchedLocalLightsRDG = CreateStructuredBuffer(
|
|
GraphBuilder,
|
|
TEXT("TranslucencyLightingVolume.BatchedLocalLights"),
|
|
sizeof(uint32),
|
|
NumUint32Elements,
|
|
BatchedLocalLights.GetData(),
|
|
InitialDataSize
|
|
);
|
|
|
|
InjectTranslucencyLightingVolumeBatch(GraphBuilder, View, ViewIndex,
|
|
BatchedLocalLights.Num(), BatchedLocalLightsRDG,
|
|
LightInjectionData.bAnyBatchedLightsWithVirtualShadowMaps,
|
|
VolumeCascadeIndex, VolumeAmbientTexture, VolumeDirectionalTexture,
|
|
Renderer);
|
|
}
|
|
}
|
|
|
|
// Unbatched lights
|
|
for (int32 LightIndex = 0; LightIndex < LightInjectionData.Unbatched.Num(); LightIndex++)
|
|
{
|
|
const FTranslucentLightInjectionCollector::FInjectionData& InjectionData = LightInjectionData.Unbatched[LightIndex];
|
|
const FLightSceneInfo* const LightSceneInfo = InjectionData.LightSceneInfo;
|
|
const FVisibleLightInfo& VisibleLightInfo = Renderer.VisibleLightInfos[LightSceneInfo->Id];
|
|
const bool bInverseSquared = LightSceneInfo->Proxy->IsInverseSquared();
|
|
const bool bDirectionalLight = LightSceneInfo->Proxy->GetLightType() == LightType_Directional;
|
|
|
|
const FVolumeBounds VolumeBounds = InjectionData.VolumeBounds[VolumeCascadeIndex];
|
|
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.VirtualShadowMapArray.GetSamplingParameters(GraphBuilder, ViewIndex);
|
|
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, bUseLightFunctionAtlas, DeferredLightParameterFlags);
|
|
PassParameters->PS.DeferredLight = GraphBuilder.CreateUniformBuffer(DeferredLightStruct);
|
|
|
|
GetVolumeShadowingShaderParameters(GraphBuilder, View, LightSceneInfo, InjectionData.ProjectedShadowInfo, PassParameters->PS.VolumeShadowingParameters);
|
|
|
|
PassParameters->PS.VirtualShadowMapId = InjectionData.VirtualShadowMapId;
|
|
PassParameters->PS.LightFunctionParameters = FLightFunctionSharedParameters::GetLightFunctionSharedParameters(LightSceneInfo, 1.0f);
|
|
PassParameters->PS.VolumeCascadeIndex = VolumeCascadeIndex;
|
|
PassParameters->PS.AVSM = HeterogeneousVolumes::GetAdaptiveVolumetricShadowMapUniformBuffer(GraphBuilder, View.ViewState, LightSceneInfo);
|
|
|
|
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);
|
|
|
|
PassParameters->PS.CameraRelativeLightPosition = GetCamRelativeLightPosition(View.ViewMatrices, *LightSceneInfo);
|
|
|
|
#if 0 // Enable this to be able to debug using DEBUG_ONE_VOXEL
|
|
ShaderPrint::SetEnabled(true);
|
|
ShaderPrint::RequestSpaceForLines(64u);
|
|
ShaderPrint::RequestSpaceForCharacters(64u);
|
|
ShaderPrint::RequestSpaceForTriangles(64u);
|
|
ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, PassParameters->PS.ShaderPrint);
|
|
#endif
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("InjectTranslucencyLightingVolume(VolumeCascade=%d%s%s%s)",
|
|
VolumeCascadeIndex,
|
|
InjectionData.VirtualShadowMapId != INDEX_NONE ? TEXT(",VirtualShadowMap") : TEXT(""),
|
|
InjectionData.ProjectedShadowInfo != nullptr ? TEXT(",ShadowMap") : TEXT(""),
|
|
InjectionData.bApplyLightFunction ? TEXT(",LightFunction") : TEXT("")),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[PassParameters, VertexShader, GeometryShader, &View, &Renderer, &InjectionData, LightSceneInfo, bDirectionalLight, VolumeBounds, VolumeCascadeIndex](FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
|
|
const FMaterialRenderProxy* MaterialProxy = InjectionData.LightFunctionMaterialProxy;
|
|
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback( View.GetFeatureLevel(), MaterialProxy );
|
|
const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap();
|
|
|
|
const bool bUseVSM = InjectionData.VirtualShadowMapId != INDEX_NONE;
|
|
bool bDynamicShadow = InjectionData.ProjectedShadowInfo != nullptr;
|
|
|
|
if (CVarTranslucencyLightingVolumeInjectDirectionalLightCSM.GetValueOnRenderThread() <= 0 && bDirectionalLight)
|
|
{
|
|
bDynamicShadow = false;
|
|
}
|
|
|
|
FTranslucentLightingInjectPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FRadialAttenuation >(!bDirectionalLight);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FDynamicallyShadowed >(bDynamicShadow);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FLightFunction >(InjectionData.bApplyLightFunction);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FVirtualShadowMap >(bUseVSM);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FAdaptiveVolumetricShadowMap >(InjectionData.bUseAdaptiveVolumetricShadowMap);
|
|
|
|
auto PixelShader = MaterialShaderMap->GetShader< FTranslucentLightingInjectPS >( PermutationVector );
|
|
|
|
SetPSOStateForVolumeInjection(VertexShader, GeometryShader, PixelShader.GetPixelShader(),
|
|
VolumeBounds, bDirectionalLight, RHICmdList, GraphicsPSOInit);
|
|
|
|
#if PSO_PRECACHING_VALIDATE
|
|
if (PSOCollectorStats::IsFullPrecachingValidationEnabled())
|
|
{
|
|
static const int32 MaterialPSOCollectorIndex = FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(GMaxRHIFeatureLevel), TranslucentLightingMaterialPSOCollectorName);
|
|
PSOCollectorStats::CheckFullPipelineStateInCache(GraphicsPSOInit, EPSOPrecacheResult::Unknown, MaterialProxy, nullptr, nullptr, MaterialPSOCollectorIndex);
|
|
}
|
|
#endif // PSO_PRECACHING_VALIDATE
|
|
|
|
SetShaderParametersLegacyPS(RHICmdList, PixelShader, View, InjectionData.LightFunctionMaterialProxy);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
});
|
|
}
|
|
}
|
|
|
|
Textures.Ambient[TextureIndex] = VolumeAmbientTexture;
|
|
Textures.Directional[TextureIndex] = VolumeDirectionalTexture;
|
|
}
|
|
|
|
GraphBuilder.FlushSetupQueue();
|
|
}
|
|
|
|
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)
|
|
{
|
|
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();
|
|
|
|
const float Exposure = View.GetLastEyeAdaptationExposure();
|
|
|
|
// 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 * FLightRenderParameters::GetLightExposureScale(Exposure, SimpleLight.InverseExposureBlend), 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](FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
|
|
SetPSOStateForVolumeInjection(VertexShader, GeometryShader, PixelShader.GetPixelShader(),
|
|
VolumeBounds, false, RHICmdList, GraphicsPSOInit);
|
|
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class FDebugTranslucencyLightingVolumeCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FDebugTranslucencyLightingVolumeCS)
|
|
SHADER_USE_PARAMETER_STRUCT(FDebugTranslucencyLightingVolumeCS, FGlobalShader)
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, InnerVolumeMarkTexture)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, OuterVolumeMarkTexture)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<>;
|
|
|
|
static int32 GetGroupSize()
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
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("THREADGROUP_SIZE"), GetGroupSize());
|
|
}
|
|
|
|
static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return EShaderPermutationPrecacheRequest::NotPrecached;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FDebugTranslucencyLightingVolumeCS, "/Engine/Private/TranslucentLightingShaders.usf", "DebugTranslucencyLightingVolumeCS", SF_Compute);
|
|
|
|
static void DrawDebugTranslucencyLightingVolume(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TArrayView<const FViewInfo> Views)
|
|
{
|
|
// Draw debug the translucency volume lighting buffer
|
|
if (GTranslucencyLightingVolumeDebug <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FIntVector TranslucencyLightingVolumeDim(GetTranslucencyLightingVolumeDim());
|
|
|
|
FRDGTextureRef DummyTexture = GSystemTextures.GetVolumetricBlackUintDummy(GraphBuilder);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "View%d", ViewIndex);
|
|
|
|
FDebugTranslucencyLightingVolumeCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDebugTranslucencyLightingVolumeCS::FParameters>();
|
|
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
|
|
PassParameters->InnerVolumeMarkTexture = View.TranslucencyVolumeMarkData[0].MarkTexture ? View.TranslucencyVolumeMarkData[0].MarkTexture : DummyTexture;
|
|
PassParameters->OuterVolumeMarkTexture = View.TranslucencyVolumeMarkData[1].MarkTexture ? View.TranslucencyVolumeMarkData[1].MarkTexture : DummyTexture;
|
|
|
|
ShaderPrint::SetEnabled(true);
|
|
ShaderPrint::RequestSpaceForLines(128u);
|
|
ShaderPrint::RequestSpaceForCharacters(128u);
|
|
ShaderPrint::RequestSpaceForTriangles(64u);
|
|
ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, PassParameters->ShaderPrintUniformBuffer);
|
|
|
|
FDebugTranslucencyLightingVolumeCS::FPermutationDomain PermutationVector;
|
|
auto ComputeShader = View.ShaderMap->GetShader<FDebugTranslucencyLightingVolumeCS>(PermutationVector);
|
|
const FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(TranslucencyLightingVolumeDim, FDebugTranslucencyLightingVolumeCS::GetGroupSize());
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("DebugTranslucencyLightingVolumeCS"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
GroupCount);
|
|
}
|
|
}
|
|
|
|
void FilterTranslucencyLightingVolume(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TArrayView<const FViewInfo> Views,
|
|
FTranslucencyLightingVolumeTextures& Textures)
|
|
{
|
|
// Draw debug information if needed.
|
|
DrawDebugTranslucencyLightingVolume(GraphBuilder, Views);
|
|
|
|
const bool bMegaLightsTranslucencyVolume = Views.Num() > 0 && MegaLights::IsEnabled(*Views[0].Family) && MegaLights::UseTranslucencyVolume();
|
|
|
|
const bool bTemporal = CVarTranslucencyLightingVolumeTemporal.GetValueOnRenderThread() || bMegaLightsTranslucencyVolume;
|
|
|
|
if (!GUseTranslucentLightingVolumes || Views.Num() == 0 || !RHISupportsVolumeTextureRendering(Views[0].GetShaderPlatform()) || (!GUseTranslucencyVolumeBlur && !bTemporal))
|
|
{
|
|
// discard history since we are not updating it this frame
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
if (View.ViewState && !View.bStatePrevViewInfoIsReadOnly)
|
|
{
|
|
FTranslucencyLightingViewState& TranslucencyLightingViewState = View.ViewState->TranslucencyLighting;
|
|
for (int32 Index = 0; Index < TVC_MAX; Index++)
|
|
{
|
|
TranslucencyLightingViewState.HistoryAmbient[Index] = nullptr;
|
|
TranslucencyLightingViewState.HistoryDirectional[Index] = nullptr;
|
|
|
|
TranslucencyLightingViewState.HistoryMark[Index] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
FRHISamplerState* SamplerStateRHI = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
|
|
const int32 TranslucencyLightingVolumeDim = GetTranslucencyLightingVolumeDim();
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, TranslucentLighting, "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++)
|
|
{
|
|
bool bHasValidHistory = false;
|
|
|
|
FRDGTextureRef HistoryAmbient;
|
|
FRDGTextureRef HistoryDirectional;
|
|
FRDGTextureRef HistoryMark;
|
|
|
|
FVector4f PrevTranslucencyLightingVolumeMin;
|
|
FVector4f PrevTranslucencyLightingVolumeInvSize;
|
|
|
|
if (bTemporal && View.ViewState && !View.bCameraCut && !View.bPrevTransformsReset)
|
|
{
|
|
FTranslucencyLightingViewState& TranslucencyLightingViewState = View.ViewState->TranslucencyLighting;
|
|
|
|
if (TranslucencyLightingViewState.HistoryAmbient[VolumeCascadeIndex] && TranslucencyLightingViewState.HistoryDirectional[VolumeCascadeIndex])
|
|
{
|
|
HistoryAmbient = GraphBuilder.RegisterExternalTexture(TranslucencyLightingViewState.HistoryAmbient[VolumeCascadeIndex]);
|
|
HistoryDirectional = GraphBuilder.RegisterExternalTexture(TranslucencyLightingViewState.HistoryDirectional[VolumeCascadeIndex]);
|
|
|
|
bHasValidHistory = true;
|
|
}
|
|
else
|
|
{
|
|
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder);
|
|
HistoryAmbient = SystemTextures.VolumetricBlack;
|
|
HistoryDirectional = SystemTextures.VolumetricBlack;
|
|
}
|
|
|
|
if (TranslucencyLightingViewState.HistoryMark[VolumeCascadeIndex])
|
|
{
|
|
HistoryMark = GraphBuilder.RegisterExternalTexture(TranslucencyLightingViewState.HistoryMark[VolumeCascadeIndex]);
|
|
}
|
|
else
|
|
{
|
|
HistoryMark = nullptr;
|
|
}
|
|
|
|
{
|
|
const FVector VolumeWorldMin = TranslucencyLightingViewState.HistoryVolumeMin[VolumeCascadeIndex];
|
|
const float VolumeVoxelSize = TranslucencyLightingViewState.HistoryVoxelSize[VolumeCascadeIndex];
|
|
const FVector3f VolumeSize = FVector3f(TranslucencyLightingViewState.HistoryVolumeSize[VolumeCascadeIndex]);
|
|
|
|
const FVector3f VolumeTranslatedWorldMin = FVector3f(VolumeWorldMin + View.ViewMatrices.GetPreViewTranslation());
|
|
|
|
PrevTranslucencyLightingVolumeMin = FVector4f(VolumeTranslatedWorldMin, 1.0f / TranslucencyLightingVolumeDim);
|
|
PrevTranslucencyLightingVolumeInvSize = FVector4f(FVector3f(1.0f) / VolumeSize, VolumeVoxelSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder);
|
|
HistoryAmbient = SystemTextures.VolumetricBlack;
|
|
HistoryDirectional = SystemTextures.VolumetricBlack;
|
|
HistoryMark = nullptr;
|
|
|
|
PrevTranslucencyLightingVolumeMin = FVector4f::Zero();
|
|
PrevTranslucencyLightingVolumeInvSize = FVector4f::Zero();
|
|
}
|
|
|
|
const uint32 TextureIndex = Textures.GetIndex(View, VolumeCascadeIndex);
|
|
|
|
if (!bTemporal || bHasValidHistory)
|
|
{
|
|
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;
|
|
|
|
const FIntVector VolumeSize = FIntVector(TranslucencyLightingVolumeDim);
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FFilterTranslucentVolumeCS::FParameters>();
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
PassParameters->VolumeSize = VolumeSize;
|
|
PassParameters->TexelSize = 1.0f / TranslucencyLightingVolumeDim;
|
|
PassParameters->VolumeCascadeIndex = VolumeCascadeIndex;
|
|
PassParameters->TranslucencyLightingVolumeAmbient = InputVolumeAmbientTexture;
|
|
PassParameters->TranslucencyLightingVolumeDirectional = InputVolumeDirectionalTexture;
|
|
PassParameters->TranslucencyLightingVolumeAmbientSampler = SamplerStateRHI;
|
|
PassParameters->TranslucencyLightingVolumeDirectionalSampler = SamplerStateRHI;
|
|
PassParameters->RWTranslucencyLightingVolumeAmbient = GraphBuilder.CreateUAV(OutputVolumeAmbientTexture);
|
|
PassParameters->RWTranslucencyLightingVolumeDirectional = GraphBuilder.CreateUAV(OutputVolumeDirectionalTexture);
|
|
|
|
PassParameters->PrevTranslucencyLightingVolumeMin = PrevTranslucencyLightingVolumeMin;
|
|
PassParameters->PrevTranslucencyLightingVolumeInvSize = PrevTranslucencyLightingVolumeInvSize;
|
|
|
|
PassParameters->HistoryTextureBilinearUVMin = FVector3f(0.5f / TranslucencyLightingVolumeDim);
|
|
PassParameters->HistoryTextureBilinearUVMax = FVector3f((TranslucencyLightingVolumeDim - 0.5f) / TranslucencyLightingVolumeDim);
|
|
|
|
PassParameters->HistoryAmbient = HistoryAmbient;
|
|
PassParameters->HistoryDirectional = HistoryDirectional;
|
|
PassParameters->HistoryAmbientSampler = SamplerStateRHI;
|
|
PassParameters->HistoryDirectionalSampler = SamplerStateRHI;
|
|
|
|
PassParameters->HistoryMark = HistoryMark;
|
|
|
|
PassParameters->HistoryWeight = CVarTranslucencyLightingVolumeHistoryWeight.GetValueOnRenderThread();
|
|
|
|
FFilterTranslucentVolumeCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FFilterTranslucentVolumeCS::FUseTemporalReprojection>(bTemporal);
|
|
PermutationVector.Set<FFilterTranslucentVolumeCS::FCheckHistoryMark>(HistoryMark != nullptr);
|
|
|
|
auto ComputeShader = View.ShaderMap->GetShader<FFilterTranslucentVolumeCS>(PermutationVector);
|
|
|
|
FIntVector NumGroups = FComputeShaderUtils::GetGroupCount(VolumeSize, FFilterTranslucentVolumeCS::GetGroupSize());
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("Cascade %d", VolumeCascadeIndex),
|
|
ComputeShader,
|
|
PassParameters,
|
|
NumGroups);
|
|
}
|
|
|
|
if (bTemporal && View.ViewState && !View.bStatePrevViewInfoIsReadOnly)
|
|
{
|
|
FTranslucencyLightingViewState& TranslucencyLightingViewState = View.ViewState->TranslucencyLighting;
|
|
|
|
GraphBuilder.QueueTextureExtraction(Textures.Ambient[TextureIndex], &TranslucencyLightingViewState.HistoryAmbient[VolumeCascadeIndex]);
|
|
GraphBuilder.QueueTextureExtraction(Textures.Directional[TextureIndex], &TranslucencyLightingViewState.HistoryDirectional[VolumeCascadeIndex]);
|
|
|
|
if (View.TranslucencyVolumeMarkData[TextureIndex].MarkTexture)
|
|
{
|
|
GraphBuilder.QueueTextureExtraction(View.TranslucencyVolumeMarkData[TextureIndex].MarkTexture, &TranslucencyLightingViewState.HistoryMark[VolumeCascadeIndex]);
|
|
}
|
|
else
|
|
{
|
|
TranslucencyLightingViewState.HistoryMark[VolumeCascadeIndex] = nullptr;
|
|
}
|
|
|
|
TranslucencyLightingViewState.HistoryVolumeMin[VolumeCascadeIndex] = View.TranslucencyLightingVolumeMin[VolumeCascadeIndex];
|
|
TranslucencyLightingViewState.HistoryVoxelSize[VolumeCascadeIndex] = View.TranslucencyVolumeVoxelSize[VolumeCascadeIndex];
|
|
TranslucencyLightingViewState.HistoryVolumeSize[VolumeCascadeIndex] = View.TranslucencyLightingVolumeSize[VolumeCascadeIndex];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SplitSimpleLightsByView(TConstArrayView<FViewInfo> Views, const FSimpleLightArray& SimpleLights, TArrayView<FSimpleLightArray> OutSimpleLightsByView)
|
|
{
|
|
check(OutSimpleLightsByView.Num() == Views.Num());
|
|
|
|
for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); ++LightIndex)
|
|
{
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
FSimpleLightPerViewEntry PerViewEntry = SimpleLights.GetViewDependentData(LightIndex, ViewIndex, Views.Num());
|
|
OutSimpleLightsByView[ViewIndex].InstanceData.Add(SimpleLights.InstanceData[LightIndex]);
|
|
OutSimpleLightsByView[ViewIndex].PerViewData.Add(PerViewEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::GatherTranslucencyVolumeMarkedVoxels(FRDGBuilder& GraphBuilder)
|
|
{
|
|
if (!IsTranslucencyLightingVolumeUsingVoxelMarking() || !ViewFamily.EngineShowFlags.DirectLighting || !RHISupportsVolumeTextureRendering(ViewFamily.GetShaderPlatform()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "TranslucencyLightingVolumeGatherMarkedVoxels");
|
|
|
|
const FIntVector VolumeSize = FIntVector(GetTranslucencyLightingVolumeDim());
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
|
|
for (uint32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++)
|
|
{
|
|
FViewInfo::FTranslucencyVolumeMarkData& VolumeMarkData = View.TranslucencyVolumeMarkData[VolumeCascadeIndex];
|
|
|
|
const bool bUseVolumeMarkTexture = VolumeMarkData.MarkTexture != nullptr;
|
|
|
|
if (VolumeMarkData.MarkTexture == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
VolumeMarkData.VoxelAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("TranslucencyLightingVolume.VoxelAllocator"));
|
|
VolumeMarkData.VoxelData = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), VolumeSize.X * VolumeSize.Y * VolumeSize.Z), TEXT("TranslucencyLightingVolume.VoxelData"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VolumeMarkData.VoxelAllocator), 0);
|
|
|
|
// TODO: when using spatial blur need to dilate marked voxels
|
|
|
|
// gather marked voxels
|
|
{
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FGatherMarkedVoxelsCS::FParameters>();
|
|
PassParameters->RWVoxelAllocator = GraphBuilder.CreateUAV(VolumeMarkData.VoxelAllocator);
|
|
PassParameters->RWVoxelData = GraphBuilder.CreateUAV(VolumeMarkData.VoxelData);
|
|
PassParameters->VolumeMarkTexture = View.TranslucencyVolumeMarkData[VolumeCascadeIndex].MarkTexture;
|
|
PassParameters->VolumeSize = VolumeSize;
|
|
|
|
FGatherMarkedVoxelsCS::FPermutationDomain PermutationVector;
|
|
|
|
auto ComputeShader = View.ShaderMap->GetShader<FGatherMarkedVoxelsCS>(PermutationVector);
|
|
|
|
FIntVector NumGroups = FComputeShaderUtils::GetGroupCount(VolumeSize, FGatherMarkedVoxelsCS::GetGroupSize());
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("GatherMarkedVoxels(VolumeCascade=%d)"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
NumGroups);
|
|
}
|
|
|
|
VolumeMarkData.VoxelIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDispatchIndirectParameters>(1), TEXT("TranslucencyLightingVolume.VoxelIndirectArgs"));
|
|
|
|
// setup indirect args
|
|
{
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FInitIndirectArgsCS::FParameters>();
|
|
PassParameters->RWIndirectArgs = GraphBuilder.CreateUAV(VolumeMarkData.VoxelIndirectArgs);
|
|
PassParameters->VoxelAllocator = GraphBuilder.CreateSRV(VolumeMarkData.VoxelAllocator);
|
|
|
|
FInitIndirectArgsCS::FPermutationDomain PermutationVector;
|
|
|
|
auto ComputeShader = View.ShaderMap->GetShader<FInitIndirectArgsCS>(PermutationVector);
|
|
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("InitIndirectArgs(VolumeCascade=%d)"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
FIntVector(1, 1, 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderTranslucencyLightingVolume(FRDGBuilder& GraphBuilder, FTranslucencyLightingVolumeTextures& Textures, const FSortedLightSetSceneInfo& SortedLightSet)
|
|
{
|
|
if (!GUseTranslucentLightingVolumes || !ViewFamily.EngineShowFlags.DirectLighting || !RHISupportsVolumeTextureRendering(ViewFamily.GetShaderPlatform()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "TranslucencyLightingVolume");
|
|
SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime);
|
|
|
|
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator>& SortedLights = SortedLightSet.SortedLights;
|
|
const FSimpleLightArray& SimpleLights = SortedLightSet.SimpleLights;
|
|
const int32 SimpleLightsEnd = SortedLightSet.SimpleLightsEnd;
|
|
|
|
const bool bMegaLightsTranslucencyVolume = MegaLights::IsEnabled(ViewFamily) && MegaLights::UseTranslucencyVolume();
|
|
|
|
if (SimpleLights.InstanceData.Num() > 0)
|
|
{
|
|
auto& SimpleLightsByView = *GraphBuilder.AllocObject<TArray<FSimpleLightArray, SceneRenderingAllocator>>();
|
|
SimpleLightsByView.SetNum(Views.Num());
|
|
|
|
SplitSimpleLightsByView(Views, SimpleLights, SimpleLightsByView);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
FSimpleLightArray& SimpleLightArray = SimpleLightsByView[ViewIndex];
|
|
|
|
if (SimpleLightArray.InstanceData.Num() > 0)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "InjectSimpleLightsTranslucentLighting");
|
|
InjectSimpleTranslucencyLightingVolumeArray(GraphBuilder, View, ViewIndex, Views.Num(), Textures, SimpleLightArray);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Shadowed and light function lights
|
|
{
|
|
FTranslucentLightInjectionCollector Collector(GraphBuilder, Views, bAreLightsInLightGrid);
|
|
|
|
// Collect all the light injection data
|
|
for (int32 LightIndex = SimpleLightsEnd; LightIndex < SortedLights.Num(); LightIndex++)
|
|
{
|
|
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
|
|
const FLightSceneInfo& LightSceneInfo = *SortedLightInfo.LightSceneInfo;
|
|
const FLightSceneProxy& LightSceneProxy = *LightSceneInfo.Proxy;
|
|
|
|
if (bMegaLightsTranslucencyVolume && SortedLightInfo.SortKey.Fields.bHandledByMegaLights)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const bool bDrawShadows = SortedLightInfo.SortKey.Fields.bShadowed;
|
|
const FLightOcclusionType OcclusionType = GetLightOcclusionType(LightSceneProxy, ViewFamily);
|
|
const bool bSupportShadowMaps = bDrawShadows && OcclusionType == FLightOcclusionType::Shadowmap;
|
|
|
|
// Collect all the light injection data
|
|
CollectLightForTranslucencyLightingVolumeInjection(&LightSceneInfo, bSupportShadowMaps, Collector);
|
|
}
|
|
|
|
// Run light injection for each view
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "InjectTranslucencyLightingVolume(View=%d)", ViewIndex);
|
|
InjectTranslucencyLightingVolume(GraphBuilder, View, ViewIndex, Scene, *this, Collector, Textures);
|
|
}
|
|
}
|
|
|
|
InjectTranslucencyLightingVolumeAmbientCubemap(GraphBuilder, Views, Textures);
|
|
if (SortedLightSet.MegaLightsLightStart < SortedLightSet.SortedLights.Num())
|
|
{
|
|
InjectTranslucencyLightingVolumeMegaLights(GraphBuilder, Views, Textures);
|
|
}
|
|
FilterTranslucencyLightingVolume(GraphBuilder, Views, Textures);
|
|
}
|
|
|
|
class FTranslucentLightingMaterialPSOCollector : public IPSOCollector
|
|
{
|
|
public:
|
|
FTranslucentLightingMaterialPSOCollector(ERHIFeatureLevel::Type InFeatureLevel) :
|
|
IPSOCollector(FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(InFeatureLevel), TranslucentLightingMaterialPSOCollectorName)),
|
|
FeatureLevel(InFeatureLevel)
|
|
{
|
|
}
|
|
|
|
virtual void CollectPSOInitializers(
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
const FMaterial& Material,
|
|
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
|
|
const FPSOPrecacheParams& PreCacheParams,
|
|
TArray<FPSOPrecacheData>& PSOInitializers
|
|
) override final;
|
|
|
|
private:
|
|
|
|
ERHIFeatureLevel::Type FeatureLevel;
|
|
};
|
|
|
|
void FTranslucentLightingMaterialPSOCollector::CollectPSOInitializers(
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
const FMaterial& Material,
|
|
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
|
|
const FPSOPrecacheParams& PreCacheParams,
|
|
TArray<FPSOPrecacheData>& PSOInitializers
|
|
)
|
|
{
|
|
if (!Material.IsLightFunction() || GTranslucencyLightingVolumeMaterialPSOPrecache == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(GlobalShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(GlobalShaderMap);
|
|
const FMaterialShaderMap* MaterialShaderMap = Material.GetGameThreadShaderMap();
|
|
|
|
EPixelFormat TranslucencyPixelFormat;
|
|
ETextureCreateFlags TranslucencyTargetFlags;
|
|
FTranslucencyLightingVolumeTextures::GetTextureFormatAndCreationFlags(TranslucencyPixelFormat, TranslucencyTargetFlags);
|
|
|
|
auto AddPSOInitializer = [&](bool bDirectionalLight, bool bDynamicShadow, bool bApplyLightFunction, bool bUseVSM, bool bUseAdaptiveVolumetricShadowMap)
|
|
{
|
|
FTranslucentLightingInjectPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FRadialAttenuation >(!bDirectionalLight);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FDynamicallyShadowed >(bDynamicShadow);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FLightFunction >(bApplyLightFunction);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FVirtualShadowMap >(bUseVSM);
|
|
PermutationVector.Set< FTranslucentLightingInjectPS::FAdaptiveVolumetricShadowMap >(bUseAdaptiveVolumetricShadowMap);
|
|
|
|
auto PixelShader = MaterialShaderMap->GetShader< FTranslucentLightingInjectPS >(PermutationVector);
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
SetupPSOStateForVolumeInjection(VertexShader, GeometryShader, PixelShader.GetPixelShader(), bDirectionalLight, GraphicsPSOInit);
|
|
|
|
// What render target formats to support?
|
|
FGraphicsPipelineRenderTargetsInfo RenderTargetsInfo;
|
|
RenderTargetsInfo.NumSamples = 1;
|
|
AddRenderTargetInfo(TranslucencyPixelFormat, TranslucencyTargetFlags, RenderTargetsInfo);
|
|
AddRenderTargetInfo(TranslucencyPixelFormat, TranslucencyTargetFlags, RenderTargetsInfo);
|
|
|
|
GraphicsPSOInit.StatePrecachePSOHash = RHIComputeStatePrecachePSOHash(GraphicsPSOInit);
|
|
ApplyTargetsInfo(GraphicsPSOInit, RenderTargetsInfo);
|
|
|
|
FPSOPrecacheData PSOPrecacheData;
|
|
PSOPrecacheData.bRequired = true;
|
|
PSOPrecacheData.Type = FPSOPrecacheData::EType::Graphics;
|
|
PSOPrecacheData.GraphicsPSOInitializer = GraphicsPSOInit;
|
|
#if PSO_PRECACHING_VALIDATE
|
|
PSOPrecacheData.PSOCollectorIndex = PSOCollectorIndex;
|
|
PSOPrecacheData.VertexFactoryType = nullptr;
|
|
#endif // PSO_PRECACHING_VALIDATE
|
|
|
|
PSOInitializers.Add(MoveTemp(PSOPrecacheData));
|
|
};
|
|
|
|
// Generate PSOs for all possible permutations - we don't have that many light function materials
|
|
for (int32 DirectionalLightIndex = 0; DirectionalLightIndex < 2; ++DirectionalLightIndex)
|
|
{
|
|
bool bDirectionalLight = DirectionalLightIndex > 0;
|
|
for (int32 DynamicShadowIndex = 0; DynamicShadowIndex < 2; ++DynamicShadowIndex)
|
|
{
|
|
bool bDynamicShadow = DynamicShadowIndex > 0;
|
|
for (int32 ApplyLightFunctionIndex = 0; ApplyLightFunctionIndex < 2; ++ApplyLightFunctionIndex)
|
|
{
|
|
bool bApplyLightFunction = ApplyLightFunctionIndex > 0;
|
|
for (int32 UseVSMIndex = 0; UseVSMIndex < 2; ++UseVSMIndex)
|
|
{
|
|
bool bUseVSM = UseVSMIndex > 0;
|
|
for (int32 UseAdaptiveVolumetricShadowMapIndex = 0; UseAdaptiveVolumetricShadowMapIndex < 2; ++UseAdaptiveVolumetricShadowMapIndex)
|
|
{
|
|
bool bUseAdaptiveVolumetricShadowMap = UseAdaptiveVolumetricShadowMapIndex > 0;
|
|
AddPSOInitializer(bDirectionalLight, bDynamicShadow, bApplyLightFunction, bUseVSM, bUseAdaptiveVolumetricShadowMap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IPSOCollector* CreateTranslucentLightingMaterialPSOCollector(ERHIFeatureLevel::Type FeatureLevel)
|
|
{
|
|
return new FTranslucentLightingMaterialPSOCollector(FeatureLevel);
|
|
}
|
|
FRegisterPSOCollectorCreateFunction RegisterTranslucentLightingMaterialPSOCollector(&CreateTranslucentLightingMaterialPSOCollector, EShadingPath::Deferred, TranslucentLightingMaterialPSOCollectorName);
|
|
|