Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/LightRendering.cpp
Charles deRousiers 1c43b10822 Fix crash when hair voxelization is disabled.
#rb none
#jira UE-115975

[CL 16388893 by Charles deRousiers in ue5-main branch]
2021-05-19 13:16:21 -04:00

2818 lines
119 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LightRendering.cpp: Light rendering implementation.
=============================================================================*/
#include "LightRendering.h"
#include "RendererModule.h"
#include "DeferredShadingRenderer.h"
#include "ScenePrivate.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "Engine/SubsurfaceProfile.h"
#include "ShowFlags.h"
#include "VisualizeTexture.h"
#include "RayTracing/RaytracingOptions.h"
#include "SceneTextureParameters.h"
#include "HairStrands/HairStrandsRendering.h"
#include "ScreenPass.h"
#include "SkyAtmosphereRendering.h"
#include "VolumetricCloudRendering.h"
#include "Strata/Strata.h"
#include "VirtualShadowMaps/VirtualShadowMapProjection.h"
#include "HairStrands/HairStrandsData.h"
// ENABLE_DEBUG_DISCARD_PROP is used to test the lighting code by allowing to discard lights to see how performance scales
// It ought never to be enabled in a shipping build, and is probably only really useful when woring on the shading code.
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
#define ENABLE_DEBUG_DISCARD_PROP 1
#else // (UE_BUILD_SHIPPING || UE_BUILD_TEST)
#define ENABLE_DEBUG_DISCARD_PROP 0
#endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
DECLARE_GPU_STAT(Lights);
IMPLEMENT_TYPE_LAYOUT(FLightFunctionSharedParameters);
IMPLEMENT_TYPE_LAYOUT(FStencilingGeometryShaderParameters);
IMPLEMENT_TYPE_LAYOUT(FOnePassPointShadowProjectionShaderParameters);
IMPLEMENT_TYPE_LAYOUT(FShadowProjectionShaderParameters);
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FDeferredLightUniformStruct, "DeferredLightUniforms");
extern int32 GUseTranslucentLightingVolumes;
ENGINE_API IPooledRenderTarget* GetSubsufaceProfileTexture_RT(FRHICommandListImmediate& RHICmdList);
extern TAutoConsoleVariable<int32> CVarVirtualShadowOnePassProjection;
static int32 GAllowDepthBoundsTest = 1;
static FAutoConsoleVariableRef CVarAllowDepthBoundsTest(
TEXT("r.AllowDepthBoundsTest"),
GAllowDepthBoundsTest,
TEXT("If true, use enable depth bounds test when rendering defered lights.")
);
static int32 bAllowSimpleLights = 1;
static FAutoConsoleVariableRef CVarAllowSimpleLights(
TEXT("r.AllowSimpleLights"),
bAllowSimpleLights,
TEXT("If true, we allow simple (ie particle) lights")
);
static TAutoConsoleVariable<int32> CVarRayTracingOcclusion(
TEXT("r.RayTracing.Shadows"),
0,
TEXT("0: use traditional rasterized shadow map (default)\n")
TEXT("1: use ray tracing shadows"),
ECVF_RenderThreadSafe | ECVF_Scalability);
static int32 GShadowRayTracingSamplesPerPixel = -1;
static FAutoConsoleVariableRef CVarShadowRayTracingSamplesPerPixel(
TEXT("r.RayTracing.Shadows.SamplesPerPixel"),
GShadowRayTracingSamplesPerPixel,
TEXT("Sets the samples-per-pixel for directional light occlusion (default = 1)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarShadowUseDenoiser(
TEXT("r.Shadow.Denoiser"),
2,
TEXT("Choose the denoising algorithm.\n")
TEXT(" 0: Disabled (default);\n")
TEXT(" 1: Forces the default denoiser of the renderer;\n")
TEXT(" 2: GScreenSpaceDenoiser witch may be overriden by a third party plugin.\n"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarMaxShadowDenoisingBatchSize(
TEXT("r.Shadow.Denoiser.MaxBatchSize"), 4,
TEXT("Maximum number of shadow to denoise at the same time."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarMaxShadowRayTracingBatchSize(
TEXT("r.RayTracing.Shadows.MaxBatchSize"), 8,
TEXT("Maximum number of shadows to trace at the same time."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarAllowClearLightSceneExtentsOnly(
TEXT("r.AllowClearLightSceneExtentsOnly"), 1,
TEXT(""),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRayTracingShadowsDirectionalLight(
TEXT("r.RayTracing.Shadows.Lights.Directional"),
1,
TEXT("Enables ray tracing shadows for directional lights (default = 1)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRayTracingShadowsPointLight(
TEXT("r.RayTracing.Shadows.Lights.Point"),
1,
TEXT("Enables ray tracing shadows for point lights (default = 1)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRayTracingShadowsSpotLight(
TEXT("r.RayTracing.Shadows.Lights.Spot"),
1,
TEXT("Enables ray tracing shadows for spot lights (default = 1)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRayTracingShadowsRectLight(
TEXT("r.RayTracing.Shadows.Lights.Rect"),
1,
TEXT("Enables ray tracing shadows for rect light (default = 1)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarAppliedLightFunctionOnHair(
TEXT("r.HairStrands.LightFunction"),
1,
TEXT("Enables Light function on hair"),
ECVF_RenderThreadSafe);
#if ENABLE_DEBUG_DISCARD_PROP
static float GDebugLightDiscardProp = 0.0f;
static FAutoConsoleVariableRef CVarDebugLightDiscardProp(
TEXT("r.DebugLightDiscardProp"),
GDebugLightDiscardProp,
TEXT("[0,1]: Proportion of lights to discard for debug/performance profiling purposes.")
);
#endif // ENABLE_DEBUG_DISCARD_PROP
#if RHI_RAYTRACING
static bool ShouldRenderRayTracingShadowsForLightType(ELightComponentType LightType)
{
switch(LightType)
{
case LightType_Directional:
return !!CVarRayTracingShadowsDirectionalLight.GetValueOnRenderThread();
case LightType_Point:
return !!CVarRayTracingShadowsPointLight.GetValueOnRenderThread();
case LightType_Spot:
return !!CVarRayTracingShadowsSpotLight.GetValueOnRenderThread();
case LightType_Rect:
return !!CVarRayTracingShadowsRectLight.GetValueOnRenderThread();
default:
return true;
}
}
bool ShouldRenderRayTracingShadows()
{
return ShouldRenderRayTracingEffect(CVarRayTracingOcclusion.GetValueOnRenderThread() > 0);
}
bool ShouldRenderRayTracingShadowsForLight(const FLightSceneProxy& LightProxy)
{
return ShouldRenderRayTracingShadows() && LightProxy.CastsRaytracedShadow()
&& ShouldRenderRayTracingShadowsForLightType((ELightComponentType)LightProxy.GetLightType());
}
bool ShouldRenderRayTracingShadowsForLight(const FLightSceneInfoCompact& LightInfo)
{
return ShouldRenderRayTracingShadows() && LightInfo.bCastRaytracedShadow
&& ShouldRenderRayTracingShadowsForLightType((ELightComponentType)LightInfo.LightType);
}
#endif // RHI_RAYTRACING
FDeferredLightUniformStruct GetDeferredLightParameters(const FSceneView& View, const FLightSceneInfo& LightSceneInfo)
{
FDeferredLightUniformStruct Parameters;
LightSceneInfo.Proxy->GetLightShaderParameters(Parameters.LightParameters);
const bool bIsRayTracedLight = ShouldRenderRayTracingShadowsForLight(*LightSceneInfo.Proxy);
const FVector2D FadeParams = LightSceneInfo.Proxy->GetDirectionalLightDistanceFadeParameters(View.GetFeatureLevel(), !bIsRayTracedLight && LightSceneInfo.IsPrecomputedLightingValid(), View.MaxShadowCascades);
// use MAD for efficiency in the shader
Parameters.DistanceFadeMAD = FVector2D(FadeParams.Y, -FadeParams.X * FadeParams.Y);
int32 ShadowMapChannel = LightSceneInfo.Proxy->GetShadowMapChannel();
static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
const bool bAllowStaticLighting = (!AllowStaticLightingVar || AllowStaticLightingVar->GetValueOnRenderThread() != 0);
if (!bAllowStaticLighting)
{
ShadowMapChannel = INDEX_NONE;
}
Parameters.ShadowMapChannelMask = FVector4(
ShadowMapChannel == 0 ? 1 : 0,
ShadowMapChannel == 1 ? 1 : 0,
ShadowMapChannel == 2 ? 1 : 0,
ShadowMapChannel == 3 ? 1 : 0);
const bool bDynamicShadows = View.Family->EngineShowFlags.DynamicShadows && GetShadowQuality() > 0;
const bool bHasLightFunction = LightSceneInfo.Proxy->GetLightFunctionMaterial() != NULL;
Parameters.ShadowedBits = LightSceneInfo.Proxy->CastsStaticShadow() || bHasLightFunction ? 1 : 0;
Parameters.ShadowedBits |= LightSceneInfo.Proxy->CastsDynamicShadow() && View.Family->EngineShowFlags.DynamicShadows ? 3 : 0;
Parameters.VolumetricScatteringIntensity = LightSceneInfo.Proxy->GetVolumetricScatteringIntensity();
static auto* ContactShadowsCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ContactShadows"));
static auto* IntensityCVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.ContactShadows.NonShadowCastingIntensity"));
Parameters.ContactShadowLength = 0;
Parameters.ContactShadowNonShadowCastingIntensity = 0.0f;
if (ContactShadowsCVar && ContactShadowsCVar->GetValueOnRenderThread() != 0 && View.Family->EngineShowFlags.ContactShadows)
{
Parameters.ContactShadowLength = LightSceneInfo.Proxy->GetContactShadowLength();
// Sign indicates if contact shadow length is in world space or screen space.
// Multiply by 2 for screen space in order to preserve old values after introducing multiply by View.ClipToView[1][1] in shader.
Parameters.ContactShadowLength *= LightSceneInfo.Proxy->IsContactShadowLengthInWS() ? -1.0f : 2.0f;
Parameters.ContactShadowNonShadowCastingIntensity = IntensityCVar ? IntensityCVar->GetValueOnRenderThread() : 0.0f;
}
// When rendering reflection captures, the direct lighting of the light is actually the indirect specular from the main view
if (View.bIsReflectionCapture)
{
Parameters.LightParameters.Color *= LightSceneInfo.Proxy->GetIndirectLightingScale();
}
const ELightComponentType LightType = (ELightComponentType)LightSceneInfo.Proxy->GetLightType();
if ((LightType == LightType_Point || LightType == LightType_Spot || LightType == LightType_Rect) && View.IsPerspectiveProjection())
{
Parameters.LightParameters.Color *= GetLightFadeFactor(View, LightSceneInfo.Proxy);
}
Parameters.LightingChannelMask = LightSceneInfo.Proxy->GetLightingChannelMask();
return Parameters;
}
void SetupSimpleDeferredLightParameters(
const FSimpleLightEntry& SimpleLight,
const FSimpleLightPerViewEntry &SimpleLightPerViewData,
FDeferredLightUniformStruct& DeferredLightUniformsValue)
{
DeferredLightUniformsValue.LightParameters.Position = SimpleLightPerViewData.Position;
DeferredLightUniformsValue.LightParameters.InvRadius = 1.0f / FMath::Max(SimpleLight.Radius, KINDA_SMALL_NUMBER);
DeferredLightUniformsValue.LightParameters.Color = SimpleLight.Color;
DeferredLightUniformsValue.LightParameters.FalloffExponent = SimpleLight.Exponent;
DeferredLightUniformsValue.LightParameters.Direction = FVector(1, 0, 0);
DeferredLightUniformsValue.LightParameters.Tangent = FVector(1, 0, 0);
DeferredLightUniformsValue.LightParameters.SpotAngles = FVector2D(-2, 1);
DeferredLightUniformsValue.LightParameters.SpecularScale = 1.0f;
DeferredLightUniformsValue.LightParameters.SourceRadius = 0.0f;
DeferredLightUniformsValue.LightParameters.SoftSourceRadius = 0.0f;
DeferredLightUniformsValue.LightParameters.SourceLength = 0.0f;
DeferredLightUniformsValue.LightParameters.SourceTexture = GWhiteTexture->TextureRHI;
DeferredLightUniformsValue.ContactShadowLength = 0.0f;
DeferredLightUniformsValue.DistanceFadeMAD = FVector2D(0, 0);
DeferredLightUniformsValue.ShadowMapChannelMask = FVector4(0, 0, 0, 0);
DeferredLightUniformsValue.ShadowedBits = 0;
DeferredLightUniformsValue.LightingChannelMask = 0;
}
FLightOcclusionType GetLightOcclusionType(const FLightSceneProxy& Proxy)
{
#if RHI_RAYTRACING
return ShouldRenderRayTracingShadowsForLight(Proxy) ? FLightOcclusionType::Raytraced : FLightOcclusionType::Shadowmap;
#else
return FLightOcclusionType::Shadowmap;
#endif
}
FLightOcclusionType GetLightOcclusionType(const FLightSceneInfoCompact& LightInfo)
{
#if RHI_RAYTRACING
return ShouldRenderRayTracingShadowsForLight(LightInfo) ? FLightOcclusionType::Raytraced : FLightOcclusionType::Shadowmap;
#else
return FLightOcclusionType::Shadowmap;
#endif
}
float GetLightFadeFactor(const FSceneView& View, const FLightSceneProxy* Proxy)
{
// Distance fade
FSphere Bounds = Proxy->GetBoundingSphere();
const float DistanceSquared = (Bounds.Center - View.ViewMatrices.GetViewOrigin()).SizeSquared();
extern float GMinScreenRadiusForLights;
float SizeFade = FMath::Square(FMath::Min(0.0002f, GMinScreenRadiusForLights / Bounds.W) * View.LODDistanceFactor) * DistanceSquared;
SizeFade = FMath::Clamp(6.0f - 6.0f * SizeFade, 0.0f, 1.0f);
extern float GLightMaxDrawDistanceScale;
float MaxDist = Proxy->GetMaxDrawDistance() * GLightMaxDrawDistanceScale;
float Range = Proxy->GetFadeRange();
float DistanceFade = MaxDist ? (MaxDist - FMath::Sqrt(DistanceSquared)) / Range : 1.0f;
DistanceFade = FMath::Clamp(DistanceFade, 0.0f, 1.0f);
return SizeFade * DistanceFade;
}
void StencilingGeometry::DrawSphere(FRHICommandList& RHICmdList)
{
RHICmdList.SetStreamSource(0, StencilingGeometry::GStencilSphereVertexBuffer.VertexBufferRHI, 0);
RHICmdList.DrawIndexedPrimitive(StencilingGeometry::GStencilSphereIndexBuffer.IndexBufferRHI, 0, 0,
StencilingGeometry::GStencilSphereVertexBuffer.GetVertexCount(), 0,
StencilingGeometry::GStencilSphereIndexBuffer.GetIndexCount() / 3, 1);
}
void StencilingGeometry::DrawVectorSphere(FRHICommandList& RHICmdList)
{
RHICmdList.SetStreamSource(0, StencilingGeometry::GStencilSphereVectorBuffer.VertexBufferRHI, 0);
RHICmdList.DrawIndexedPrimitive(StencilingGeometry::GStencilSphereIndexBuffer.IndexBufferRHI, 0, 0,
StencilingGeometry::GStencilSphereVectorBuffer.GetVertexCount(), 0,
StencilingGeometry::GStencilSphereIndexBuffer.GetIndexCount() / 3, 1);
}
void StencilingGeometry::DrawCone(FRHICommandList& RHICmdList)
{
// No Stream Source needed since it will generate vertices on the fly
RHICmdList.SetStreamSource(0, StencilingGeometry::GStencilConeVertexBuffer.VertexBufferRHI, 0);
RHICmdList.DrawIndexedPrimitive(StencilingGeometry::GStencilConeIndexBuffer.IndexBufferRHI, 0, 0,
FStencilConeIndexBuffer::NumVerts, 0, StencilingGeometry::GStencilConeIndexBuffer.GetIndexCount() / 3, 1);
}
/** The stencil sphere vertex buffer. */
TGlobalResource<StencilingGeometry::TStencilSphereVertexBuffer<18, 12, FVector4> > StencilingGeometry::GStencilSphereVertexBuffer;
TGlobalResource<StencilingGeometry::TStencilSphereVertexBuffer<18, 12, FVector> > StencilingGeometry::GStencilSphereVectorBuffer;
/** The stencil sphere index buffer. */
TGlobalResource<StencilingGeometry::TStencilSphereIndexBuffer<18, 12> > StencilingGeometry::GStencilSphereIndexBuffer;
TGlobalResource<StencilingGeometry::TStencilSphereVertexBuffer<4, 4, FVector4> > StencilingGeometry::GLowPolyStencilSphereVertexBuffer;
TGlobalResource<StencilingGeometry::TStencilSphereIndexBuffer<4, 4> > StencilingGeometry::GLowPolyStencilSphereIndexBuffer;
/** The (dummy) stencil cone vertex buffer. */
TGlobalResource<StencilingGeometry::FStencilConeVertexBuffer> StencilingGeometry::GStencilConeVertexBuffer;
/** The stencil cone index buffer. */
TGlobalResource<StencilingGeometry::FStencilConeIndexBuffer> StencilingGeometry::GStencilConeIndexBuffer;
// Implement a version for directional lights, and a version for point / spot lights
IMPLEMENT_SHADER_TYPE(template<>,TDeferredLightVS<false>,TEXT("/Engine/Private/DeferredLightVertexShaders.usf"),TEXT("DirectionalVertexMain"),SF_Vertex);
IMPLEMENT_SHADER_TYPE(template<>,TDeferredLightVS<true>,TEXT("/Engine/Private/DeferredLightVertexShaders.usf"),TEXT("RadialVertexMain"),SF_Vertex);
struct FRenderLightParams
{
// Precompute transmittance
FShaderResourceViewRHIRef DeepShadow_TransmittanceMaskBuffer = nullptr;
uint32 DeepShadow_TransmittanceMaskBufferMaxCount = 0;
FRHITexture* ScreenShadowMaskSubPixelTexture = nullptr;
// Cloud shadow data
FMatrix Cloud_WorldToLightClipShadowMatrix;
float Cloud_ShadowmapFarDepthKm = 0.0f;
FRHITexture* Cloud_ShadowmapTexture = nullptr;
float Cloud_ShadowmapStrength = 0.0f;
};
class TDeferredLightHairVS : public FGlobalShader
{
DECLARE_SHADER_TYPE(TDeferredLightHairVS, Global);
public:
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("SHADER_HAIR"), 1);
}
TDeferredLightHairVS() {}
TDeferredLightHairVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
FGlobalShader(Initializer)
{
HairStrandsParameters.Bind(Initializer.ParameterMap, FHairStrandsViewUniformParameters::StaticStructMetadata.GetShaderVariableName());
}
void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, FRHIUniformBuffer* HairStrandsUniformBuffer)
{
FRHIVertexShader* ShaderRHI = RHICmdList.GetBoundVertexShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
if (HairStrandsUniformBuffer)
{
SetUniformBufferParameter(RHICmdList, ShaderRHI, HairStrandsParameters, HairStrandsUniformBuffer);
}
}
private:
LAYOUT_FIELD(FShaderUniformBufferParameter, HairStrandsParameters);
};
IMPLEMENT_SHADER_TYPE(, TDeferredLightHairVS, TEXT("/Engine/Private/DeferredLightVertexShaders.usf"), TEXT("HairVertexMain"), SF_Vertex);
enum class ELightSourceShape
{
Directional,
Capsule,
Rect,
MAX
};
/** A pixel shader for rendering the light in a deferred pass. */
class FDeferredLightPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FDeferredLightPS, Global)
class FSourceShapeDim : SHADER_PERMUTATION_ENUM_CLASS("LIGHT_SOURCE_SHAPE", ELightSourceShape);
class FSourceTextureDim : SHADER_PERMUTATION_BOOL("USE_SOURCE_TEXTURE");
class FIESProfileDim : SHADER_PERMUTATION_BOOL("USE_IES_PROFILE");
class FInverseSquaredDim : SHADER_PERMUTATION_BOOL("INVERSE_SQUARED_FALLOFF");
class FVisualizeCullingDim : SHADER_PERMUTATION_BOOL("VISUALIZE_LIGHT_CULLING");
class FLightingChannelsDim : SHADER_PERMUTATION_BOOL("USE_LIGHTING_CHANNELS");
class FTransmissionDim : SHADER_PERMUTATION_BOOL("USE_TRANSMISSION");
class FHairLighting : SHADER_PERMUTATION_INT("USE_HAIR_LIGHTING", 2);
class FAtmosphereTransmittance : SHADER_PERMUTATION_BOOL("USE_ATMOSPHERE_TRANSMITTANCE");
class FCloudTransmittance : SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE");
class FAnistropicMaterials : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
class FStrataFastPath : SHADER_PERMUTATION_BOOL("STRATA_FASTPATH");
using FPermutationDomain = TShaderPermutationDomain<
FSourceShapeDim,
FSourceTextureDim,
FIESProfileDim,
FInverseSquaredDim,
FVisualizeCullingDim,
FLightingChannelsDim,
FTransmissionDim,
FHairLighting,
FAtmosphereTransmittance,
FCloudTransmittance,
FAnistropicMaterials,
FStrataFastPath>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
if( PermutationVector.Get< FSourceShapeDim >() == ELightSourceShape::Directional && (
PermutationVector.Get< FIESProfileDim >() ||
PermutationVector.Get< FInverseSquaredDim >() ) )
{
return false;
}
if (PermutationVector.Get< FSourceShapeDim >() != ELightSourceShape::Directional && (PermutationVector.Get<FAtmosphereTransmittance>() || PermutationVector.Get<FCloudTransmittance>()))
{
return false;
}
if( PermutationVector.Get< FSourceShapeDim >() == ELightSourceShape::Rect )
{
if( !PermutationVector.Get< FInverseSquaredDim >() )
{
return false;
}
}
else
{
if( PermutationVector.Get< FSourceTextureDim >() )
{
return false;
}
}
if (PermutationVector.Get< FHairLighting >() && (
PermutationVector.Get< FVisualizeCullingDim >() ||
PermutationVector.Get< FTransmissionDim >()))
{
return false;
}
if (PermutationVector.Get<FDeferredLightPS::FAnistropicMaterials>())
{
// Anisotropic materials do not currently support rect lights
if (PermutationVector.Get<FSourceShapeDim>() == ELightSourceShape::Rect || PermutationVector.Get<FSourceTextureDim>())
{
return false;
}
// (Hair Lighting == 2) has its own BxDF and anisotropic BRDF is only for DefaultLit and ClearCoat materials.
if (PermutationVector.Get<FHairLighting>() == 2)
{
return false;
}
if (!FDataDrivenShaderPlatformInfo::GetSupportsAnisotropicMaterials(Parameters.Platform))
{
return false;
}
}
if (PermutationVector.Get<FStrataFastPath>() && !Strata::IsStrataEnabled())
{
return false;
}
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("USE_HAIR_COMPLEX_TRANSMITTANCE"), IsHairStrandsSupported(EHairStrandsShaderType::All, Parameters.Platform) ? 1u : 0u);
OutEnvironment.SetDefine(TEXT("STRATA_ENABLED"), Strata::IsStrataEnabled() ? 1u : 0u);
}
FDeferredLightPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
LightAttenuationTexture.Bind(Initializer.ParameterMap, TEXT("LightAttenuationTexture"));
LightAttenuationTextureSampler.Bind(Initializer.ParameterMap, TEXT("LightAttenuationTextureSampler"));
LTCMatTexture.Bind(Initializer.ParameterMap, TEXT("LTCMatTexture"));
LTCMatSampler.Bind(Initializer.ParameterMap, TEXT("LTCMatSampler"));
LTCAmpTexture.Bind(Initializer.ParameterMap, TEXT("LTCAmpTexture"));
LTCAmpSampler.Bind(Initializer.ParameterMap, TEXT("LTCAmpSampler"));
IESTexture.Bind(Initializer.ParameterMap, TEXT("IESTexture"));
IESTextureSampler.Bind(Initializer.ParameterMap, TEXT("IESTextureSampler"));
LightingChannelsTexture.Bind(Initializer.ParameterMap, TEXT("LightingChannelsTexture"));
LightingChannelsSampler.Bind(Initializer.ParameterMap, TEXT("LightingChannelsSampler"));
TransmissionProfilesTexture.Bind(Initializer.ParameterMap, TEXT("SSProfilesTexture"));
TransmissionProfilesLinearSampler.Bind(Initializer.ParameterMap, TEXT("TransmissionProfilesLinearSampler"));
HairTransmittanceBuffer.Bind(Initializer.ParameterMap, TEXT("HairTransmittanceBuffer"));
HairTransmittanceBufferMaxCount.Bind(Initializer.ParameterMap, TEXT("HairTransmittanceBufferMaxCount"));
ScreenShadowMaskSubPixelTexture.Bind(Initializer.ParameterMap, TEXT("ScreenShadowMaskSubPixelTexture")); // TODO hook the shader itself
HairShadowMaskValid.Bind(Initializer.ParameterMap, TEXT("HairShadowMaskValid"));
HairStrandsParameters.Bind(Initializer.ParameterMap, FHairStrandsViewUniformParameters::StaticStructMetadata.GetShaderVariableName());
DummyRectLightTextureForCapsuleCompilerWarning.Bind(Initializer.ParameterMap, TEXT("DummyRectLightTextureForCapsuleCompilerWarning"));
CloudShadowmapTexture.Bind(Initializer.ParameterMap, TEXT("CloudShadowmapTexture"));
CloudShadowmapSampler.Bind(Initializer.ParameterMap, TEXT("CloudShadowmapSampler"));
CloudShadowmapFarDepthKm.Bind(Initializer.ParameterMap, TEXT("CloudShadowmapFarDepthKm"));
CloudShadowmapWorldToLightClipMatrix.Bind(Initializer.ParameterMap, TEXT("CloudShadowmapWorldToLightClipMatrix"));
CloudShadowmapStrength.Bind(Initializer.ParameterMap, TEXT("CloudShadowmapStrength"));
}
FDeferredLightPS()
{}
public:
void SetParameters(
FRHICommandList& RHICmdList,
const FViewInfo& View,
const FLightSceneInfo* LightSceneInfo,
FRHITexture* ScreenShadowMaskTexture,
FRHITexture* LightingChannelsTextureRHI,
FRenderLightParams* RenderLightParams,
FRHIUniformBuffer* HairStrandsUniformBuffer)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
SetParametersBase(RHICmdList, ShaderRHI, View, ScreenShadowMaskTexture, LightingChannelsTextureRHI, LightSceneInfo->Proxy->GetIESTextureResource(), RenderLightParams);
SetDeferredLightParameters(RHICmdList, ShaderRHI, GetUniformBufferParameter<FDeferredLightUniformStruct>(), LightSceneInfo, View);
if (HairStrandsUniformBuffer)
{
SetUniformBufferParameter(RHICmdList, ShaderRHI, HairStrandsParameters, HairStrandsUniformBuffer);
}
}
void SetParametersSimpleLight(FRHICommandList& RHICmdList, const FViewInfo& View, const FSimpleLightEntry& SimpleLight, const FSimpleLightPerViewEntry& SimpleLightPerViewData)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
SetParametersBase(RHICmdList, ShaderRHI, View, nullptr, nullptr, nullptr, nullptr);
SetSimpleDeferredLightParameters(RHICmdList, ShaderRHI, GetUniformBufferParameter<FDeferredLightUniformStruct>(), SimpleLight, SimpleLightPerViewData, View);
}
private:
void SetParametersBase(
FRHICommandList& RHICmdList,
FRHIPixelShader* ShaderRHI,
const FViewInfo& View,
FRHITexture* ScreenShadowMaskTexture,
FRHITexture* LightingChannelsTextureRHI,
FTexture* IESTextureResource,
FRenderLightParams* RenderLightParams)
{
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
if (TRDGUniformBufferRef<FStrataGlobalUniformParameters> StrataUniformBuffer = Strata::BindStrataGlobalUniformParameters(View.StrataSceneData))
{
FGlobalShader::SetParameters<FStrataGlobalUniformParameters>(RHICmdList, ShaderRHI, StrataUniformBuffer->GetRHIRef());
}
if(LightAttenuationTexture.IsBound())
{
if (!ScreenShadowMaskTexture)
{
ScreenShadowMaskTexture = GWhiteTexture->TextureRHI;
}
SetTextureParameter(
RHICmdList,
ShaderRHI,
LightAttenuationTexture,
LightAttenuationTextureSampler,
TStaticSamplerState<SF_Point,AM_Wrap,AM_Wrap,AM_Wrap>::GetRHI(),
ScreenShadowMaskTexture);
}
SetTextureParameter(
RHICmdList,
ShaderRHI,
LTCMatTexture,
LTCMatSampler,
TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
GSystemTextures.LTCMat->GetShaderResourceRHI()
);
SetTextureParameter(
RHICmdList,
ShaderRHI,
LTCAmpTexture,
LTCAmpSampler,
TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
GSystemTextures.LTCAmp->GetShaderResourceRHI()
);
{
FRHITexture* TextureRHI = IESTextureResource ? IESTextureResource->TextureRHI : GWhiteTexture->TextureRHI;
SetTextureParameter(
RHICmdList,
ShaderRHI,
IESTexture,
IESTextureSampler,
TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
TextureRHI
);
}
if( LightingChannelsTexture.IsBound() )
{
SetTextureParameter(
RHICmdList,
ShaderRHI,
LightingChannelsTexture,
LightingChannelsSampler,
TStaticSamplerState<SF_Point,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
LightingChannelsTextureRHI ? LightingChannelsTextureRHI : GWhiteTexture->TextureRHI.GetReference()
);
}
if( TransmissionProfilesTexture.IsBound() )
{
FRHITexture* SubsurfaceTextureRHI = GBlackTexture->TextureRHI;
if (auto* SubsurfaceRT = GetSubsufaceProfileTexture_RT((FRHICommandListImmediate&)RHICmdList))
{
// no subsurface profile was used yet
SubsurfaceTextureRHI = SubsurfaceRT->GetShaderResourceRHI();
}
SetTextureParameter(RHICmdList,
ShaderRHI,
TransmissionProfilesTexture,
TransmissionProfilesLinearSampler,
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
SubsurfaceTextureRHI);
}
if (HairTransmittanceBuffer.IsBound())
{
const uint32 TransmittanceBufferMaxCount = RenderLightParams ? RenderLightParams->DeepShadow_TransmittanceMaskBufferMaxCount : 0;
SetShaderValue(
RHICmdList,
ShaderRHI,
HairTransmittanceBufferMaxCount,
TransmittanceBufferMaxCount);
if (RenderLightParams && RenderLightParams->DeepShadow_TransmittanceMaskBuffer)
{
SetSRVParameter(RHICmdList, ShaderRHI, HairTransmittanceBuffer, RenderLightParams->DeepShadow_TransmittanceMaskBuffer);
}
}
if (ScreenShadowMaskSubPixelTexture.IsBound())
{
if (RenderLightParams)
{
SetTextureParameter(
RHICmdList,
ShaderRHI,
ScreenShadowMaskSubPixelTexture,
LightAttenuationTextureSampler,
TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
(RenderLightParams && RenderLightParams->ScreenShadowMaskSubPixelTexture) ? (FRHITexture2D*)RenderLightParams->ScreenShadowMaskSubPixelTexture : GWhiteTexture->TextureRHI);
uint32 InHairShadowMaskValid = RenderLightParams->ScreenShadowMaskSubPixelTexture ? 1 : 0;
SetShaderValue(
RHICmdList,
ShaderRHI,
HairShadowMaskValid,
InHairShadowMaskValid);
}
}
if (DummyRectLightTextureForCapsuleCompilerWarning.IsBound())
{
SetTextureParameter(
RHICmdList,
ShaderRHI,
DummyRectLightTextureForCapsuleCompilerWarning,
LTCMatSampler,
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
GSystemTextures.DepthDummy->GetShaderResourceRHI()
);
}
if (CloudShadowmapTexture.IsBound())
{
if (RenderLightParams && RenderLightParams->Cloud_ShadowmapTexture)
{
SetTextureParameter(
RHICmdList,
ShaderRHI,
CloudShadowmapTexture,
CloudShadowmapSampler,
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
RenderLightParams->Cloud_ShadowmapTexture ? (FRHITexture2D*)RenderLightParams->Cloud_ShadowmapTexture : GBlackVolumeTexture->TextureRHI);
SetShaderValue(
RHICmdList,
ShaderRHI,
CloudShadowmapFarDepthKm,
RenderLightParams->Cloud_ShadowmapFarDepthKm);
SetShaderValue(
RHICmdList,
ShaderRHI,
CloudShadowmapWorldToLightClipMatrix,
(FMatrix44f)RenderLightParams->Cloud_WorldToLightClipShadowMatrix);
SetShaderValue(
RHICmdList,
ShaderRHI,
CloudShadowmapStrength,
RenderLightParams->Cloud_ShadowmapStrength);
}
}
}
LAYOUT_FIELD(FShaderResourceParameter, LightAttenuationTexture);
LAYOUT_FIELD(FShaderResourceParameter, LightAttenuationTextureSampler);
LAYOUT_FIELD(FShaderResourceParameter, LTCMatTexture);
LAYOUT_FIELD(FShaderResourceParameter, LTCMatSampler);
LAYOUT_FIELD(FShaderResourceParameter, LTCAmpTexture);
LAYOUT_FIELD(FShaderResourceParameter, LTCAmpSampler);
LAYOUT_FIELD(FShaderResourceParameter, IESTexture);
LAYOUT_FIELD(FShaderResourceParameter, IESTextureSampler);
LAYOUT_FIELD(FShaderResourceParameter, LightingChannelsTexture);
LAYOUT_FIELD(FShaderResourceParameter, LightingChannelsSampler);
LAYOUT_FIELD(FShaderResourceParameter, TransmissionProfilesTexture);
LAYOUT_FIELD(FShaderResourceParameter, TransmissionProfilesLinearSampler);
LAYOUT_FIELD(FShaderParameter, HairTransmittanceBufferMaxCount);
LAYOUT_FIELD(FShaderResourceParameter, HairTransmittanceBuffer);
LAYOUT_FIELD(FShaderUniformBufferParameter, HairStrandsParameters);
LAYOUT_FIELD(FShaderResourceParameter, ScreenShadowMaskSubPixelTexture);
LAYOUT_FIELD(FShaderParameter, HairShadowMaskValid);
LAYOUT_FIELD(FShaderResourceParameter, DummyRectLightTextureForCapsuleCompilerWarning);
LAYOUT_FIELD(FShaderResourceParameter, CloudShadowmapTexture);
LAYOUT_FIELD(FShaderResourceParameter, CloudShadowmapSampler);
LAYOUT_FIELD(FShaderParameter, CloudShadowmapFarDepthKm);
LAYOUT_FIELD(FShaderParameter, CloudShadowmapWorldToLightClipMatrix);
LAYOUT_FIELD(FShaderParameter, CloudShadowmapStrength);
};
IMPLEMENT_GLOBAL_SHADER(FDeferredLightPS, "/Engine/Private/DeferredLightPixelShaders.usf", "DeferredLightPixelMain", SF_Pixel);
/** Shader used to visualize stationary light overlap. */
template<bool bRadialAttenuation>
class TDeferredLightOverlapPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(TDeferredLightOverlapPS,Global)
public:
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("RADIAL_ATTENUATION"), (uint32)bRadialAttenuation);
}
TDeferredLightOverlapPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
HasValidChannel.Bind(Initializer.ParameterMap, TEXT("HasValidChannel"));
}
TDeferredLightOverlapPS()
{
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, const FLightSceneInfo* LightSceneInfo)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI,View.ViewUniformBuffer);
const float HasValidChannelValue = LightSceneInfo->Proxy->GetPreviewShadowMapChannel() == INDEX_NONE ? 0.0f : 1.0f;
SetShaderValue(RHICmdList, ShaderRHI, HasValidChannel, HasValidChannelValue);
SetDeferredLightParameters(RHICmdList, ShaderRHI, GetUniformBufferParameter<FDeferredLightUniformStruct>(), LightSceneInfo, View);
}
private:
LAYOUT_FIELD(FShaderParameter, HasValidChannel);
};
IMPLEMENT_SHADER_TYPE(template<>, TDeferredLightOverlapPS<true>, TEXT("/Engine/Private/StationaryLightOverlapShaders.usf"), TEXT("OverlapRadialPixelMain"), SF_Pixel);
IMPLEMENT_SHADER_TYPE(template<>, TDeferredLightOverlapPS<false>, TEXT("/Engine/Private/StationaryLightOverlapShaders.usf"), TEXT("OverlapDirectionalPixelMain"), SF_Pixel);
static void SplitSimpleLightsByView(TArrayView<const FViewInfo> Views, const FSimpleLightArray& SimpleLights, TArrayView<FSimpleLightArray> SimpleLightsByView)
{
check(SimpleLightsByView.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());
SimpleLightsByView[ViewIndex].InstanceData.Add(SimpleLights.InstanceData[LightIndex]);
SimpleLightsByView[ViewIndex].PerViewData.Add(PerViewEntry);
}
}
}
/** Gathers simple lights from visible primtives in the passed in views. */
void FSceneRenderer::GatherSimpleLights(const FSceneViewFamily& ViewFamily, const TArray<FViewInfo>& Views, FSimpleLightArray& SimpleLights)
{
TArray<const FPrimitiveSceneInfo*, SceneRenderingAllocator> PrimitivesWithSimpleLights;
// Gather visible primitives from all views that might have simple lights
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
for (int32 PrimitiveIndex = 0; PrimitiveIndex < View.VisibleDynamicPrimitivesWithSimpleLights.Num(); PrimitiveIndex++)
{
const FPrimitiveSceneInfo* PrimitiveSceneInfo = View.VisibleDynamicPrimitivesWithSimpleLights[PrimitiveIndex];
// TArray::AddUnique is slow, but not expecting many entries in PrimitivesWithSimpleLights
PrimitivesWithSimpleLights.AddUnique(PrimitiveSceneInfo);
}
}
// Gather simple lights from the primitives
for (int32 PrimitiveIndex = 0; PrimitiveIndex < PrimitivesWithSimpleLights.Num(); PrimitiveIndex++)
{
const FPrimitiveSceneInfo* Primitive = PrimitivesWithSimpleLights[PrimitiveIndex];
Primitive->Proxy->GatherSimpleLights(ViewFamily, SimpleLights);
}
}
/** Gets a readable light name for use with a draw event. */
void FSceneRenderer::GetLightNameForDrawEvent(const FLightSceneProxy* LightProxy, FString& LightNameWithLevel)
{
#if WANTS_DRAW_MESH_EVENTS
if (GetEmitDrawEvents())
{
FString FullLevelName = LightProxy->GetLevelName().ToString();
const int32 LastSlashIndex = FullLevelName.Find(TEXT("/"), ESearchCase::CaseSensitive, ESearchDir::FromEnd);
if (LastSlashIndex != INDEX_NONE)
{
// Trim the leading path before the level name to make it more readable
// The level FName was taken directly from the Outermost UObject, otherwise we would do this operation on the game thread
FullLevelName.MidInline(LastSlashIndex + 1, FullLevelName.Len() - (LastSlashIndex + 1), false);
}
LightNameWithLevel = FullLevelName + TEXT(".") + LightProxy->GetComponentName().ToString();
}
#endif
}
extern int32 GbEnableAsyncComputeTranslucencyLightingVolumeClear;
uint32 GetShadowQuality();
static bool LightRequiresDenosier(const FLightSceneInfo& LightSceneInfo)
{
ELightComponentType LightType = ELightComponentType(LightSceneInfo.Proxy->GetLightType());
if (LightType == LightType_Directional)
{
return LightSceneInfo.Proxy->GetLightSourceAngle() > 0;
}
else if (LightType == LightType_Point || LightType == LightType_Spot)
{
return LightSceneInfo.Proxy->GetSourceRadius() > 0;
}
else if (LightType == LightType_Rect)
{
return true;
}
else
{
check(0);
}
return false;
}
void FSceneRenderer::GatherAndSortLights(FSortedLightSetSceneInfo& OutSortedLights, bool bShadowedLightsInClustered)
{
if (bAllowSimpleLights)
{
GatherSimpleLights(ViewFamily, Views, OutSortedLights.SimpleLights);
}
FSimpleLightArray &SimpleLights = OutSortedLights.SimpleLights;
TArray<FSortedLightSceneInfo, SceneRenderingAllocator> &SortedLights = OutSortedLights.SortedLights;
// NOTE: we allocate space also for simple lights such that they can be referenced in the same sorted range
SortedLights.Empty(Scene->Lights.Num() + SimpleLights.InstanceData.Num());
bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows && GetShadowQuality() > 0;
#if ENABLE_DEBUG_DISCARD_PROP
int Total = Scene->Lights.Num() + SimpleLights.InstanceData.Num();
int NumToKeep = int(float(Total) * (1.0f - GDebugLightDiscardProp));
const float DebugDiscardStride = float(NumToKeep) / float(Total);
float DebugDiscardCounter = 0.0f;
#endif // ENABLE_DEBUG_DISCARD_PROP
// Build a list of visible lights.
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
const FLightSceneInfo* const LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
#if ENABLE_DEBUG_DISCARD_PROP
{
int PrevCounter = int(DebugDiscardCounter);
DebugDiscardCounter += DebugDiscardStride;
if (PrevCounter >= int(DebugDiscardCounter))
{
continue;
}
}
#endif // ENABLE_DEBUG_DISCARD_PROP
if (LightSceneInfo->ShouldRenderLightViewIndependent()
// Reflection override skips direct specular because it tends to be blindingly bright with a perfectly smooth surface
&& !ViewFamily.EngineShowFlags.ReflectionOverride)
{
// Check if the light is visible in any of the views.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (LightSceneInfo->ShouldRenderLight(Views[ViewIndex]))
{
FSortedLightSceneInfo* SortedLightInfo = new(SortedLights) FSortedLightSceneInfo(LightSceneInfo);
// Check for shadows and light functions.
SortedLightInfo->SortKey.Fields.LightType = LightSceneInfoCompact.LightType;
SortedLightInfo->SortKey.Fields.bTextureProfile = ViewFamily.EngineShowFlags.TexturedLightProfiles && LightSceneInfo->Proxy->GetIESTextureResource();
SortedLightInfo->SortKey.Fields.bShadowed = bDynamicShadows && CheckForProjectedShadows(LightSceneInfo);
SortedLightInfo->SortKey.Fields.bLightFunction = ViewFamily.EngineShowFlags.LightFunctions && CheckForLightFunction(LightSceneInfo);
SortedLightInfo->SortKey.Fields.bUsesLightingChannels = Views[ViewIndex].bUsesLightingChannels && LightSceneInfo->Proxy->GetLightingChannelMask() != GetDefaultLightingChannelMask();
// These are not simple lights.
SortedLightInfo->SortKey.Fields.bIsNotSimpleLight = 1;
// tiled and clustered deferred lighting only supported for certain lights that don't use any additional features
// And also that are not directional (mostly because it does'nt make so much sense to insert them into every grid cell in the universe)
// In the forward case one directional light gets put into its own variables, and in the deferred case it gets a full-screen pass.
// Usually it'll have shadows and stuff anyway.
// Rect lights are not supported as the performance impact is significant even if not used, for now, left for trad. deferred.
const bool bTiledOrClusteredDeferredSupported =
!SortedLightInfo->SortKey.Fields.bTextureProfile &&
(!SortedLightInfo->SortKey.Fields.bShadowed || bShadowedLightsInClustered) &&
!SortedLightInfo->SortKey.Fields.bLightFunction &&
!SortedLightInfo->SortKey.Fields.bUsesLightingChannels
&& LightSceneInfoCompact.LightType != LightType_Directional
&& LightSceneInfoCompact.LightType != LightType_Rect;
SortedLightInfo->SortKey.Fields.bTiledDeferredNotSupported = !(bTiledOrClusteredDeferredSupported && LightSceneInfo->Proxy->IsTiledDeferredLightingSupported());
SortedLightInfo->SortKey.Fields.bClusteredDeferredNotSupported = !bTiledOrClusteredDeferredSupported;
break;
}
}
}
}
// Add the simple lights also
for (int32 SimpleLightIndex = 0; SimpleLightIndex < SimpleLights.InstanceData.Num(); SimpleLightIndex++)
{
#if ENABLE_DEBUG_DISCARD_PROP
{
int PrevCounter = int(DebugDiscardCounter);
DebugDiscardCounter += DebugDiscardStride;
if (PrevCounter >= int(DebugDiscardCounter))
{
continue;
}
}
#endif // ENABLE_DEBUG_DISCARD_PROP
FSortedLightSceneInfo* SortedLightInfo = new(SortedLights) FSortedLightSceneInfo(SimpleLightIndex);
SortedLightInfo->SortKey.Fields.LightType = LightType_Point;
SortedLightInfo->SortKey.Fields.bTextureProfile = 0;
SortedLightInfo->SortKey.Fields.bShadowed = 0;
SortedLightInfo->SortKey.Fields.bLightFunction = 0;
SortedLightInfo->SortKey.Fields.bUsesLightingChannels = 0;
// These are simple lights.
SortedLightInfo->SortKey.Fields.bIsNotSimpleLight = 0;
// Simple lights are ok to use with tiled and clustered deferred lighting
SortedLightInfo->SortKey.Fields.bTiledDeferredNotSupported = 0;
SortedLightInfo->SortKey.Fields.bClusteredDeferredNotSupported = 0;
}
// Sort non-shadowed, non-light function lights first to avoid render target switches.
struct FCompareFSortedLightSceneInfo
{
FORCEINLINE bool operator()( const FSortedLightSceneInfo& A, const FSortedLightSceneInfo& B ) const
{
return A.SortKey.Packed < B.SortKey.Packed;
}
};
SortedLights.Sort( FCompareFSortedLightSceneInfo() );
// Scan and find ranges.
OutSortedLights.SimpleLightsEnd = SortedLights.Num();
OutSortedLights.TiledSupportedEnd = SortedLights.Num();
OutSortedLights.ClusteredSupportedEnd = SortedLights.Num();
OutSortedLights.AttenuationLightStart = SortedLights.Num();
// Iterate over all lights to be rendered and build ranges for tiled deferred and unshadowed lights
for (int32 LightIndex = 0; LightIndex < SortedLights.Num(); LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const bool bDrawShadows = SortedLightInfo.SortKey.Fields.bShadowed;
const bool bDrawLightFunction = SortedLightInfo.SortKey.Fields.bLightFunction;
const bool bTextureLightProfile = SortedLightInfo.SortKey.Fields.bTextureProfile;
const bool bLightingChannels = SortedLightInfo.SortKey.Fields.bUsesLightingChannels;
if (SortedLightInfo.SortKey.Fields.bIsNotSimpleLight && OutSortedLights.SimpleLightsEnd == SortedLights.Num())
{
// Mark the first index to not be simple
OutSortedLights.SimpleLightsEnd = LightIndex;
}
if (SortedLightInfo.SortKey.Fields.bTiledDeferredNotSupported && OutSortedLights.TiledSupportedEnd == SortedLights.Num())
{
// Mark the first index to not support tiled deferred
OutSortedLights.TiledSupportedEnd = LightIndex;
}
if (SortedLightInfo.SortKey.Fields.bClusteredDeferredNotSupported && OutSortedLights.ClusteredSupportedEnd == SortedLights.Num())
{
// Mark the first index to not support clustered deferred
OutSortedLights.ClusteredSupportedEnd = LightIndex;
}
if( (bDrawShadows || bDrawLightFunction || bLightingChannels) && SortedLightInfo.SortKey.Fields.bClusteredDeferredNotSupported )
{
// Once we find a shadowed light, we can exit the loop, these lights should never support tiled deferred rendering either
check(SortedLightInfo.SortKey.Fields.bTiledDeferredNotSupported);
OutSortedLights.AttenuationLightStart = LightIndex;
break;
}
}
// Make sure no obvious things went wrong!
check(OutSortedLights.TiledSupportedEnd >= OutSortedLights.SimpleLightsEnd);
check(OutSortedLights.ClusteredSupportedEnd >= OutSortedLights.TiledSupportedEnd);
check(OutSortedLights.AttenuationLightStart >= OutSortedLights.ClusteredSupportedEnd);
}
/** Shader parameters to use when creating a RenderLight(...) pass. */
BEGIN_SHADER_PARAMETER_STRUCT(FRenderLightParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FStrataGlobalUniformParameters, Strata)
SHADER_PARAMETER_STRUCT_INCLUDE(FVolumetricCloudShadowAOParameters, CloudShadowAO)
RDG_TEXTURE_ACCESS(ShadowMaskTexture, ERHIAccess::SRVGraphics)
RDG_TEXTURE_ACCESS(LightingChannelsTexture, ERHIAccess::SRVGraphics)
// We reference all the Strata tiled resources we might need in this pass
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, TileListBufferSimple)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, TileListBufferComplex)
RDG_BUFFER_ACCESS(TileIndirectBufferSimple, ERHIAccess::IndirectArgs)
RDG_BUFFER_ACCESS(TileIndirectBufferComplex, ERHIAccess::IndirectArgs)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
void GetRenderLightParameters(
const FViewInfo& View,
FRDGTextureRef SceneColorTexture,
FRDGTextureRef SceneDepthTexture,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
TRDGUniformBufferRef<FHairStrandsViewUniformParameters> HairStrandsUniformBuffer,
FRDGTextureRef ShadowMaskTexture,
FRDGTextureRef LightingChannelsTexture,
const FVolumetricCloudShadowAOParameters& CloudShadowAOParameters,
FRenderLightParameters& Parameters)
{
Parameters.SceneTextures = SceneTexturesUniformBuffer;
Parameters.HairStrands = HairStrandsUniformBuffer;
Parameters.Strata = Strata::BindStrataGlobalUniformParameters(View.StrataSceneData);
Parameters.ShadowMaskTexture = ShadowMaskTexture;
Parameters.LightingChannelsTexture = LightingChannelsTexture;
Parameters.CloudShadowAO = CloudShadowAOParameters;
Parameters.RenderTargets[0] = FRenderTargetBinding(SceneColorTexture, ERenderTargetLoadAction::ELoad);
if (SceneDepthTexture)
{
Parameters.RenderTargets.DepthStencil = FDepthStencilBinding(SceneDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite);
}
if (Strata::ShouldPassesReadingStrataBeTiled(View.Family->GetFeatureLevel()))
{
Parameters.TileListBufferSimple = View.StrataSceneData->ClassificationTileListBufferSRV[EStrataTileMaterialType::ESimple];
Parameters.TileListBufferComplex = View.StrataSceneData->ClassificationTileListBufferSRV[EStrataTileMaterialType::EComplex];
Parameters.TileIndirectBufferSimple = View.StrataSceneData->ClassificationTileIndirectBuffer[EStrataTileMaterialType::ESimple];
Parameters.TileIndirectBufferComplex = View.StrataSceneData->ClassificationTileIndirectBuffer[EStrataTileMaterialType::EComplex];
}
}
FHairStrandsTransmittanceMaskData CreateDummyHairStrandsTransmittanceMaskData(FRDGBuilder& GraphBuilder, FGlobalShaderMap* ShaderMap);
void GetRenderLightParameters(
const FViewInfo& View,
const FMinimalSceneTextures& SceneTextures,
const FHairStrandsViewData& HairViewData,
FRDGTextureRef ShadowMaskTexture,
FRDGTextureRef LightingChannelsTexture,
const FVolumetricCloudShadowAOParameters& CloudShadowAOParameters,
FRenderLightParameters& Parameters)
{
GetRenderLightParameters(View, SceneTextures.Color.Target, SceneTextures.Depth.Target, SceneTextures.UniformBuffer, HairViewData.UniformBuffer, ShadowMaskTexture, LightingChannelsTexture, CloudShadowAOParameters, Parameters);
}
void FDeferredShadingSceneRenderer::RenderLights(
FRDGBuilder& GraphBuilder,
FMinimalSceneTextures& SceneTextures,
const FTranslucencyLightingVolumeTextures& TranslucencyLightingVolumeTextures,
FRDGTextureRef LightingChannelsTexture,
FSortedLightSetSceneInfo& SortedLightSet)
{
const bool bUseHairLighting = HairStrands::HasViewHairStrandsData(Views);
RDG_EVENT_SCOPE(GraphBuilder, "Lights");
RDG_GPU_STAT_SCOPE(GraphBuilder, Lights);
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderLights, FColor::Emerald);
SCOPE_CYCLE_COUNTER(STAT_LightingDrawTime);
SCOPE_CYCLE_COUNTER(STAT_LightRendering);
const FSimpleLightArray &SimpleLights = SortedLightSet.SimpleLights;
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator> &SortedLights = SortedLightSet.SortedLights;
const int32 AttenuationLightStart = SortedLightSet.AttenuationLightStart;
const int32 SimpleLightsEnd = SortedLightSet.SimpleLightsEnd;
FHairStrandsTransmittanceMaskData DummyTransmittanceMaskData;
if (bUseHairLighting && Views.Num() > 0)
{
DummyTransmittanceMaskData = CreateDummyHairStrandsTransmittanceMaskData(GraphBuilder, Views[0].ShaderMap);
}
{
RDG_EVENT_SCOPE(GraphBuilder, "DirectLighting");
// STRATA_TODO move right after stencil clear so that it is also common with EnvLight pass
if (ViewFamily.EngineShowFlags.DirectLighting &&
Strata::IsStrataEnabled() && Strata::IsClassificationEnabled())
{
// Update the stencil buffer, marking simple/complex strata material only once for all the following passes.
Strata::AddStrataStencilPass(GraphBuilder, Views, SceneTextures);
}
if(ViewFamily.EngineShowFlags.DirectLighting)
{
RDG_EVENT_SCOPE(GraphBuilder, "NonShadowedLights");
INC_DWORD_STAT_BY(STAT_NumUnshadowedLights, AttenuationLightStart);
// Currently they have a special path anyway in case of standard deferred so always skip the simple lights
int32 StandardDeferredStart = SortedLightSet.SimpleLightsEnd;
bool bRenderSimpleLightsStandardDeferred = SortedLightSet.SimpleLights.InstanceData.Num() > 0;
UE_CLOG(ShouldUseClusteredDeferredShading() && !AreLightsInLightGrid(), LogRenderer, Warning,
TEXT("Clustered deferred shading is enabled, but lights were not injected in grid, falling back to other methods (hint 'r.LightCulling.Quality' may cause this)."));
// True if the clustered shading is enabled and the feature level is there, and that the light grid had lights injected.
if (ShouldUseClusteredDeferredShading() && AreLightsInLightGrid())
{
FRDGTextureRef ShadowMaskBits = nullptr;
FRDGTextureRef HairStrandsShadowMaskBits = nullptr;
if( VirtualShadowMapArray.IsAllocated() && CVarVirtualShadowOnePassProjection.GetValueOnRenderThread() )
{
// TODO: This needs to move into the view loop in clustered deferred shading pass
for (const FViewInfo& View : Views)
{
ShadowMaskBits = RenderVirtualShadowMapProjectionOnePass(
GraphBuilder,
SceneTextures,
View,
VirtualShadowMapArray,
EVirtualShadowMapProjectionInputType::GBuffer);
if (HairStrands::HasViewHairStrandsData(View))
{
HairStrandsShadowMaskBits = RenderVirtualShadowMapProjectionOnePass(
GraphBuilder,
SceneTextures,
View,
VirtualShadowMapArray,
EVirtualShadowMapProjectionInputType::HairStrands);
}
}
}
else
{
ShadowMaskBits = GraphBuilder.RegisterExternalTexture( GSystemTextures.ZeroUIntDummy );
}
// Tell the trad. deferred that the clustered deferred capable lights are taken care of.
// This includes the simple lights
StandardDeferredStart = SortedLightSet.ClusteredSupportedEnd;
// Tell the trad. deferred that the simple lights are spoken for.
bRenderSimpleLightsStandardDeferred = false;
AddClusteredDeferredShadingPass(GraphBuilder, SceneTextures, SortedLightSet, ShadowMaskBits, HairStrandsShadowMaskBits);
}
else if (CanUseTiledDeferred())
{
bool bAnyViewIsStereo = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
if (IStereoRendering::IsStereoEyeView(Views[ViewIndex]))
{
bAnyViewIsStereo = true;
break;
}
}
// Use tiled deferred shading on any unshadowed lights without a texture light profile
if (ShouldUseTiledDeferred(SortedLightSet.TiledSupportedEnd) && !bAnyViewIsStereo)
{
// Update the range that needs to be processed by standard deferred to exclude the lights done with tiled
StandardDeferredStart = SortedLightSet.TiledSupportedEnd;
bRenderSimpleLightsStandardDeferred = false;
RenderTiledDeferredLighting(GraphBuilder, SceneTextures, SortedLights, SortedLightSet.SimpleLightsEnd, SortedLightSet.TiledSupportedEnd, SimpleLights);
}
}
if (bRenderSimpleLightsStandardDeferred)
{
RenderSimpleLightsStandardDeferred(GraphBuilder, SceneTextures, SortedLightSet.SimpleLights);
}
{
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
{
const FViewInfo& View = Views[ViewIndex];
FRenderLightParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightParameters>();
GetRenderLightParameters(View, SceneTextures, View.HairStrandsViewData, nullptr, LightingChannelsTexture, {}, *PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("StandardDeferredLighting"),
PassParameters,
ERDGPassFlags::Raster,
[this, &View, &SortedLights, LightingChannelsTexture, StandardDeferredStart, AttenuationLightStart, PassParameters](FRHICommandList& RHICmdList)
{
// Draw non-shadowed non-light function lights without changing render targets between them
for (int32 LightIndex = StandardDeferredStart; LightIndex < AttenuationLightStart; LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const FLightSceneInfo* const LightSceneInfo = SortedLightInfo.LightSceneInfo;
SCOPED_GPU_MASK(RHICmdList, View.GPUMask);
// Render the light to the scene color buffer, using a 1x1 white texture as input
RenderLight(RHICmdList, View, LightSceneInfo, PassParameters, nullptr, TryGetRHI(LightingChannelsTexture), false, false);
}
});
}
}
// Add a special version when hair rendering is enabled for getting lighting on hair.
if (bUseHairLighting)
{
FRDGTextureRef NullScreenShadowMaskSubPixelTexture = nullptr;
for (FViewInfo& View : Views)
{
if (HairStrands::HasViewHairStrandsData(View))
{
// Draw non-shadowed non-light function lights without changing render targets between them
for (int32 LightIndex = StandardDeferredStart; LightIndex < AttenuationLightStart; LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const FLightSceneInfo* const LightSceneInfo = SortedLightInfo.LightSceneInfo;
RenderLightForHair(GraphBuilder, View, SceneTextures.UniformBuffer, LightSceneInfo, NullScreenShadowMaskSubPixelTexture, LightingChannelsTexture, DummyTransmittanceMaskData);
}
}
}
}
if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering)
{
if (AttenuationLightStart)
{
// Inject non-shadowed, non-simple, non-light function lights in to the volume.
InjectTranslucencyLightingVolumeArray(GraphBuilder, Views, Scene, *this, TranslucencyLightingVolumeTextures, VisibleLightInfos, SortedLights, TInterval<int32>(SimpleLightsEnd, AttenuationLightStart));
}
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)
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
RDG_EVENT_SCOPE(GraphBuilder, "InjectSimpleLightsTranslucentLighting");
InjectSimpleTranslucencyLightingVolumeArray(GraphBuilder, View, ViewIndex, Views.Num(), TranslucencyLightingVolumeTextures, SimpleLightArray);
}
}
}
}
}
{
RDG_EVENT_SCOPE(GraphBuilder, "ShadowedLights");
const int32 DenoiserMode = CVarShadowUseDenoiser.GetValueOnRenderThread();
const IScreenSpaceDenoiser* DefaultDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
const IScreenSpaceDenoiser* DenoiserToUse = DenoiserMode == 1 ? DefaultDenoiser : GScreenSpaceDenoiser;
TArray<FRDGTextureRef, SceneRenderingAllocator> PreprocessedShadowMaskTextures;
TArray<FRDGTextureRef, SceneRenderingAllocator> PreprocessedShadowMaskSubPixelTextures;
const int32 MaxDenoisingBatchSize = FMath::Clamp(CVarMaxShadowDenoisingBatchSize.GetValueOnRenderThread(), 1, IScreenSpaceDenoiser::kMaxBatchSize);
const int32 MaxRTShadowBatchSize = CVarMaxShadowRayTracingBatchSize.GetValueOnRenderThread();
const bool bDoShadowDenoisingBatching = DenoiserMode != 0 && MaxDenoisingBatchSize > 1;
//#dxr_todo: support multiview for the batching case
const bool bDoShadowBatching = (bDoShadowDenoisingBatching || MaxRTShadowBatchSize > 1) && Views.Num() == 1;
// Optimisations: batches all shadow ray tracing denoising. Definitely could be smarter to avoid high VGPR pressure if this entire
// function was converted to render graph, and want least intrusive change as possible. So right not it trades render target memory pressure
// for denoising perf.
if (RHI_RAYTRACING && bDoShadowBatching)
{
const uint32 ViewIndex = 0;
FViewInfo& View = Views[ViewIndex];
// Allocate PreprocessedShadowMaskTextures once so QueueTextureExtraction can deferred write.
{
if (!View.bStatePrevViewInfoIsReadOnly)
{
View.ViewState->PrevFrameViewInfo.ShadowHistories.Empty();
View.ViewState->PrevFrameViewInfo.ShadowHistories.Reserve(SortedLights.Num());
}
PreprocessedShadowMaskTextures.SetNum(SortedLights.Num());
}
PreprocessedShadowMaskTextures.SetNum(SortedLights.Num());
if (HairStrands::HasViewHairStrandsData(View))
{
PreprocessedShadowMaskSubPixelTextures.SetNum(SortedLights.Num());
}
} // if (RHI_RAYTRACING)
const bool bDirectLighting = ViewFamily.EngineShowFlags.DirectLighting;
FRDGTextureRef SharedScreenShadowMaskTexture = nullptr;
FRDGTextureRef SharedScreenShadowMaskSubPixelTexture = nullptr;
// Draw shadowed and light function lights
for (int32 LightIndex = AttenuationLightStart; LightIndex < SortedLights.Num(); LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const FLightSceneInfo& LightSceneInfo = *SortedLightInfo.LightSceneInfo;
const FLightSceneProxy& LightSceneProxy = *LightSceneInfo.Proxy;
// Note: Skip shadow mask generation for rect light if direct illumination is computed
// stochastically (rather than analytically + shadow mask)
const bool bDrawShadows = SortedLightInfo.SortKey.Fields.bShadowed;
const bool bDrawLightFunction = SortedLightInfo.SortKey.Fields.bLightFunction;
const bool bDrawPreviewIndicator = ViewFamily.EngineShowFlags.PreviewShadowsIndicator && !LightSceneInfo.IsPrecomputedLightingValid() && LightSceneProxy.HasStaticShadowing();
const bool bDrawHairShadow = bDrawShadows && bUseHairLighting;
const bool bUseHairDeepShadow = bDrawShadows && bUseHairLighting && LightSceneProxy.CastsHairStrandsDeepShadow();
bool bInjectedTranslucentVolume = false;
bool bUsedShadowMaskTexture = false;
FScopeCycleCounter Context(LightSceneProxy.GetStatId());
FRDGTextureRef ScreenShadowMaskTexture = nullptr;
FRDGTextureRef ScreenShadowMaskSubPixelTexture = nullptr;
if (bDrawShadows || bDrawLightFunction || bDrawPreviewIndicator)
{
if (!SharedScreenShadowMaskTexture)
{
const FRDGTextureDesc SharedScreenShadowMaskTextureDesc(FRDGTextureDesc::Create2D(SceneTextures.Config.Extent, PF_B8G8R8A8, FClearValueBinding::White, TexCreate_RenderTargetable | TexCreate_ShaderResource | GFastVRamConfig.ScreenSpaceShadowMask));
SharedScreenShadowMaskTexture = GraphBuilder.CreateTexture(SharedScreenShadowMaskTextureDesc, TEXT("ShadowMaskTexture"));
if (bUseHairLighting)
{
SharedScreenShadowMaskSubPixelTexture = GraphBuilder.CreateTexture(SharedScreenShadowMaskTextureDesc, TEXT("ShadowMaskSubPixelTexture"));
}
}
ScreenShadowMaskTexture = SharedScreenShadowMaskTexture;
ScreenShadowMaskSubPixelTexture = SharedScreenShadowMaskSubPixelTexture;
}
FString LightNameWithLevel;
GetLightNameForDrawEvent(&LightSceneProxy, LightNameWithLevel);
RDG_EVENT_SCOPE(GraphBuilder, "%s", *LightNameWithLevel);
if (bDrawShadows)
{
INC_DWORD_STAT(STAT_NumShadowedLights);
const FLightOcclusionType OcclusionType = GetLightOcclusionType(LightSceneProxy);
// Inline ray traced shadow batching, launches shadow batches when needed
// reduces memory overhead while keeping shadows batched to optimize costs
{
const uint32 ViewIndex = 0;
FViewInfo& View = Views[ViewIndex];
IScreenSpaceDenoiser::FShadowRayTracingConfig RayTracingConfig;
RayTracingConfig.RayCountPerPixel = GShadowRayTracingSamplesPerPixel > -1? GShadowRayTracingSamplesPerPixel : LightSceneProxy.GetSamplesPerPixel();
const bool bDenoiserCompatible = !LightRequiresDenosier(LightSceneInfo) || IScreenSpaceDenoiser::EShadowRequirements::PenumbraAndClosestOccluder == DenoiserToUse->GetShadowRequirements(View, LightSceneInfo, RayTracingConfig);
const bool bWantsBatchedShadow = OcclusionType == FLightOcclusionType::Raytraced &&
bDoShadowBatching &&
bDenoiserCompatible &&
SortedLightInfo.SortKey.Fields.bShadowed;
// determine if this light doesn't yet have a precomuted shadow and execute a batch to amortize costs if one is needed
if (
RHI_RAYTRACING &&
bWantsBatchedShadow &&
(PreprocessedShadowMaskTextures.Num() == 0 || !PreprocessedShadowMaskTextures[LightIndex - AttenuationLightStart]))
{
RDG_EVENT_SCOPE(GraphBuilder, "ShadowBatch");
TStaticArray<IScreenSpaceDenoiser::FShadowVisibilityParameters, IScreenSpaceDenoiser::kMaxBatchSize> DenoisingQueue;
TStaticArray<int32, IScreenSpaceDenoiser::kMaxBatchSize> LightIndices;
FSceneTextureParameters SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, SceneTextures.UniformBuffer);
int32 ProcessShadows = 0;
const auto QuickOffDenoisingBatch = [&]
{
int32 InputParameterCount = 0;
for (int32 i = 0; i < IScreenSpaceDenoiser::kMaxBatchSize; i++)
{
InputParameterCount += DenoisingQueue[i].LightSceneInfo != nullptr ? 1 : 0;
}
check(InputParameterCount >= 1);
TStaticArray<IScreenSpaceDenoiser::FShadowVisibilityOutputs, IScreenSpaceDenoiser::kMaxBatchSize> Outputs;
RDG_EVENT_SCOPE(GraphBuilder, "%s%s(Shadow BatchSize=%d) %dx%d",
DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""),
DenoiserToUse->GetDebugName(),
InputParameterCount,
View.ViewRect.Width(), View.ViewRect.Height());
DenoiserToUse->DenoiseShadowVisibilityMasks(
GraphBuilder,
View,
&View.PrevViewInfo,
SceneTextureParameters,
DenoisingQueue,
InputParameterCount,
Outputs);
for (int32 i = 0; i < InputParameterCount; i++)
{
const FLightSceneInfo* LocalLightSceneInfo = DenoisingQueue[i].LightSceneInfo;
int32 LocalLightIndex = LightIndices[i];
FRDGTextureRef& RefDestination = PreprocessedShadowMaskTextures[LocalLightIndex - AttenuationLightStart];
check(RefDestination == nullptr);
RefDestination = Outputs[i].Mask;
DenoisingQueue[i].LightSceneInfo = nullptr;
}
}; // QuickOffDenoisingBatch
// Ray trace shadows of light that needs, and quick off denoising batch.
for (int32 LightBatchIndex = LightIndex; LightBatchIndex < SortedLights.Num(); LightBatchIndex++)
{
const FSortedLightSceneInfo& BatchSortedLightInfo = SortedLights[LightBatchIndex];
const FLightSceneInfo& BatchLightSceneInfo = *BatchSortedLightInfo.LightSceneInfo;
// Denoiser do not support texture rect light important sampling.
const bool bBatchDrawShadows = BatchSortedLightInfo.SortKey.Fields.bShadowed;
if (!bBatchDrawShadows)
continue;
const FLightOcclusionType BatchOcclusionType = GetLightOcclusionType(*BatchLightSceneInfo.Proxy);
if (BatchOcclusionType != FLightOcclusionType::Raytraced)
continue;
const bool bRequiresDenoiser = LightRequiresDenosier(BatchLightSceneInfo) && DenoiserMode > 0;
IScreenSpaceDenoiser::FShadowRayTracingConfig BatchRayTracingConfig;
BatchRayTracingConfig.RayCountPerPixel = GShadowRayTracingSamplesPerPixel > -1 ? GShadowRayTracingSamplesPerPixel : BatchLightSceneInfo.Proxy->GetSamplesPerPixel();
IScreenSpaceDenoiser::EShadowRequirements DenoiserRequirements = bRequiresDenoiser ?
DenoiserToUse->GetShadowRequirements(View, BatchLightSceneInfo, BatchRayTracingConfig) :
IScreenSpaceDenoiser::EShadowRequirements::Bailout;
// Not worth batching and increase memory pressure if the denoiser do not support this ray tracing config.
// TODO: add suport for batch with multiple SPP.
if (bRequiresDenoiser && DenoiserRequirements != IScreenSpaceDenoiser::EShadowRequirements::PenumbraAndClosestOccluder)
{
continue;
}
// Ray trace the shadow.
//#dxr_todo: support multiview for the batching case
FRDGTextureRef RayTracingShadowMaskTexture;
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_FloatRGBA,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
RayTracingShadowMaskTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingOcclusion"));
}
FRDGTextureRef RayDistanceTexture;
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_R16F,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
RayDistanceTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingOcclusionDistance"));
}
FRDGTextureRef SubPixelRayTracingShadowMaskTexture = nullptr;
FRDGTextureUAV* SubPixelRayTracingShadowMaskUAV = nullptr;
if (bUseHairLighting)
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_FloatRGBA,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
SubPixelRayTracingShadowMaskTexture = GraphBuilder.CreateTexture(Desc, TEXT("SubPixelRayTracingOcclusion"));
SubPixelRayTracingShadowMaskUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SubPixelRayTracingShadowMaskTexture));
}
FString BatchLightNameWithLevel;
GetLightNameForDrawEvent(BatchLightSceneInfo.Proxy, BatchLightNameWithLevel);
FRDGTextureUAV* RayTracingShadowMaskUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(RayTracingShadowMaskTexture));
FRDGTextureUAV* RayHitDistanceUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(RayDistanceTexture));
{
RDG_EVENT_SCOPE(GraphBuilder, "%s", *BatchLightNameWithLevel);
// Ray trace the shadow cast by opaque geometries on to hair strands geometries
// Note: No denoiser is required on this output, as the hair strands are geometrically noisy, which make it hard to denoise
RenderRayTracingShadows(
GraphBuilder,
SceneTextureParameters,
View,
BatchLightSceneInfo,
BatchRayTracingConfig,
DenoiserRequirements,
LightingChannelsTexture,
RayTracingShadowMaskUAV,
RayHitDistanceUAV,
SubPixelRayTracingShadowMaskUAV);
if (HairStrands::HasViewHairStrandsData(View))
{
FRDGTextureRef& RefDestination = PreprocessedShadowMaskSubPixelTextures[LightBatchIndex - AttenuationLightStart];
check(RefDestination == nullptr);
RefDestination = SubPixelRayTracingShadowMaskTexture;
}
}
bool bBatchFull = false;
if (bRequiresDenoiser)
{
// Queue the ray tracing output for shadow denoising.
for (int32 i = 0; i < IScreenSpaceDenoiser::kMaxBatchSize; i++)
{
if (DenoisingQueue[i].LightSceneInfo == nullptr)
{
DenoisingQueue[i].LightSceneInfo = &BatchLightSceneInfo;
DenoisingQueue[i].RayTracingConfig = RayTracingConfig;
DenoisingQueue[i].InputTextures.Mask = RayTracingShadowMaskTexture;
DenoisingQueue[i].InputTextures.ClosestOccluder = RayDistanceTexture;
LightIndices[i] = LightBatchIndex;
// If queue for this light type is full, quick of the batch.
if ((i + 1) == MaxDenoisingBatchSize)
{
QuickOffDenoisingBatch();
bBatchFull = true;
}
break;
}
else
{
check((i - 1) < IScreenSpaceDenoiser::kMaxBatchSize);
}
}
}
else
{
PreprocessedShadowMaskTextures[LightBatchIndex - AttenuationLightStart] = RayTracingShadowMaskTexture;
}
// terminate batch if we filled a denoiser batch or hit our max light batch
ProcessShadows++;
if (bBatchFull || ProcessShadows == MaxRTShadowBatchSize)
{
break;
}
}
// Ensures all denoising queues are processed.
if (DenoisingQueue[0].LightSceneInfo)
{
QuickOffDenoisingBatch();
}
}
} // end inline batched raytraced shadow
if (RHI_RAYTRACING && PreprocessedShadowMaskTextures.Num() > 0 && PreprocessedShadowMaskTextures[LightIndex - AttenuationLightStart])
{
const uint32 ShadowMaskIndex = LightIndex - AttenuationLightStart;
ScreenShadowMaskTexture = PreprocessedShadowMaskTextures[ShadowMaskIndex];
PreprocessedShadowMaskTextures[ShadowMaskIndex] = nullptr;
// Subp-ixel shadow for hair strands geometries
if (bUseHairLighting && ShadowMaskIndex < uint32(PreprocessedShadowMaskSubPixelTextures.Num()))
{
ScreenShadowMaskSubPixelTexture = PreprocessedShadowMaskSubPixelTextures[ShadowMaskIndex];
PreprocessedShadowMaskSubPixelTextures[ShadowMaskIndex] = nullptr;
}
// Inject deep shadow mask if the light supports it
if (bUseHairDeepShadow)
{
RenderHairStrandsShadowMask(GraphBuilder, Views, &LightSceneInfo, ScreenShadowMaskTexture);
}
}
else if (OcclusionType == FLightOcclusionType::Raytraced)
{
FSceneTextureParameters SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, SceneTextures.UniformBuffer);
FRDGTextureRef RayTracingShadowMaskTexture;
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_FloatRGBA,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
RayTracingShadowMaskTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingOcclusion"));
}
FRDGTextureRef RayDistanceTexture;
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_R16F,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
RayDistanceTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingOcclusionDistance"));
}
FRDGTextureUAV* RayTracingShadowMaskUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(RayTracingShadowMaskTexture));
FRDGTextureUAV* RayHitDistanceUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(RayDistanceTexture));
FRDGTextureRef SubPixelRayTracingShadowMaskTexture = nullptr;
FRDGTextureUAV* SubPixelRayTracingShadowMaskUAV = nullptr;
if (bUseHairLighting)
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_FloatRGBA,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
SubPixelRayTracingShadowMaskTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingOcclusion"));
SubPixelRayTracingShadowMaskUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SubPixelRayTracingShadowMaskTexture));
}
FRDGTextureRef RayTracingShadowMaskTileTexture;
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_FloatRGBA,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
RayTracingShadowMaskTileTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingOcclusionTile"));
}
bool bIsMultiview = Views.Num() > 0;
for (FViewInfo& View : Views)
{
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
IScreenSpaceDenoiser::FShadowRayTracingConfig RayTracingConfig;
RayTracingConfig.RayCountPerPixel = GShadowRayTracingSamplesPerPixel > -1 ? GShadowRayTracingSamplesPerPixel : LightSceneProxy.GetSamplesPerPixel();
IScreenSpaceDenoiser::EShadowRequirements DenoiserRequirements = IScreenSpaceDenoiser::EShadowRequirements::Bailout;
if (DenoiserMode != 0 && LightRequiresDenosier(LightSceneInfo))
{
DenoiserRequirements = DenoiserToUse->GetShadowRequirements(View, LightSceneInfo, RayTracingConfig);
}
RenderRayTracingShadows(
GraphBuilder,
SceneTextureParameters,
View,
LightSceneInfo,
RayTracingConfig,
DenoiserRequirements,
LightingChannelsTexture,
RayTracingShadowMaskUAV,
RayHitDistanceUAV,
SubPixelRayTracingShadowMaskUAV);
if (DenoiserRequirements != IScreenSpaceDenoiser::EShadowRequirements::Bailout)
{
TStaticArray<IScreenSpaceDenoiser::FShadowVisibilityParameters, IScreenSpaceDenoiser::kMaxBatchSize> InputParameters;
TStaticArray<IScreenSpaceDenoiser::FShadowVisibilityOutputs, IScreenSpaceDenoiser::kMaxBatchSize> Outputs;
InputParameters[0].InputTextures.Mask = RayTracingShadowMaskTexture;
InputParameters[0].InputTextures.ClosestOccluder = RayDistanceTexture;
InputParameters[0].LightSceneInfo = &LightSceneInfo;
InputParameters[0].RayTracingConfig = RayTracingConfig;
int32 InputParameterCount = 1;
RDG_EVENT_SCOPE(GraphBuilder, "%s%s(Shadow BatchSize=%d) %dx%d",
DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""),
DenoiserToUse->GetDebugName(),
InputParameterCount,
View.ViewRect.Width(), View.ViewRect.Height());
DenoiserToUse->DenoiseShadowVisibilityMasks(
GraphBuilder,
View,
&View.PrevViewInfo,
SceneTextureParameters,
InputParameters,
InputParameterCount,
Outputs);
if (bIsMultiview)
{
AddDrawTexturePass(GraphBuilder, View, Outputs[0].Mask, RayTracingShadowMaskTileTexture, View.ViewRect.Min, View.ViewRect.Min, View.ViewRect.Size());
ScreenShadowMaskTexture = RayTracingShadowMaskTileTexture;
}
else
{
ScreenShadowMaskTexture = Outputs[0].Mask;
}
}
else
{
ScreenShadowMaskTexture = RayTracingShadowMaskTexture;
}
if (HairStrands::HasViewHairStrandsData(View))
{
ScreenShadowMaskSubPixelTexture = SubPixelRayTracingShadowMaskTexture;
}
}
// Inject deep shadow mask if the light supports it
if (bUseHairDeepShadow)
{
RenderHairStrandsShadowMask(GraphBuilder, Views, &LightSceneInfo, ScreenShadowMaskTexture);
}
}
else // (OcclusionType == FOcclusionType::Shadowmap)
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
View.HeightfieldLightingViewInfo.ClearShadowing(GraphBuilder, View, LightSceneInfo);
}
const auto ClearShadowMask = [&](FRDGTextureRef InScreenShadowMaskTexture)
{
// Clear light attenuation for local lights with a quad covering their extents
const bool bClearLightScreenExtentsOnly = CVarAllowClearLightSceneExtentsOnly.GetValueOnRenderThread() && SortedLightInfo.SortKey.Fields.LightType != LightType_Directional;
if (bClearLightScreenExtentsOnly)
{
FRenderTargetParameters* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(InScreenShadowMaskTexture, ERenderTargetLoadAction::ENoAction);
GraphBuilder.AddPass(
RDG_EVENT_NAME("ClearQuad"),
PassParameters,
ERDGPassFlags::Raster,
[this, &LightSceneProxy](FRHICommandList& RHICmdList)
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
SCOPED_GPU_MASK(RHICmdList, View.GPUMask);
FIntRect ScissorRect;
if (!LightSceneProxy.GetScissorRect(ScissorRect, View, View.ViewRect))
{
ScissorRect = View.ViewRect;
}
if (ScissorRect.Min.X < ScissorRect.Max.X && ScissorRect.Min.Y < ScissorRect.Max.Y)
{
RHICmdList.SetViewport(ScissorRect.Min.X, ScissorRect.Min.Y, 0.0f, ScissorRect.Max.X, ScissorRect.Max.Y, 1.0f);
DrawClearQuad(RHICmdList, true, FLinearColor(1, 1, 1, 1), false, 0, false, 0);
}
else
{
LightSceneProxy.GetScissorRect(ScissorRect, View, View.ViewRect);
}
}
});
}
else
{
AddClearRenderTargetPass(GraphBuilder, InScreenShadowMaskTexture);
}
};
ClearShadowMask(ScreenShadowMaskTexture);
if (ScreenShadowMaskSubPixelTexture)
{
ClearShadowMask(ScreenShadowMaskSubPixelTexture);
}
RenderDeferredShadowProjections(GraphBuilder, SceneTextures, TranslucencyLightingVolumeTextures, &LightSceneInfo, ScreenShadowMaskTexture, ScreenShadowMaskSubPixelTexture, bInjectedTranslucentVolume);
}
bUsedShadowMaskTexture = true;
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
View.HeightfieldLightingViewInfo.ComputeLighting(GraphBuilder, View, LightSceneInfo);
}
// Render light function to the attenuation buffer.
if (bDirectLighting)
{
if (bDrawLightFunction)
{
const bool bLightFunctionRendered = RenderLightFunction(GraphBuilder, SceneTextures, &LightSceneInfo, ScreenShadowMaskTexture, bDrawShadows, false, false);
bUsedShadowMaskTexture |= bLightFunctionRendered;
if (CVarAppliedLightFunctionOnHair.GetValueOnRenderThread() > 0 && bLightFunctionRendered && ScreenShadowMaskSubPixelTexture)
{
RenderLightFunction(GraphBuilder, SceneTextures, &LightSceneInfo, ScreenShadowMaskSubPixelTexture, bDrawShadows, false, true);
}
}
if (bDrawPreviewIndicator)
{
RenderPreviewShadowsIndicator(GraphBuilder, SceneTextures, &LightSceneInfo, ScreenShadowMaskTexture, bUsedShadowMaskTexture, false);
}
if (!bDrawShadows)
{
INC_DWORD_STAT(STAT_NumLightFunctionOnlyLights);
}
}
if(bDirectLighting && !bInjectedTranslucentVolume)
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
// Accumulate this light's unshadowed contribution to the translucency lighting volume
InjectTranslucencyLightingVolume(GraphBuilder, View, ViewIndex, Scene, *this, TranslucencyLightingVolumeTextures, VisibleLightInfos, LightSceneInfo, nullptr);
}
}
// If we never rendered into the mask, don't attempt to read from it.
if (!bUsedShadowMaskTexture)
{
ScreenShadowMaskTexture = nullptr;
ScreenShadowMaskSubPixelTexture = nullptr;
}
// Render the light to the scene color buffer, conditionally using the attenuation buffer or a 1x1 white texture as input
if (bDirectLighting)
{
const bool bRenderOverlap = false;
RenderLight(GraphBuilder, SceneTextures, &LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, bRenderOverlap);
}
if (bUseHairLighting)
{
for (FViewInfo& View : Views)
{
if (bDrawHairShadow && HairStrands::HasViewHairStrandsData(View))
{
FHairStrandsTransmittanceMaskData TransmittanceMaskData = RenderHairStrandsTransmittanceMask(GraphBuilder, View, &LightSceneInfo, ScreenShadowMaskSubPixelTexture);
if (TransmittanceMaskData.TransmittanceMask == nullptr)
{
TransmittanceMaskData = DummyTransmittanceMaskData;
}
// Note: ideally the light should still be evaluated for hair when not casting shadow, but for preserving the old behavior, and not adding
// any perf. regression, we disable this light for hair rendering
RenderLightForHair(GraphBuilder, View, SceneTextures.UniformBuffer, &LightSceneInfo, ScreenShadowMaskSubPixelTexture, LightingChannelsTexture, TransmittanceMaskData);
}
}
}
}
}
}
}
void FDeferredShadingSceneRenderer::RenderLightArrayForOverlapViewmode(
FRHICommandList& RHICmdList,
FRHITexture* LightingChannelsTexture,
const TSparseArray<FLightSceneInfoCompact>& LightArray)
{
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(LightArray); LightIt; ++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
const FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
// Nothing to do for black lights.
if(LightSceneInfoCompact.Color.IsAlmostBlack())
{
continue;
}
// Only render shadow casting stationary lights
if (!LightSceneInfo->Proxy->HasStaticShadowing() ||
LightSceneInfo->Proxy->HasStaticLighting() ||
!LightSceneInfo->Proxy->CastsStaticShadow())
{
continue;
}
// Check if the light is visible in any of the views.
for (const FViewInfo& View : Views)
{
SCOPED_GPU_MASK(RHICmdList, View.GPUMask);
RenderLight(RHICmdList, View, LightSceneInfo, nullptr, nullptr, LightingChannelsTexture, true, false);
}
}
}
void FDeferredShadingSceneRenderer::RenderStationaryLightOverlap(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
FRDGTextureRef LightingChannelsTexture)
{
if (Scene->bIsEditorScene)
{
FRenderLightParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightParameters>();
GetRenderLightParameters(Views[0], SceneTextures, Views[0].HairStrandsViewData, nullptr, LightingChannelsTexture, {}, *PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("StationaryLightOverlap"),
PassParameters,
ERDGPassFlags::Raster,
[this, LightingChannelsTexture](FRHICommandList& RHICmdList)
{
FRHITexture* LightingChannelsTextureRHI = TryGetRHI(LightingChannelsTexture);
// Clear to discard base pass values in scene color since we didn't skip that, to have valid scene depths
DrawClearQuad(RHICmdList, FLinearColor::Black);
RenderLightArrayForOverlapViewmode(RHICmdList, LightingChannelsTextureRHI, Scene->Lights);
//Note: making use of FScene::InvisibleLights, which contains lights that haven't been added to the scene in the same way as visible lights
// So code called by RenderLightArrayForOverlapViewmode must be careful what it accesses
RenderLightArrayForOverlapViewmode(RHICmdList, LightingChannelsTextureRHI, Scene->InvisibleLights);
});
}
}
/** Sets up rasterizer and depth state for rendering bounding geometry in a deferred pass. */
void SetBoundingGeometryRasterizerAndDepthState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, const FViewInfo& View, bool bCameraInsideLightGeometry)
{
if (bCameraInsideLightGeometry)
{
// Render backfaces with depth tests disabled since the camera is inside (or close to inside) the light geometry
GraphicsPSOInit.RasterizerState = View.bReverseCulling ? TStaticRasterizerState<FM_Solid, CM_CW>::GetRHI() : TStaticRasterizerState<FM_Solid, CM_CCW>::GetRHI();
}
else
{
// Render frontfaces with depth tests on to get the speedup from HiZ since the camera is outside the light geometry
GraphicsPSOInit.RasterizerState = View.bReverseCulling ? TStaticRasterizerState<FM_Solid, CM_CCW>::GetRHI() : TStaticRasterizerState<FM_Solid, CM_CW>::GetRHI();
}
if (Strata::IsStrataEnabled() && Strata::IsClassificationEnabled())
{
GraphicsPSOInit.DepthStencilState =
bCameraInsideLightGeometry
? TStaticDepthStencilState<
false, CF_Always,
true, CF_Equal, SO_Keep, SO_Keep, SO_Keep,
true, CF_Equal, SO_Keep, SO_Keep, SO_Keep,
Strata::StencilBit, 0x0>::GetRHI()
: TStaticDepthStencilState<
false, CF_DepthNearOrEqual,
true, CF_Equal, SO_Keep, SO_Keep, SO_Keep,
true, CF_Equal, SO_Keep, SO_Keep, SO_Keep,
Strata::StencilBit, 0x0>::GetRHI();
}
else
{
GraphicsPSOInit.DepthStencilState =
bCameraInsideLightGeometry
? TStaticDepthStencilState<false, CF_Always>::GetRHI()
: TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI();
}
}
template<bool bUseIESProfile, bool bRadialAttenuation, bool bInverseSquaredFalloff>
static void SetShaderTemplLightingSimple(
FRHICommandList& RHICmdList,
FGraphicsPipelineStateInitializer& GraphicsPSOInit,
const FViewInfo& View,
const TShaderRef<FShader>& VertexShader,
const FSimpleLightEntry& SimpleLight,
const FSimpleLightPerViewEntry& SimpleLightPerViewData)
{
FDeferredLightPS::FPermutationDomain PermutationVector;
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >( ELightSourceShape::Capsule );
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >( bUseIESProfile );
PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >( bInverseSquaredFalloff );
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >( View.Family->EngineShowFlags.VisualizeLightCulling );
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >( false );
PermutationVector.Set< FDeferredLightPS::FAnistropicMaterials >(false);
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >( false );
PermutationVector.Set< FDeferredLightPS::FHairLighting>( 0 );
PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >( false );
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(false);
PermutationVector.Set< FDeferredLightPS::FStrataFastPath>(false);
TShaderMapRef< FDeferredLightPS > PixelShader( View.ShaderMap, PermutationVector );
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetParametersSimpleLight(RHICmdList, View, SimpleLight, SimpleLightPerViewData);
}
// Use DBT to allow work culling on shadow lights
void CalculateLightNearFarDepthFromBounds(const FViewInfo& View, const FSphere &LightBounds, float &NearDepth, float &FarDepth)
{
const FMatrix ViewProjection = View.ViewMatrices.GetViewProjectionMatrix();
const FVector ViewDirection = View.GetViewDirection();
// push camera relative bounds center along view vec by its radius
const FVector FarPoint = LightBounds.Center + LightBounds.W * ViewDirection;
const FVector4 FarPoint4 = FVector4(FarPoint, 1.f);
const FVector4 FarPoint4Clip = ViewProjection.TransformFVector4(FarPoint4);
FarDepth = FarPoint4Clip.Z / FarPoint4Clip.W;
// pull camera relative bounds center along -view vec by its radius
const FVector NearPoint = LightBounds.Center - LightBounds.W * ViewDirection;
const FVector4 NearPoint4 = FVector4(NearPoint, 1.f);
const FVector4 NearPoint4Clip = ViewProjection.TransformFVector4(NearPoint4);
NearDepth = NearPoint4Clip.Z / NearPoint4Clip.W;
// negative means behind view, but we use a NearClipPlane==1.f depth
if (NearPoint4Clip.W < 0)
NearDepth = 1;
if (FarPoint4Clip.W < 0)
FarDepth = 1;
NearDepth = FMath::Clamp(NearDepth, 0.0f, 1.0f);
FarDepth = FMath::Clamp(FarDepth, 0.0f, 1.0f);
}
static void BindAtmosphereAndCloudResources(
const FScene* Scene,
const FViewInfo* View,
const FLightSceneProxy* Proxy,
FRenderLightParams& RenderLightParams,
bool& bAtmospherePerPixelTransmittance,
bool& bCloudPerPixelTransmittance)
{
bAtmospherePerPixelTransmittance =
Proxy->GetLightType() == LightType_Directional &&
Proxy->IsUsedAsAtmosphereSunLight() &&
Proxy->GetUsePerPixelAtmosphereTransmittance() &&
ShouldRenderSkyAtmosphere(Scene, View->Family->EngineShowFlags);
const FLightSceneProxy* AtmosphereLight0Proxy = Scene->AtmosphereLights[0] ? Scene->AtmosphereLights[0]->Proxy : nullptr;
const FLightSceneProxy* AtmosphereLight1Proxy = Scene->AtmosphereLights[1] ? Scene->AtmosphereLights[1]->Proxy : nullptr;
const FVolumetricCloudRenderSceneInfo* CloudInfo = Scene->GetVolumetricCloudSceneInfo();
const bool VolumetricCloudShadowMap0Valid = View->VolumetricCloudShadowExtractedRenderTarget[0] != nullptr;
const bool VolumetricCloudShadowMap1Valid = View->VolumetricCloudShadowExtractedRenderTarget[1] != nullptr;
const bool bLight0CloudPerPixelTransmittance = CloudInfo && VolumetricCloudShadowMap0Valid && AtmosphereLight0Proxy == Proxy && AtmosphereLight0Proxy && AtmosphereLight0Proxy->GetCloudShadowOnSurfaceStrength() > 0.0f;
const bool bLight1CloudPerPixelTransmittance = CloudInfo && VolumetricCloudShadowMap1Valid && AtmosphereLight1Proxy == Proxy && AtmosphereLight1Proxy && AtmosphereLight1Proxy->GetCloudShadowOnSurfaceStrength() > 0.0f;
if (bLight0CloudPerPixelTransmittance)
{
RenderLightParams.Cloud_ShadowmapTexture = View->VolumetricCloudShadowExtractedRenderTarget[0]->GetShaderResourceRHI();
RenderLightParams.Cloud_ShadowmapFarDepthKm = CloudInfo->GetVolumetricCloudCommonShaderParameters().CloudShadowmapFarDepthKm[0].X;
RenderLightParams.Cloud_WorldToLightClipShadowMatrix = CloudInfo->GetVolumetricCloudCommonShaderParameters().CloudShadowmapWorldToLightClipMatrix[0];
RenderLightParams.Cloud_ShadowmapStrength = AtmosphereLight0Proxy->GetCloudShadowOnSurfaceStrength();
}
else if (bLight1CloudPerPixelTransmittance)
{
RenderLightParams.Cloud_ShadowmapTexture = View->VolumetricCloudShadowExtractedRenderTarget[1]->GetShaderResourceRHI();
RenderLightParams.Cloud_ShadowmapFarDepthKm = CloudInfo->GetVolumetricCloudCommonShaderParameters().CloudShadowmapFarDepthKm[1].X;
RenderLightParams.Cloud_WorldToLightClipShadowMatrix = CloudInfo->GetVolumetricCloudCommonShaderParameters().CloudShadowmapWorldToLightClipMatrix[1];
RenderLightParams.Cloud_ShadowmapStrength = AtmosphereLight1Proxy->GetCloudShadowOnSurfaceStrength();
}
bCloudPerPixelTransmittance = bLight0CloudPerPixelTransmittance || bLight1CloudPerPixelTransmittance;
}
/**
* Used by RenderLights to render a light to the scene color buffer.
*
* @param LightSceneInfo Represents the current light
* @param LightIndex The light's index into FScene::Lights
* @return true if anything got rendered
*/
void FDeferredShadingSceneRenderer::RenderLight(
FRHICommandList& RHICmdList,
const FViewInfo& View,
const FLightSceneInfo* LightSceneInfo,
FRenderLightParameters* PassParameters, // If this is null, it means we cannot use Strata tiles and fallback to previous behavior.
FRHITexture* ScreenShadowMaskTexture,
FRHITexture* LightingChannelsTexture,
bool bRenderOverlap, bool bIssueDrawEvent)
{
// Ensure the light is valid for this view
if (!LightSceneInfo->ShouldRenderLight(View))
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
INC_DWORD_STAT(STAT_NumLightsUsingStandardDeferred);
SCOPED_CONDITIONAL_DRAW_EVENT(RHICmdList, StandardDeferredLighting, bIssueDrawEvent);
auto RenderInternalLight = [&]( bool bEnableStrataStencilTest, bool bEnableStrataTiledPass, uint32 StrataTileMaterialType)
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
const FLightSceneProxy* RESTRICT LightProxy = LightSceneInfo->Proxy;
const FSphere LightBounds = LightProxy->GetBoundingSphere();
const bool bTransmission = LightProxy->Transmission();
bool bUseIESTexture = false;
if(View.Family->EngineShowFlags.TexturedLightProfiles)
{
bUseIESTexture = (LightSceneInfo->Proxy->GetIESTextureResource() != 0);
}
// Set the device viewport for the view.
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
FRenderLightParams RenderLightParams;
if (bEnableStrataStencilTest)
{
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<
false, CF_Always,
true, CF_Equal, SO_Keep, SO_Keep, SO_Keep,
true, CF_Equal, SO_Keep, SO_Keep, SO_Keep,
Strata::StencilBit, 0x0>::GetRHI();
}
else
{
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
}
if (LightProxy->GetLightType() == LightType_Directional)
{
// Turn DBT back off
GraphicsPSOInit.bDepthBounds = false;
TShaderMapRef<TDeferredLightVS<false> > VertexShader(View.ShaderMap);
Strata::FStrataTilePassVS::FParameters VSParameters;
Strata::FStrataTilePassVS::FPermutationDomain VSPermutationVector;
VSPermutationVector.Set< Strata::FStrataTilePassVS::FEnableDebug >(false);
VSPermutationVector.Set< Strata::FStrataTilePassVS::FEnableTexCoordScreenVector >(true);
TShaderMapRef<Strata::FStrataTilePassVS> StrataTilePassVertexShader(View.ShaderMap, VSPermutationVector);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
if (bRenderOverlap)
{
TShaderMapRef<TDeferredLightOverlapPS<false> > PixelShader(View.ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo);
}
else
{
bool bAtmospherePerPixelTransmittance = false;
bool bCloudPerPixelTransmittance = false;
BindAtmosphereAndCloudResources(
Scene,
&View,
LightSceneInfo->Proxy,
RenderLightParams,
bAtmospherePerPixelTransmittance,
bCloudPerPixelTransmittance);
FDeferredLightPS::FPermutationDomain PermutationVector;
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >( ELightSourceShape::Directional );
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >( false );
PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >( false );
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >( View.Family->EngineShowFlags.VisualizeLightCulling );
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >( View.bUsesLightingChannels );
PermutationVector.Set< FDeferredLightPS::FAnistropicMaterials >(ShouldRenderAnisotropyPass());
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >( bTransmission );
PermutationVector.Set< FDeferredLightPS::FHairLighting>(0);
// Only directional lights are rendered in this path, so we only need to check if it is use to light the atmosphere
PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >(bAtmospherePerPixelTransmittance);
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(bCloudPerPixelTransmittance);
PermutationVector.Set< FDeferredLightPS::FStrataFastPath >(StrataTileMaterialType == EStrataTileMaterialType::ESimple);
TShaderMapRef< FDeferredLightPS > PixelShader( View.ShaderMap, PermutationVector );
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
if (bEnableStrataTiledPass)
{
Strata::FillUpTiledPassData((EStrataTileMaterialType)StrataTileMaterialType, View, VSParameters, GraphicsPSOInit.PrimitiveType);
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = StrataTilePassVertexShader.GetVertexShader();
}
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, &RenderLightParams, nullptr);
}
RHICmdList.SetStencilRef(StrataTileMaterialType == EStrataTileMaterialType::ESimple ? Strata::StencilBit : 0u);
if (!bEnableStrataTiledPass)
{
VertexShader->SetParameters(RHICmdList, View, LightSceneInfo);
// Apply the directional light as a full screen quad
DrawRectangle(
RHICmdList,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Size(),
GetSceneTextureExtent(),
VertexShader,
EDRF_UseTriangleOptimization);
}
else
{
SetShaderParameters(RHICmdList, StrataTilePassVertexShader, StrataTilePassVertexShader.GetVertexShader(), VSParameters);
RHICmdList.DrawPrimitiveIndirect(VSParameters.TileIndirectBuffer->GetIndirectRHICallBuffer(), 0);
}
}
else
{
// Use DBT to allow work culling on shadow lights
// Disable depth bound when hair rendering is enabled as this rejects partially covered pixel write (with opaque background)
GraphicsPSOInit.bDepthBounds = GSupportsDepthBoundsTest && GAllowDepthBoundsTest != 0;
TShaderMapRef<TDeferredLightVS<true> > VertexShader(View.ShaderMap);
const bool bCameraInsideLightGeometry = ((FVector)View.ViewMatrices.GetViewOrigin() - LightBounds.Center).SizeSquared() < FMath::Square(LightBounds.W * 1.05f + View.NearClippingDistance * 2.0f)
//const bool bCameraInsideLightGeometry = LightProxy->AffectsBounds( FSphere( View.ViewMatrices.GetViewOrigin(), View.NearClippingDistance * 2.0f ) )
// Always draw backfaces in ortho
//@todo - accurate ortho camera / light intersection
|| !View.IsPerspectiveProjection();
SetBoundingGeometryRasterizerAndDepthState(GraphicsPSOInit, View, bCameraInsideLightGeometry);
if (bRenderOverlap)
{
TShaderMapRef<TDeferredLightOverlapPS<true> > PixelShader(View.ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo);
}
else
{
FDeferredLightPS::FPermutationDomain PermutationVector;
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >( LightProxy->IsRectLight() ? ELightSourceShape::Rect : ELightSourceShape::Capsule );
PermutationVector.Set< FDeferredLightPS::FSourceTextureDim >( LightProxy->IsRectLight() && LightProxy->HasSourceTexture() );
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >( bUseIESTexture );
PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >( LightProxy->IsInverseSquared() );
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >( View.Family->EngineShowFlags.VisualizeLightCulling );
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >( View.bUsesLightingChannels );
PermutationVector.Set< FDeferredLightPS::FAnistropicMaterials >(ShouldRenderAnisotropyPass() && !LightSceneInfo->Proxy->IsRectLight());
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >( bTransmission );
PermutationVector.Set< FDeferredLightPS::FHairLighting>(0);
PermutationVector.Set < FDeferredLightPS::FAtmosphereTransmittance >(false);
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(false);
PermutationVector.Set< FDeferredLightPS::FStrataFastPath >(StrataTileMaterialType == EStrataTileMaterialType::ESimple);
TShaderMapRef< FDeferredLightPS > PixelShader( View.ShaderMap, PermutationVector );
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, &RenderLightParams, nullptr);
}
VertexShader->SetParameters(RHICmdList, View, LightSceneInfo);
RHICmdList.SetStencilRef(StrataTileMaterialType == EStrataTileMaterialType::ESimple ? Strata::StencilBit : 0u);
// Use DBT to allow work culling on shadow lights
if (GraphicsPSOInit.bDepthBounds)
{
// Can use the depth bounds test to skip work for pixels which won't be touched by the light (i.e outside the depth range)
float NearDepth = 1.f;
float FarDepth = 0.f;
CalculateLightNearFarDepthFromBounds(View,LightBounds,NearDepth,FarDepth);
if (NearDepth <= FarDepth)
{
NearDepth = 1.0f;
FarDepth = 0.0f;
}
// UE uses reversed depth, so far < near
RHICmdList.SetDepthBounds(FarDepth, NearDepth);
}
if( LightProxy->GetLightType() == LightType_Point ||
LightProxy->GetLightType() == LightType_Rect )
{
// Apply the point or spot light with some approximate bounding geometry,
// So we can get speedups from depth testing and not processing pixels outside of the light's influence.
StencilingGeometry::DrawSphere(RHICmdList);
}
else if (LightProxy->GetLightType() == LightType_Spot)
{
StencilingGeometry::DrawCone(RHICmdList);
}
}
};
const bool bStrataClassificationEnabled = Strata::IsStrataEnabled() && Strata::IsClassificationEnabled();
const bool bTilePassesReadingStrataEnabled = Strata::ShouldPassesReadingStrataBeTiled(Scene->GetFeatureLevel());
if (bStrataClassificationEnabled && bTilePassesReadingStrataEnabled && PassParameters != nullptr)
{
const bool bEnableStrataTiledPass = true;
const bool bEnableStrataStencilTest = false;
RenderInternalLight(bEnableStrataStencilTest, bEnableStrataTiledPass, EStrataTileMaterialType::ESimple);
RenderInternalLight(bEnableStrataStencilTest, bEnableStrataTiledPass, EStrataTileMaterialType::EComplex);
}
else
{
const bool bEnableStrataTiledPass = false;
const bool bEnableStrataStencilTest = bStrataClassificationEnabled;
RenderInternalLight(bEnableStrataStencilTest, bEnableStrataTiledPass, EStrataTileMaterialType::EComplex);
if (Strata::IsStrataEnabled() && Strata::IsClassificationEnabled())
{
RenderInternalLight(bEnableStrataStencilTest, bEnableStrataTiledPass, EStrataTileMaterialType::ESimple);
}
}
}
void FDeferredShadingSceneRenderer::RenderLight(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const FLightSceneInfo* LightSceneInfo,
FRDGTextureRef ScreenShadowMaskTexture,
FRDGTextureRef LightingChannelsTexture,
bool bRenderOverlap)
{
ERDGPassFlags PassFlags = ERDGPassFlags::Raster;
const FVolumetricCloudRenderSceneInfo* CloudInfo = Scene->GetVolumetricCloudSceneInfo();
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
{
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, ViewCount > 1, "View%d", ViewIndex);
const FViewInfo& View = Views[ViewIndex];
FRenderLightParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightParameters>();
GetRenderLightParameters(View, SceneTextures, View.HairStrandsViewData, ScreenShadowMaskTexture, LightingChannelsTexture, GetCloudShadowAOParameters(GraphBuilder, View, CloudInfo), *PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("StandardDeferredLighting"),
PassParameters,
PassFlags,
[this, &View, LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, bRenderOverlap, PassParameters](FRHICommandList& RHICmdList)
{
RenderLight(
RHICmdList,
View,
LightSceneInfo,
PassParameters,
TryGetRHI(ScreenShadowMaskTexture),
TryGetRHI(LightingChannelsTexture),
bRenderOverlap,
false);
});
}
}
BEGIN_SHADER_PARAMETER_STRUCT(FRenderLightForHairParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FRenderLightParameters, Light)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HairTransmittanceMaskSRV)
END_SHADER_PARAMETER_STRUCT()
void FDeferredShadingSceneRenderer::RenderLightForHair(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
const FLightSceneInfo* LightSceneInfo,
FRDGTextureRef HairShadowMaskTexture,
FRDGTextureRef LightingChannelsTexture,
const FHairStrandsTransmittanceMaskData& InTransmittanceMaskData)
{
const bool bHairRenderingEnabled = HairStrands::HasViewHairStrandsData(View);
if (!bHairRenderingEnabled)
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
INC_DWORD_STAT(STAT_NumLightsUsingStandardDeferred);
RDG_EVENT_SCOPE(GraphBuilder, "StandardDeferredLighting_Hair");
const FSphere LightBounds = LightSceneInfo->Proxy->GetBoundingSphere();
const bool bTransmission = LightSceneInfo->Proxy->Transmission();
const FVolumetricCloudRenderSceneInfo* CloudInfo = Scene->GetVolumetricCloudSceneInfo();
{
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
// Ensure the light is valid for this view
if (!LightSceneInfo->ShouldRenderLight(View))
{
return;
}
const FHairStrandsVisibilityData& HairVisibilityData = View.HairStrandsViewData.VisibilityData;
if (!HairVisibilityData.SampleLightingBuffer)
{
return;
}
FRenderLightForHairParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightForHairParameters>();
GetRenderLightParameters(
View,
HairVisibilityData.SampleLightingBuffer,
nullptr,
SceneTexturesUniformBuffer,
HairStrands::BindHairStrandsViewUniformParameters(View),
HairShadowMaskTexture,
LightingChannelsTexture,
GetCloudShadowAOParameters(GraphBuilder, View, CloudInfo),
PassParameters->Light);
// Sanity check
check(InTransmittanceMaskData.TransmittanceMask);
PassParameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View);
PassParameters->HairTransmittanceMaskSRV = GraphBuilder.CreateSRV(InTransmittanceMaskData.TransmittanceMask, FHairStrandsTransmittanceMaskData::Format);
const bool bIsShadowMaskValid = !!PassParameters->Light.ShadowMaskTexture;
const uint32 MaxTransmittanceElementCount = InTransmittanceMaskData.TransmittanceMask ? InTransmittanceMaskData.TransmittanceMask->Desc.NumElements : 0;
GraphBuilder.AddPass(
{},
PassParameters,
ERDGPassFlags::Raster,
[this, &HairVisibilityData, &View, PassParameters, LightSceneInfo, MaxTransmittanceElementCount, HairShadowMaskTexture, LightingChannelsTexture, bIsShadowMaskValid](FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(0, 0, 0.0f, HairVisibilityData.SampleLightingViewportResolution.X, HairVisibilityData.SampleLightingViewportResolution.Y, 1.0f);
FRenderLightParams RenderLightParams;
RenderLightParams.DeepShadow_TransmittanceMaskBufferMaxCount = MaxTransmittanceElementCount;
RenderLightParams.ScreenShadowMaskSubPixelTexture = bIsShadowMaskValid ? PassParameters->Light.ShadowMaskTexture->GetRHI() : GSystemTextures.WhiteDummy->GetShaderResourceRHI();
RenderLightParams.DeepShadow_TransmittanceMaskBuffer = PassParameters->HairTransmittanceMaskSRV->GetRHI();
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Max, BF_SourceAlpha, BF_DestAlpha>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
FDeferredLightPS::FPermutationDomain PermutationVector;
if (LightSceneInfo->Proxy->GetLightType() == LightType_Directional)
{
bool bAtmospherePerPixelTransmittance = false;
bool bCloudPerPixelTransmittance = false;
BindAtmosphereAndCloudResources(
Scene,
&View,
LightSceneInfo->Proxy,
RenderLightParams,
bAtmospherePerPixelTransmittance,
bCloudPerPixelTransmittance);
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >(ELightSourceShape::Directional);
PermutationVector.Set< FDeferredLightPS::FSourceTextureDim >(false);
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >(false);
PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >(false);
PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >(bAtmospherePerPixelTransmittance);
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(bCloudPerPixelTransmittance);
}
else
{
const bool bUseIESTexture = View.Family->EngineShowFlags.TexturedLightProfiles && LightSceneInfo->Proxy->GetIESTextureResource() != 0;
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >(LightSceneInfo->Proxy->IsRectLight() ? ELightSourceShape::Rect : ELightSourceShape::Capsule);
PermutationVector.Set< FDeferredLightPS::FSourceTextureDim >(LightSceneInfo->Proxy->IsRectLight() && LightSceneInfo->Proxy->HasSourceTexture());
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >(bUseIESTexture);
PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >(LightSceneInfo->Proxy->IsInverseSquared());
PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >(false);
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(false);
}
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >(View.bUsesLightingChannels);
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >(false);
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >(false);
PermutationVector.Set< FDeferredLightPS::FHairLighting>(1);
TShaderMapRef<TDeferredLightHairVS> VertexShader(View.ShaderMap);
TShaderMapRef<FDeferredLightPS> PixelShader(View.ShaderMap, PermutationVector);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
GraphicsPSOInit.bDepthBounds = false;
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
VertexShader->SetParameters(RHICmdList, View, PassParameters->HairStrands->GetRHI());
PixelShader->SetParameters(
RHICmdList,
View,
LightSceneInfo,
TryGetRHI(HairShadowMaskTexture),
TryGetRHI(LightingChannelsTexture),
&RenderLightParams,
PassParameters->HairStrands->GetRHI());
RHICmdList.SetStreamSource(0, nullptr, 0);
RHICmdList.DrawPrimitive(0, 1, 1);
});
}
}
// Forward lighting version for hair
void FDeferredShadingSceneRenderer::RenderLightsForHair(
FRDGBuilder& GraphBuilder,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
FSortedLightSetSceneInfo &SortedLightSet,
FRDGTextureRef ScreenShadowMaskSubPixelTexture,
FRDGTextureRef LightingChannelsTexture)
{
const FSimpleLightArray &SimpleLights = SortedLightSet.SimpleLights;
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator> &SortedLights = SortedLightSet.SortedLights;
const int32 AttenuationLightStart = SortedLightSet.AttenuationLightStart;
const int32 SimpleLightsEnd = SortedLightSet.SimpleLightsEnd;
if (ViewFamily.EngineShowFlags.DirectLighting)
{
RDG_EVENT_SCOPE(GraphBuilder, "DirectLighting");
for (FViewInfo& View : Views)
{
if (!HairStrands::HasViewHairStrandsData(View))
{
continue;
}
FHairStrandsTransmittanceMaskData DummyTransmittanceMaskData = CreateDummyHairStrandsTransmittanceMaskData(GraphBuilder, View.ShaderMap);
for (int32 LightIndex = AttenuationLightStart; LightIndex < SortedLights.Num(); LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const FLightSceneInfo& LightSceneInfo = *SortedLightInfo.LightSceneInfo;
if (LightSceneInfo.Proxy)
{
const bool bDrawHairShadow = SortedLightInfo.SortKey.Fields.bShadowed;
FHairStrandsTransmittanceMaskData TransmittanceMaskData = DummyTransmittanceMaskData;
if (bDrawHairShadow)
{
TransmittanceMaskData = RenderHairStrandsTransmittanceMask(GraphBuilder, View, &LightSceneInfo, ScreenShadowMaskSubPixelTexture);
}
RenderLightForHair(
GraphBuilder,
View,
SceneTexturesUniformBuffer,
&LightSceneInfo,
ScreenShadowMaskSubPixelTexture,
LightingChannelsTexture,
TransmittanceMaskData);
}
}
}
}
}
BEGIN_SHADER_PARAMETER_STRUCT(FSimpleLightsStandardDeferredParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FStrataGlobalUniformParameters, Strata)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
void FDeferredShadingSceneRenderer::RenderSimpleLightsStandardDeferred(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const FSimpleLightArray& SimpleLights)
{
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
INC_DWORD_STAT_BY(STAT_NumLightsUsingStandardDeferred, SimpleLights.InstanceData.Num());
FSimpleLightsStandardDeferredParameters* PassParameters = GraphBuilder.AllocParameters<FSimpleLightsStandardDeferredParameters>();
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
PassParameters->Strata = Strata::BindStrataGlobalUniformParameters(&Scene->StrataSceneData);
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.Color.Target, ERenderTargetLoadAction::ELoad);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite);
GraphBuilder.AddPass(
RDG_EVENT_NAME("StandardDeferredSimpleLights"),
PassParameters,
ERDGPassFlags::Raster,
[this, &SimpleLights](FRHICommandList& RHICmdList)
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
// Use additive blending for color
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
const int32 NumViews = Views.Num();
for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++)
{
const FSimpleLightEntry& SimpleLight = SimpleLights.InstanceData[LightIndex];
for (int32 ViewIndex = 0; ViewIndex < NumViews; ViewIndex++)
{
const FSimpleLightPerViewEntry& SimpleLightPerViewData = SimpleLights.GetViewDependentData(LightIndex, ViewIndex, NumViews);
const FSphere LightBounds(SimpleLightPerViewData.Position, SimpleLight.Radius);
const FViewInfo& View = Views[ViewIndex];
// Set the device viewport for the view.
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
TShaderMapRef<TDeferredLightVS<true> > VertexShader(View.ShaderMap);
const bool bCameraInsideLightGeometry = ((FVector)View.ViewMatrices.GetViewOrigin() - LightBounds.Center).SizeSquared() < FMath::Square(LightBounds.W * 1.05f + View.NearClippingDistance * 2.0f)
// Always draw backfaces in ortho
//@todo - accurate ortho camera / light intersection
|| !View.IsPerspectiveProjection();
SetBoundingGeometryRasterizerAndDepthState(GraphicsPSOInit, View, bCameraInsideLightGeometry);
if (SimpleLight.Exponent == 0)
{
// inverse squared
SetShaderTemplLightingSimple<false, true, true>(RHICmdList, GraphicsPSOInit, View, VertexShader, SimpleLight, SimpleLightPerViewData);
}
else
{
// light's exponent, not inverse squared
SetShaderTemplLightingSimple<false, true, false>(RHICmdList, GraphicsPSOInit, View, VertexShader, SimpleLight, SimpleLightPerViewData);
}
VertexShader->SetSimpleLightParameters(RHICmdList, View, LightBounds);
// Apply the point or spot light with some approximately bounding geometry,
// So we can get speedups from depth testing and not processing pixels outside of the light's influence.
StencilingGeometry::DrawSphere(RHICmdList);
}
}
});
}
class FCopyStencilToLightingChannelsPS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FCopyStencilToLightingChannelsPS);
SHADER_USE_PARAMETER_STRUCT(FCopyStencilToLightingChannelsPS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D<float>, SceneStencilTexture)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("STENCIL_LIGHTING_CHANNELS_SHIFT"), STENCIL_LIGHTING_CHANNELS_BIT_ID);
OutEnvironment.SetRenderTargetOutputFormat(0, PF_R16_UINT);
}
};
IMPLEMENT_GLOBAL_SHADER(FCopyStencilToLightingChannelsPS, "/Engine/Private/DownsampleDepthPixelShader.usf", "CopyStencilToLightingChannelsPS", SF_Pixel);
FRDGTextureRef FDeferredShadingSceneRenderer::CopyStencilToLightingChannelTexture(FRDGBuilder& GraphBuilder, FRDGTextureSRVRef SceneStencilTexture)
{
bool bNeedToCopyStencilToTexture = false;
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
{
bNeedToCopyStencilToTexture = bNeedToCopyStencilToTexture
|| Views[ViewIndex].bUsesLightingChannels
// Lumen uses a bit in stencil
|| GetViewPipelineState(Views[ViewIndex]).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen
|| GetViewPipelineState(Views[ViewIndex]).ReflectionsMethod == EReflectionsMethod::Lumen;
}
FRDGTextureRef LightingChannelsTexture = nullptr;
if (bNeedToCopyStencilToTexture)
{
RDG_EVENT_SCOPE(GraphBuilder, "CopyStencilToLightingChannels");
{
check(SceneStencilTexture && SceneStencilTexture->Desc.Texture);
const FIntPoint TextureExtent = SceneStencilTexture->Desc.Texture->Desc.Extent;
const FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(TextureExtent, PF_R8_UINT, FClearValueBinding::None, TexCreate_RenderTargetable | TexCreate_ShaderResource);
LightingChannelsTexture = GraphBuilder.CreateTexture(Desc, TEXT("LightingChannels"));
}
const ERenderTargetLoadAction LoadAction = ERenderTargetLoadAction::ENoAction;
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
{
const FViewInfo& View = Views[ViewIndex];
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
auto* PassParameters = GraphBuilder.AllocParameters<FCopyStencilToLightingChannelsPS::FParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(LightingChannelsTexture, View.DecayLoadAction(LoadAction));
PassParameters->SceneStencilTexture = SceneStencilTexture;
PassParameters->View = View.ViewUniformBuffer;
const FScreenPassTextureViewport Viewport(LightingChannelsTexture, View.ViewRect);
TShaderMapRef<FCopyStencilToLightingChannelsPS> PixelShader(View.ShaderMap);
AddDrawScreenPass(GraphBuilder, {}, View, Viewport, Viewport, PixelShader, PassParameters);
}
}
return LightingChannelsTexture;
}