You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #jira none #fyi sebastien.hillaire #preflight 6273fd2e2e58cb727df35672 [CL 20059820 by Charles deRousiers in ue5-main branch]
2677 lines
117 KiB
C++
2677 lines
117 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"
|
|
#include "AnisotropyRendering.h"
|
|
#include "Engine/SubsurfaceProfile.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;
|
|
|
|
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()
|
|
{
|
|
const bool bIsStereo = GEngine->StereoRenderingDevice.IsValid() && GEngine->StereoRenderingDevice->IsStereoEnabled();
|
|
const bool bHairStrands = IsHairStrandsEnabled(EHairStrandsShaderType::Strands);
|
|
|
|
return ShouldRenderRayTracingEffect((CVarRayTracingOcclusion.GetValueOnRenderThread() > 0) && !(bIsStereo && bHairStrands), ERayTracingPipelineCompatibilityFlags::FullPipeline, nullptr);
|
|
}
|
|
|
|
bool ShouldRenderRayTracingShadowsForLight(const FLightSceneProxy& LightProxy)
|
|
{
|
|
const bool bShadowRayTracingAllowed = ShouldRenderRayTracingEffect(true, ERayTracingPipelineCompatibilityFlags::FullPipeline, nullptr);
|
|
return (LightProxy.CastsRaytracedShadow() == ECastRayTracedShadow::Enabled || (ShouldRenderRayTracingShadows() && LightProxy.CastsRaytracedShadow() == ECastRayTracedShadow::UseProjectSetting))
|
|
&& ShouldRenderRayTracingShadowsForLightType((ELightComponentType)LightProxy.GetLightType())
|
|
&& bShadowRayTracingAllowed;
|
|
}
|
|
|
|
bool ShouldRenderRayTracingShadowsForLight(const FLightSceneInfoCompact& LightInfo)
|
|
{
|
|
const bool bShadowRayTracingAllowed = ShouldRenderRayTracingEffect(true, ERayTracingPipelineCompatibilityFlags::FullPipeline, nullptr);
|
|
return (LightInfo.CastRaytracedShadow == ECastRayTracedShadow::Enabled || (ShouldRenderRayTracingShadows() && LightInfo.CastRaytracedShadow == ECastRayTracedShadow::UseProjectSetting))
|
|
&& ShouldRenderRayTracingShadowsForLightType((ELightComponentType)LightInfo.LightType)
|
|
&& bShadowRayTracingAllowed;
|
|
}
|
|
#endif // RHI_RAYTRACING
|
|
|
|
FDrawFullScreenRectangleParameters FDeferredLightVS::GetFullScreenRectParameters(
|
|
float X, float Y,
|
|
float SizeX, float SizeY,
|
|
float U, float V,
|
|
float SizeU, float SizeV,
|
|
FIntPoint InTargetSize,
|
|
FIntPoint InTextureSize)
|
|
{
|
|
FDrawFullScreenRectangleParameters Out;
|
|
Out.PosScaleBias = FVector4f(SizeX, SizeY, X, Y);
|
|
Out.UVScaleBias = FVector4f(SizeU, SizeV, U, V);
|
|
Out.InvTargetSizeAndTextureSize = FVector4f(
|
|
1.0f / InTargetSize.X, 1.0f / InTargetSize.Y,
|
|
1.0f / InTextureSize.X, 1.0f / InTextureSize.Y);
|
|
return Out;
|
|
}
|
|
|
|
FDeferredLightVS::FParameters FDeferredLightVS::GetParameters(const FViewInfo& View,
|
|
float X, float Y,
|
|
float SizeX, float SizeY,
|
|
float U, float V,
|
|
float SizeU, float SizeV,
|
|
FIntPoint TargetSize,
|
|
FIntPoint TextureSize,
|
|
bool bBindViewUniform)
|
|
{
|
|
FParameters Out;
|
|
if (bBindViewUniform)
|
|
{
|
|
Out.View = View.ViewUniformBuffer;
|
|
}
|
|
Out.Geometry = FStencilingGeometryShaderParameters::GetParameters(FVector4f(0, 0, 0, 0));
|
|
Out.FullScreenRect = GetFullScreenRectParameters(X, Y, SizeX, SizeY, U, V, SizeU, SizeV, TargetSize, TextureSize);
|
|
return Out;
|
|
}
|
|
|
|
FDeferredLightVS::FParameters FDeferredLightVS::GetParameters(const FViewInfo& View, bool bBindViewUniform)
|
|
{
|
|
FParameters Out;
|
|
if (bBindViewUniform)
|
|
{
|
|
Out.View = View.ViewUniformBuffer;
|
|
}
|
|
Out.Geometry = FStencilingGeometryShaderParameters::GetParameters(FVector4f(0, 0, 0, 0));
|
|
Out.FullScreenRect = GetFullScreenRectParameters(
|
|
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(),
|
|
View.GetSceneTexturesConfig().Extent);
|
|
|
|
return Out;
|
|
}
|
|
|
|
FDeferredLightVS::FParameters FDeferredLightVS::GetParameters(const FViewInfo& View, const FSphere& LightBounds, bool bBindViewUniform)
|
|
{
|
|
FVector4f StencilingSpherePosAndScale;
|
|
StencilingGeometry::GStencilSphereVertexBuffer.CalcTransform(StencilingSpherePosAndScale, LightBounds, View.ViewMatrices.GetPreViewTranslation());
|
|
|
|
FParameters Out;
|
|
if (bBindViewUniform)
|
|
{
|
|
Out.View = View.ViewUniformBuffer;
|
|
}
|
|
Out.Geometry = FStencilingGeometryShaderParameters::GetParameters((FVector4f)StencilingSpherePosAndScale); // LWC_TODO: Precision loss
|
|
Out.FullScreenRect = GetFullScreenRectParameters(0, 0, 0, 0, 0, 0, 0, 0, FIntPoint(1, 1), FIntPoint(1, 1));
|
|
return Out;
|
|
}
|
|
|
|
FDeferredLightVS::FParameters FDeferredLightVS::GetParameters(const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, bool bBindViewUniform)
|
|
{
|
|
FParameters Out;
|
|
if (bBindViewUniform)
|
|
{
|
|
Out.View = View.ViewUniformBuffer;
|
|
}
|
|
Out.Geometry = FStencilingGeometryShaderParameters::GetParameters(View, LightSceneInfo);
|
|
Out.FullScreenRect = GetFullScreenRectParameters(0, 0, 0, 0, 0, 0, 0, 0, FIntPoint(1, 1), FIntPoint(1, 1));
|
|
return Out;
|
|
}
|
|
|
|
static void RenderLight(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FScene* Scene,
|
|
const FViewInfo& View,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
const FLightSceneInfo* LightSceneInfo,
|
|
FRDGTextureRef ScreenShadowMaskTexture,
|
|
FRDGTextureRef LightingChannelsTexture,
|
|
bool bRenderOverlap,
|
|
bool bCloudShadow);
|
|
|
|
FDeferredLightUniformStruct GetDeferredLightParameters(const FSceneView& View, const FLightSceneInfo& LightSceneInfo)
|
|
{
|
|
FDeferredLightUniformStruct Out;
|
|
|
|
FLightRenderParameters LightParameters;
|
|
LightSceneInfo.Proxy->GetLightShaderParameters(LightParameters);
|
|
LightParameters.MakeShaderParameters(View.ViewMatrices, Out.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
|
|
Out.DistanceFadeMAD = FVector2f(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;
|
|
}
|
|
|
|
Out.ShadowMapChannelMask = FVector4f(
|
|
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;
|
|
Out.ShadowedBits = LightSceneInfo.Proxy->CastsStaticShadow() || bHasLightFunction ? 1 : 0;
|
|
Out.ShadowedBits |= LightSceneInfo.Proxy->CastsDynamicShadow() && View.Family->EngineShowFlags.DynamicShadows ? 3 : 0;
|
|
|
|
Out.VolumetricScatteringIntensity = LightSceneInfo.Proxy->GetVolumetricScatteringIntensity();
|
|
|
|
static auto* ContactShadowsCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ContactShadows"));
|
|
static auto* IntensityCVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.ContactShadows.NonShadowCastingIntensity"));
|
|
|
|
Out.ContactShadowLength = 0;
|
|
Out.ContactShadowNonShadowCastingIntensity = 0.0f;
|
|
|
|
if (ContactShadowsCVar && ContactShadowsCVar->GetValueOnRenderThread() != 0 && View.Family->EngineShowFlags.ContactShadows)
|
|
{
|
|
Out.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.
|
|
Out.ContactShadowLength *= LightSceneInfo.Proxy->IsContactShadowLengthInWS() ? -1.0f : 2.0f;
|
|
|
|
Out.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)
|
|
{
|
|
Out.LightParameters.Color *= LightSceneInfo.Proxy->GetIndirectLightingScale();
|
|
}
|
|
|
|
const ELightComponentType LightType = (ELightComponentType)LightSceneInfo.Proxy->GetLightType();
|
|
if ((LightType == LightType_Point || LightType == LightType_Spot || LightType == LightType_Rect) && View.IsPerspectiveProjection())
|
|
{
|
|
Out.LightParameters.Color *= GetLightFadeFactor(View, LightSceneInfo.Proxy);
|
|
}
|
|
|
|
Out.LightingChannelMask = LightSceneInfo.Proxy->GetLightingChannelMask();
|
|
|
|
// Ensure the light falloff exponent is set to 0 so that lighting shaders handle it as inverse-squared attenuated light
|
|
if (LightSceneInfo.Proxy->IsInverseSquared())
|
|
{
|
|
Out.LightParameters.FalloffExponent = 0.0f;
|
|
}
|
|
return Out;
|
|
}
|
|
|
|
FDeferredLightUniformStruct GetSimpleDeferredLightParameters(
|
|
const FSceneView& View,
|
|
const FSimpleLightEntry& SimpleLight,
|
|
const FVector& LightWorldPosition)
|
|
{
|
|
FDeferredLightUniformStruct Out;
|
|
Out.ShadowMapChannelMask = FVector4f(0, 0, 0, 0);
|
|
Out.DistanceFadeMAD = FVector2f(0, 0);
|
|
Out.ContactShadowLength = 0.0f;
|
|
Out.ContactShadowNonShadowCastingIntensity = 0.f;
|
|
Out.VolumetricScatteringIntensity = SimpleLight.VolumetricScatteringIntensity;
|
|
Out.ShadowedBits = 0;
|
|
Out.LightingChannelMask = 0;
|
|
|
|
Out.LightParameters.TranslatedWorldPosition = FVector3f(LightWorldPosition + View.ViewMatrices.GetPreViewTranslation());
|
|
Out.LightParameters.InvRadius = 1.0f / FMath::Max(SimpleLight.Radius, KINDA_SMALL_NUMBER);
|
|
Out.LightParameters.Color = (FVector3f)SimpleLight.Color;
|
|
Out.LightParameters.FalloffExponent = SimpleLight.Exponent;
|
|
Out.LightParameters.Direction = FVector3f(1, 0, 0);
|
|
Out.LightParameters.Tangent = FVector3f(1, 0, 0);
|
|
Out.LightParameters.SpotAngles = FVector2f(-2, 1);
|
|
Out.LightParameters.SpecularScale = 1.0f;
|
|
Out.LightParameters.SourceRadius = 0.0f;
|
|
Out.LightParameters.SoftSourceRadius = 0.0f;
|
|
Out.LightParameters.SourceLength = 0.0f;
|
|
Out.LightParameters.RectLightBarnCosAngle = 0;
|
|
Out.LightParameters.RectLightBarnLength = -2.0f;
|
|
Out.LightParameters.RectLightAtlasUVOffset = FVector2f::ZeroVector;
|
|
Out.LightParameters.RectLightAtlasUVScale = FVector2f::ZeroVector;
|
|
Out.LightParameters.RectLightAtlasMaxLevel = FLightRenderParameters::GetRectLightAtlasInvalidMIPLevel();
|
|
return Out;
|
|
}
|
|
FDeferredLightUniformStruct GetSimpleDeferredLightParameters(
|
|
const FSceneView& View,
|
|
const FSimpleLightEntry& SimpleLight,
|
|
const FSimpleLightPerViewEntry& SimpleLightPerViewData)
|
|
{
|
|
return GetSimpleDeferredLightParameters(View, SimpleLight, SimpleLightPerViewData.Position);
|
|
}
|
|
|
|
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, FVector4f> > StencilingGeometry::GStencilSphereVertexBuffer;
|
|
TGlobalResource<StencilingGeometry::TStencilSphereVertexBuffer<18, 12, FVector3f> > StencilingGeometry::GStencilSphereVectorBuffer;
|
|
|
|
/** The stencil sphere index buffer. */
|
|
TGlobalResource<StencilingGeometry::TStencilSphereIndexBuffer<18, 12> > StencilingGeometry::GStencilSphereIndexBuffer;
|
|
|
|
TGlobalResource<StencilingGeometry::TStencilSphereVertexBuffer<4, 4, FVector4f> > 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_GLOBAL_SHADER(FDeferredLightVS, "/Engine/Private/DeferredLightVertexShaders.usf", "VertexMain", SF_Vertex);
|
|
|
|
class FDeferredLightHairVS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FDeferredLightHairVS, Global);
|
|
SHADER_USE_PARAMETER_STRUCT(FDeferredLightHairVS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands)
|
|
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("SHADER_HAIR"), 1);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FDeferredLightHairVS, "/Engine/Private/DeferredLightVertexShaders.usf", "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)
|
|
SHADER_USE_PARAMETER_STRUCT(FDeferredLightPS, FGlobalShader);
|
|
|
|
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 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 FStrataTileType : SHADER_PERMUTATION_INT("STRATA_TILETYPE", 3);
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<
|
|
FSourceShapeDim,
|
|
FSourceTextureDim,
|
|
FIESProfileDim,
|
|
FVisualizeCullingDim,
|
|
FLightingChannelsDim,
|
|
FTransmissionDim,
|
|
FHairLighting,
|
|
FAtmosphereTransmittance,
|
|
FCloudTransmittance,
|
|
FAnistropicMaterials,
|
|
FStrataTileType>;
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
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)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FLightCloudTransmittanceParameters, CloudShadow)
|
|
SHADER_PARAMETER(uint32, CloudShadowEnabled)
|
|
SHADER_PARAMETER(uint32, HairTransmittanceBufferMaxCount)
|
|
SHADER_PARAMETER(uint32, HairShadowMaskValid)
|
|
SHADER_PARAMETER(FVector4f, ShadowChannelMask)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LightAttenuationTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, LightAttenuationTextureSampler)
|
|
SHADER_PARAMETER_TEXTURE(Texture2D, IESTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, IESTextureSampler)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LightingChannelsTexture)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, LightingChannelsSampler)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HairTransmittanceBuffer)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ScreenShadowMaskSubPixelTexture)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DummyRectLightTextureForCapsuleCompilerWarning)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, DummyRectLightSamplerForCapsuleCompilerWarning)
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FDeferredLightUniformStruct, DeferredLight)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
// Build FVisualizeCullingDim permutation only for a restricted number of case, as they don't impact the 'estimated cost' of lighting
|
|
if (PermutationVector.Get< FVisualizeCullingDim >() && (
|
|
PermutationVector.Get< FSourceTextureDim >() ||
|
|
PermutationVector.Get< FIESProfileDim >() ||
|
|
PermutationVector.Get< FTransmissionDim >() ||
|
|
PermutationVector.Get< FHairLighting >() ||
|
|
PermutationVector.Get< FAtmosphereTransmittance >() ||
|
|
PermutationVector.Get< FCloudTransmittance >() ||
|
|
PermutationVector.Get< FAnistropicMaterials >()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (PermutationVector.Get< FSourceShapeDim >() == ELightSourceShape::Directional && PermutationVector.Get< FIESProfileDim >())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (PermutationVector.Get< FSourceShapeDim >() != ELightSourceShape::Directional && (PermutationVector.Get<FAtmosphereTransmittance>() || PermutationVector.Get<FCloudTransmittance>()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( PermutationVector.Get< FSourceShapeDim >() != ELightSourceShape::Rect && PermutationVector.Get< FSourceTextureDim >())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (PermutationVector.Get< FHairLighting >() && PermutationVector.Get< FTransmissionDim >())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (PermutationVector.Get<FDeferredLightPS::FAnistropicMaterials>())
|
|
{
|
|
if (Strata::IsStrataEnabled())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 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 (!Strata::IsStrataEnabled() && PermutationVector.Get<FStrataTileType>() != 0)
|
|
{
|
|
return false;
|
|
}
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
|
|
}
|
|
|
|
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
|
|
{
|
|
// Build FVisualizeCullingDim permutation only for a restricted number of case, as they don't impact the 'estimated cost' of lighting
|
|
if (PermutationVector.Get< FVisualizeCullingDim >())
|
|
{
|
|
PermutationVector.Set< FSourceTextureDim >(false);
|
|
PermutationVector.Set< FIESProfileDim >(false);
|
|
PermutationVector.Set< FTransmissionDim >(false);
|
|
PermutationVector.Set< FHairLighting >(false);
|
|
PermutationVector.Set< FAtmosphereTransmittance >(false);
|
|
PermutationVector.Set< FCloudTransmittance >(false);
|
|
PermutationVector.Set< FAnistropicMaterials >(false);
|
|
}
|
|
|
|
return PermutationVector;
|
|
}
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FDeferredLightPS, "/Engine/Private/DeferredLightPixelShaders.usf", "DeferredLightPixelMain", SF_Pixel);
|
|
|
|
/** Shader used to visualize stationary light overlap. */
|
|
class FDeferredLightOverlapPS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FDeferredLightOverlapPS, Global)
|
|
SHADER_USE_PARAMETER_STRUCT(FDeferredLightOverlapPS, FGlobalShader);
|
|
|
|
class FRadialAttenuation : SHADER_PERMUTATION_BOOL("RADIAL_ATTENUATION");
|
|
using FPermutationDomain = TShaderPermutationDomain<FRadialAttenuation>;
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FDeferredLightUniformStruct, DeferredLight)
|
|
SHADER_PARAMETER(float, bHasValidChannel)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FDeferredLightOverlapPS, "/Engine/Private/StationaryLightOverlapShaders.usf", "OverlapPixelMain", 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 TArrayView<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->GetOwnerNameOrLabel();
|
|
}
|
|
#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;
|
|
}
|
|
|
|
bool FSceneRenderer::AllowSimpleLights() const
|
|
{
|
|
return bAllowSimpleLights == 1;
|
|
}
|
|
|
|
void FSceneRenderer::GatherAndSortLights(FSortedLightSetSceneInfo& OutSortedLights, bool bShadowedLightsInClustered)
|
|
{
|
|
if (AllowSimpleLights())
|
|
{
|
|
GatherSimpleLights(*ActiveViewFamily, 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 = ActiveViewFamily->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 (auto LightIt = Scene->Lights.CreateConstIterator(); 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
|
|
&& !ActiveViewFamily->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 = ActiveViewFamily->EngineShowFlags.TexturedLightProfiles && LightSceneInfo->Proxy->GetIESTextureResource();
|
|
SortedLightInfo->SortKey.Fields.bShadowed = bDynamicShadows && CheckForProjectedShadows(LightSceneInfo);
|
|
SortedLightInfo->SortKey.Fields.bLightFunction = ActiveViewFamily->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 bClusteredDeferredSupported =
|
|
!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;
|
|
|
|
// One pass projection is supported for lights with only virtual shadow maps
|
|
// TODO: Exclude lights that also have non-virtual shadow maps
|
|
bool bHasVirtualShadowMap = ActiveViewFamily->VisibleLightInfos[LightSceneInfo->Id].GetVirtualShadowMapId(&Views[ViewIndex]) != INDEX_NONE;
|
|
SortedLightInfo->SortKey.Fields.bDoesNotWriteIntoPackedShadowMask = !bClusteredDeferredSupported || !bHasVirtualShadowMap;
|
|
SortedLightInfo->SortKey.Fields.bClusteredDeferredNotSupported = !bClusteredDeferredSupported;
|
|
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.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.ClusteredSupportedEnd = SortedLights.Num();
|
|
OutSortedLights.UnbatchedLightStart = 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.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 an unbatched shadowed light, we can exit the loop
|
|
check(SortedLightInfo.SortKey.Fields.bClusteredDeferredNotSupported);
|
|
OutSortedLights.UnbatchedLightStart = LightIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Make sure no obvious things went wrong!
|
|
check(OutSortedLights.ClusteredSupportedEnd >= OutSortedLights.SimpleLightsEnd);
|
|
check(OutSortedLights.UnbatchedLightStart >= OutSortedLights.ClusteredSupportedEnd);
|
|
}
|
|
|
|
FHairStrandsTransmittanceMaskData CreateDummyHairStrandsTransmittanceMaskData(FRDGBuilder& GraphBuilder, FGlobalShaderMap* ShaderMap);
|
|
|
|
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 UnbatchedLightStart = SortedLightSet.UnbatchedLightStart;
|
|
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 (ActiveViewFamily->EngineShowFlags.DirectLighting && Strata::IsStrataEnabled())
|
|
{
|
|
// Update the stencil buffer, marking simple/complex strata material only once for all the following passes.
|
|
Strata::AddStrataStencilPass(GraphBuilder, Views, SceneTextures);
|
|
}
|
|
|
|
if(ActiveViewFamily->EngineShowFlags.DirectLighting)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "BatchedLights");
|
|
INC_DWORD_STAT_BY(STAT_NumBatchedLights, UnbatchedLightStart);
|
|
|
|
// 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( ActiveViewFamily->VirtualShadowMapArray.IsAllocated() && CVarVirtualShadowOnePassProjection.GetValueOnRenderThread() )
|
|
{
|
|
// TODO: This needs to move into the view loop in clustered deferred shading pass
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
ShadowMaskBits = RenderVirtualShadowMapProjectionOnePass(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
View, ViewIndex,
|
|
ActiveViewFamily->VirtualShadowMapArray,
|
|
EVirtualShadowMapProjectionInputType::GBuffer);
|
|
|
|
if (HairStrands::HasViewHairStrandsData(View))
|
|
{
|
|
HairStrandsShadowMaskBits = RenderVirtualShadowMapProjectionOnePass(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
View, ViewIndex,
|
|
ActiveViewFamily->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);
|
|
}
|
|
|
|
if (bRenderSimpleLightsStandardDeferred)
|
|
{
|
|
RenderSimpleLightsStandardDeferred(GraphBuilder, SceneTextures, SortedLightSet.SimpleLights);
|
|
}
|
|
|
|
// Draw non-shadowed non-light function lights without changing render targets between them
|
|
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, ViewCount > 1, "View%d", ViewIndex);
|
|
SCOPED_GPU_MASK(GraphBuilder.RHICmdList, View.GPUMask);
|
|
|
|
for (int32 LightIndex = StandardDeferredStart; LightIndex < UnbatchedLightStart; LightIndex++)
|
|
{
|
|
// Render the light to the scene color buffer, using a 1x1 white texture as input
|
|
const FLightSceneInfo* LightSceneInfo = SortedLights[LightIndex].LightSceneInfo;
|
|
RenderLight(GraphBuilder, Scene, View, SceneTextures, LightSceneInfo, nullptr, LightingChannelsTexture, false /*bRenderOverlap*/, false /*bCloudShadow*/);
|
|
}
|
|
}
|
|
|
|
// 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 < UnbatchedLightStart; LightIndex++)
|
|
{
|
|
const FLightSceneInfo* LightSceneInfo = SortedLights[LightIndex].LightSceneInfo;
|
|
RenderLightForHair(GraphBuilder, View, SceneTextures, LightSceneInfo, NullScreenShadowMaskSubPixelTexture, LightingChannelsTexture, DummyTransmittanceMaskData, false /*bForwardRendering*/);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering)
|
|
{
|
|
if (UnbatchedLightStart)
|
|
{
|
|
// Inject non-shadowed, non-simple, non-light function lights in to the volume.
|
|
InjectTranslucencyLightingVolumeArray(GraphBuilder, Views, Scene, *this, TranslucencyLightingVolumeTextures, ActiveViewFamily->VisibleLightInfos, SortedLights, TInterval<int32>(SimpleLightsEnd, UnbatchedLightStart));
|
|
}
|
|
|
|
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, "UnbatchedLights");
|
|
|
|
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 = ActiveViewFamily->EngineShowFlags.DirectLighting;
|
|
|
|
FRDGTextureRef SharedScreenShadowMaskTexture = nullptr;
|
|
FRDGTextureRef SharedScreenShadowMaskSubPixelTexture = nullptr;
|
|
|
|
// Draw shadowed and light function lights
|
|
for (int32 LightIndex = UnbatchedLightStart; 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 = ActiveViewFamily->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 - UnbatchedLightStart]))
|
|
{
|
|
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 - UnbatchedLightStart];
|
|
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 - UnbatchedLightStart];
|
|
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 - UnbatchedLightStart] = 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 - UnbatchedLightStart])
|
|
{
|
|
const uint32 ShadowMaskIndex = LightIndex - UnbatchedLightStart;
|
|
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)
|
|
{
|
|
RenderHairStrandsDeepShadowMask(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, false /*bForwardShading*/, ScreenShadowMaskTexture);
|
|
}
|
|
}
|
|
else // (OcclusionType == FOcclusionType::Shadowmap)
|
|
{
|
|
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;
|
|
}
|
|
|
|
// 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, ActiveViewFamily->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)
|
|
{
|
|
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, ViewCount > 1, "View%d", ViewIndex);
|
|
SCOPED_GPU_MASK(GraphBuilder.RHICmdList, View.GPUMask);
|
|
RenderLight(GraphBuilder, Scene, View, SceneTextures, &LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, false /*bRenderOverlap*/, true /*bCloudShadow*/);
|
|
}
|
|
}
|
|
|
|
if (bUseHairLighting)
|
|
{
|
|
for (FViewInfo& View : Views)
|
|
{
|
|
if (bDrawHairShadow && HairStrands::HasViewHairStrandsData(View))
|
|
{
|
|
FHairStrandsTransmittanceMaskData TransmittanceMaskData = RenderHairStrandsTransmittanceMask(GraphBuilder, View, &LightSceneInfo, false, 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, &LightSceneInfo, ScreenShadowMaskSubPixelTexture, LightingChannelsTexture, TransmittanceMaskData, false /*bForwardRendering*/);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void RenderLightArrayForOverlapViewmode(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FScene* Scene,
|
|
const TArrayView<FViewInfo>& Views,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
FRDGTextureRef LightingChannelsTexture,
|
|
const TSparseArray<FLightSceneInfoCompact, TAlignedSparseArrayAllocator<alignof(FLightSceneInfoCompact)>>& LightArray)
|
|
{
|
|
for (auto LightIt = LightArray.CreateConstIterator(); 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(GraphBuilder.RHICmdList, View.GPUMask);
|
|
RenderLight(GraphBuilder, Scene, View, SceneTextures, LightSceneInfo, nullptr, LightingChannelsTexture, true /*bRenderOverlap*/, false /*bCloudShadow*/);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderStationaryLightOverlap(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
FRDGTextureRef LightingChannelsTexture)
|
|
{
|
|
if (Scene->bIsEditorScene)
|
|
{
|
|
// Clear to discard base pass values in scene color since we didn't skip that, to have valid scene depths
|
|
AddClearRenderTargetPass(GraphBuilder, SceneTextures.Color.Target, FLinearColor::Black);
|
|
|
|
RenderLightArrayForOverlapViewmode(GraphBuilder, Scene, Views, SceneTextures, LightingChannelsTexture, 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(GraphBuilder, Scene, Views, SceneTextures, LightingChannelsTexture, Scene->InvisibleLights);
|
|
}
|
|
}
|
|
|
|
static void InternalSetBoundingGeometryRasterizerState(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();
|
|
}
|
|
}
|
|
|
|
template<ECompareFunction CompareFunction>
|
|
static uint32 InternalSetBoundingGeometryDepthState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, EStrataTileType TileType)
|
|
{
|
|
// bCameraInsideLightGeometry = true -> CompareFunction = Always
|
|
// bCameraInsideLightGeometry = false -> CompareFunction = CF_DepthNearOrEqual
|
|
uint32 StencilRef = 0u;
|
|
if (TileType != EStrataTileType::ECount)
|
|
{
|
|
check(Strata::IsStrataEnabled());
|
|
switch (TileType)
|
|
{
|
|
case EStrataTileType::ESimple : StencilRef = Strata::StencilBit_Fast; GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CompareFunction, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, Strata::StencilBit_Fast, 0x0>::GetRHI(); break;
|
|
case EStrataTileType::ESingle : StencilRef = Strata::StencilBit_Single; GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CompareFunction, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, Strata::StencilBit_Single, 0x0>::GetRHI(); break;
|
|
case EStrataTileType::EComplex: StencilRef = Strata::StencilBit_Complex; GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CompareFunction, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, true, CF_Equal, SO_Keep, SO_Keep, SO_Keep, Strata::StencilBit_Complex, 0x0>::GetRHI(); break;
|
|
default: check(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CompareFunction>::GetRHI();
|
|
}
|
|
return StencilRef;
|
|
}
|
|
|
|
/** Sets up rasterizer and depth state for rendering bounding geometry in a deferred pass. */
|
|
static uint32 SetBoundingGeometryRasterizerAndDepthState(FGraphicsPipelineStateInitializer& GraphicsPSOInit, const FViewInfo& View, bool bCameraInsideLightGeometry, EStrataTileType TileType)
|
|
{
|
|
uint32 StencilRef = 0u;
|
|
InternalSetBoundingGeometryRasterizerState(GraphicsPSOInit, View, bCameraInsideLightGeometry);
|
|
if (bCameraInsideLightGeometry)
|
|
{
|
|
StencilRef = InternalSetBoundingGeometryDepthState<CF_Always>(GraphicsPSOInit, TileType);
|
|
}
|
|
else
|
|
{
|
|
StencilRef = InternalSetBoundingGeometryDepthState<CF_DepthNearOrEqual>(GraphicsPSOInit, TileType);
|
|
}
|
|
|
|
return StencilRef;
|
|
}
|
|
|
|
// Use DBT to allow work culling on shadow lights
|
|
static 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 TRDGUniformBufferRef<FDeferredLightUniformStruct> CreateDeferredLightUniformBuffer(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FLightSceneInfo& LightSceneInfo)
|
|
{
|
|
auto* DeferredLightStruct = GraphBuilder.AllocParameters<FDeferredLightUniformStruct>();
|
|
*DeferredLightStruct = GetDeferredLightParameters(View, LightSceneInfo);
|
|
return GraphBuilder.CreateUniformBuffer(DeferredLightStruct);
|
|
}
|
|
|
|
static TRDGUniformBufferRef<FDeferredLightUniformStruct> CreateDeferredLightUniformBuffer(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSimpleLightEntry& SimpleLight, const FVector& SimpleLightPosition)
|
|
{
|
|
auto* DeferredLightStruct = GraphBuilder.AllocParameters<FDeferredLightUniformStruct>();
|
|
*DeferredLightStruct = GetSimpleDeferredLightParameters(View, SimpleLight, SimpleLightPosition);
|
|
return GraphBuilder.CreateUniformBuffer(DeferredLightStruct);
|
|
}
|
|
|
|
static FDeferredLightPS::FParameters GetDeferredLightPSParameters(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FScene* Scene,
|
|
const FViewInfo& View,
|
|
const FLightSceneInfo* LightSceneInfo,
|
|
FRDGTextureRef SceneColorTexture,
|
|
FRDGTextureRef SceneDepthTexture,
|
|
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
|
|
TRDGUniformBufferRef<FHairStrandsViewUniformParameters> HairStrandsUniformBuffer,
|
|
FRDGTextureRef ShadowMaskTexture,
|
|
FRDGTextureRef LightingChannelsTexture,
|
|
bool bCloudShadow)
|
|
{
|
|
FDeferredLightPS::FParameters Out;
|
|
|
|
const ELightComponentType LightType = (ELightComponentType)LightSceneInfo->Proxy->GetLightType();
|
|
const bool bIsDirectional = LightType == LightType_Directional;
|
|
|
|
FRDGTextureRef WhiteDummy = GSystemTextures.GetWhiteDummy(GraphBuilder);
|
|
FRDGTextureRef DepthDummy = GSystemTextures.GetDepthDummy(GraphBuilder);
|
|
FRDGBufferRef BufferDummy = GSystemTextures.GetDefaultBuffer(GraphBuilder, 4, 0u);
|
|
FRDGBufferSRVRef BufferDummySRV = GraphBuilder.CreateSRV(BufferDummy, PF_R32_UINT);
|
|
|
|
// PS - General parameters
|
|
const FVolumetricCloudRenderSceneInfo* CloudInfo = bCloudShadow ? Scene->GetVolumetricCloudSceneInfo() : nullptr;
|
|
Out.SceneTextures = SceneTexturesUniformBuffer;
|
|
Out.HairStrands = HairStrandsUniformBuffer;
|
|
Out.Strata = Strata::BindStrataGlobalUniformParameters(View);
|
|
Out.LightingChannelsTexture = LightingChannelsTexture ? LightingChannelsTexture : WhiteDummy;
|
|
Out.LightingChannelsSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Out.CloudShadowAO = GetCloudShadowAOParameters(GraphBuilder, View, CloudInfo);
|
|
Out.CloudShadowEnabled = SetupLightCloudTransmittanceParameters(GraphBuilder, Scene, View, LightSceneInfo, Out.CloudShadow) ? 1 : 0;
|
|
Out.LightAttenuationTexture = ShadowMaskTexture ? ShadowMaskTexture : WhiteDummy;
|
|
Out.LightAttenuationTextureSampler = TStaticSamplerState<SF_Point, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
Out.DummyRectLightTextureForCapsuleCompilerWarning = DepthDummy;
|
|
Out.DummyRectLightSamplerForCapsuleCompilerWarning = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Out.IESTextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Out.IESTexture = GSystemTextures.WhiteDummy->GetRHI();
|
|
if (LightSceneInfo->Proxy->GetIESTextureResource())
|
|
{
|
|
Out.IESTexture = LightSceneInfo->Proxy->GetIESTextureResource()->TextureRHI;
|
|
}
|
|
Out.View = View.ViewUniformBuffer;
|
|
Out.DeferredLight = CreateDeferredLightUniformBuffer(GraphBuilder, View, *LightSceneInfo);
|
|
// PS - Hair (default value)
|
|
Out.ScreenShadowMaskSubPixelTexture = WhiteDummy;
|
|
Out.HairTransmittanceBuffer = BufferDummySRV;
|
|
Out.HairTransmittanceBufferMaxCount = 0;
|
|
Out.HairShadowMaskValid = false;
|
|
Out.ShadowChannelMask = FVector4f(1, 1, 1, 1);
|
|
// PS - Render Targets
|
|
Out.RenderTargets[0] = FRenderTargetBinding(SceneColorTexture, ERenderTargetLoadAction::ELoad);
|
|
if (Strata::IsStrataOpaqueMaterialRoughRefractionEnabled())
|
|
{
|
|
Out.RenderTargets[1] = FRenderTargetBinding(Scene->StrataSceneData.SeparatedOpaqueRoughRefractionSceneColor, ERenderTargetLoadAction::ELoad);
|
|
Out.RenderTargets[2] = FRenderTargetBinding(Scene->StrataSceneData.SeparatedSubSurfaceSceneColor, ERenderTargetLoadAction::ELoad);
|
|
}
|
|
if (SceneDepthTexture)
|
|
{
|
|
Out.RenderTargets.DepthStencil = FDepthStencilBinding(SceneDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite);
|
|
}
|
|
|
|
return Out;
|
|
}
|
|
|
|
// Used by RenderLights to render a light to the scene color buffer.
|
|
template<typename TShaderType, typename TParametersType>
|
|
static void InternalRenderLight(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FScene* Scene,
|
|
const FViewInfo& View,
|
|
const FLightSceneInfo* LightSceneInfo,
|
|
TShaderType& PixelShader,
|
|
TParametersType* PassParameters,
|
|
EStrataTileType StrataTileMaterialType,
|
|
const TCHAR* ShaderName)
|
|
{
|
|
const FLightSceneProxy* RESTRICT LightProxy = LightSceneInfo->Proxy;
|
|
const bool bTransmission = LightProxy->Transmission();
|
|
const FSphere LightBounds = LightProxy->GetBoundingSphere();
|
|
const ELightComponentType LightType = (ELightComponentType)LightProxy->GetLightType();
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("%s", ShaderName),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[Scene, &View, PixelShader, LightSceneInfo, PassParameters, LightBounds, LightType, StrataTileMaterialType](FRHICommandList& RHICmdList)
|
|
{
|
|
|
|
const bool bIsRadial = LightType != LightType_Directional;
|
|
const bool bEnableStrataTiledPass = StrataTileMaterialType != EStrataTileType::ECount;
|
|
const bool bEnableStrataStencilTest = StrataTileMaterialType != EStrataTileType::ECount && bIsRadial;
|
|
|
|
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
|
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
|
// 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);
|
|
|
|
if (Strata::IsStrataOpaqueMaterialRoughRefractionEnabled())
|
|
{
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One,
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One,
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
|
|
}
|
|
else
|
|
{
|
|
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
|
|
}
|
|
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
|
|
|
|
if (LightType == LightType_Directional)
|
|
{
|
|
FDeferredLightVS::FPermutationDomain PermutationVectorVS;
|
|
PermutationVectorVS.Set<FDeferredLightVS::FRadialLight>(false);
|
|
TShaderMapRef<FDeferredLightVS> VertexShader(View.ShaderMap, PermutationVectorVS);
|
|
|
|
Strata::FStrataTilePassVS::FPermutationDomain VSPermutationVector;
|
|
VSPermutationVector.Set< Strata::FStrataTilePassVS::FEnableDebug >(false);
|
|
VSPermutationVector.Set< Strata::FStrataTilePassVS::FEnableTexCoordScreenVector >(true);
|
|
TShaderMapRef<Strata::FStrataTilePassVS> TileVertexShader(View.ShaderMap, VSPermutationVector);
|
|
|
|
Strata::FStrataTilePassVS::FParameters VSParameters;
|
|
if (Strata::IsStrataEnabled())
|
|
{
|
|
VSParameters = Strata::SetTileParameters(View, StrataTileMaterialType, GraphicsPSOInit.PrimitiveType);
|
|
}
|
|
|
|
// Turn DBT back off
|
|
GraphicsPSOInit.bDepthBounds = false;
|
|
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = bEnableStrataTiledPass ? TileVertexShader.GetVertexShader() : VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0x0);
|
|
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
|
|
if (StrataTileMaterialType != ECount)
|
|
{
|
|
check(Strata::IsStrataEnabled());
|
|
SetShaderParameters(RHICmdList, TileVertexShader, TileVertexShader.GetVertexShader(), VSParameters);
|
|
RHICmdList.DrawPrimitiveIndirect(VSParameters.TileIndirectBuffer->GetIndirectRHICallBuffer(), Strata::TileTypeDrawIndirectArgOffset(StrataTileMaterialType));
|
|
}
|
|
else
|
|
{
|
|
FDeferredLightVS::FParameters VSParameters2 = FDeferredLightVS::GetParameters(View);
|
|
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), VSParameters2);
|
|
|
|
// 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(),
|
|
View.GetSceneTexturesConfig().Extent,
|
|
VertexShader,
|
|
EDRF_UseTriangleOptimization);
|
|
}
|
|
}
|
|
else // Radial light (LightType_Point, LightType_Spot, LightType_Rect)
|
|
{
|
|
// 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;
|
|
|
|
FDeferredLightVS::FPermutationDomain PermutationVectorVS;
|
|
PermutationVectorVS.Set<FDeferredLightVS::FRadialLight>(true);
|
|
TShaderMapRef<FDeferredLightVS> VertexShader(View.ShaderMap, PermutationVectorVS);
|
|
|
|
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();
|
|
|
|
const uint32 StencilRef = SetBoundingGeometryRasterizerAndDepthState(GraphicsPSOInit, View, bCameraInsideLightGeometry, StrataTileMaterialType);
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
|
|
FDeferredLightVS::FParameters VSParameters2 = FDeferredLightVS::GetParameters(View, LightSceneInfo);
|
|
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), VSParameters2);
|
|
|
|
// 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( LightType == LightType_Point || LightType == 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 (LightType == LightType_Spot)
|
|
{
|
|
StencilingGeometry::DrawCone(RHICmdList);
|
|
}
|
|
}
|
|
}); // RenderPass
|
|
}
|
|
|
|
|
|
/** Shader parameters for Standard Deferred Light Overlap Debug pass. */
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FRenderLightParameters, )
|
|
// PS/VS parameter structs
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightPS::FParameters, PS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightVS::FParameters, VS)
|
|
// Strata tiles
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FStrataTileParameter, StrataTileSimple)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FStrataTileParameter, StrataTileSingle)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FStrataTileParameter, StrataTileComplex)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
/** Shader parameters for Standard Deferred Light pass. */
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FRenderLightOverlapParameters, )
|
|
// PS/VS parameter structs
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightOverlapPS::FParameters, PS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightVS::FParameters, VS)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static void RenderLight(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FScene* Scene,
|
|
const FViewInfo& View,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
const FLightSceneInfo* LightSceneInfo,
|
|
FRDGTextureRef ScreenShadowMaskTexture,
|
|
FRDGTextureRef LightingChannelsTexture,
|
|
bool bRenderOverlap,
|
|
bool bCloudShadow)
|
|
{
|
|
// Ensure the light is valid for this view
|
|
if (!LightSceneInfo->ShouldRenderLight(View))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
|
|
INC_DWORD_STAT(STAT_NumLightsUsingStandardDeferred);
|
|
|
|
const FLightSceneProxy* RESTRICT LightProxy = LightSceneInfo->Proxy;
|
|
const bool bUseIESTexture = View.Family->EngineShowFlags.TexturedLightProfiles && (LightSceneInfo->Proxy->GetIESTextureResource() != 0);
|
|
const bool bTransmission = LightProxy->Transmission();
|
|
const FSphere LightBounds = LightProxy->GetBoundingSphere();
|
|
const ELightComponentType LightType = (ELightComponentType)LightProxy->GetLightType();
|
|
const bool bIsRadial = LightType != LightType_Directional;
|
|
const bool bSupportAnisotropyPermutation = ShouldRenderAnisotropyPass(View) && !Strata::IsStrataEnabled(); // Strata managed anisotropy differently than legacy path. No need for special permutation.
|
|
|
|
// Debug Overlap shader
|
|
if (bRenderOverlap)
|
|
{
|
|
FRenderLightOverlapParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightOverlapParameters>();
|
|
// PS - General parameters
|
|
PassParameters->PS.bHasValidChannel = LightSceneInfo->Proxy->GetPreviewShadowMapChannel() == INDEX_NONE ? 0.0f : 1.0f;
|
|
PassParameters->PS.View = View.ViewUniformBuffer;
|
|
PassParameters->PS.DeferredLight = CreateDeferredLightUniformBuffer(GraphBuilder, View, *LightSceneInfo);
|
|
PassParameters->PS.SceneTextures = SceneTextures.UniformBuffer;
|
|
PassParameters->PS.RenderTargets[0] = FRenderTargetBinding(SceneTextures.Color.Target, ERenderTargetLoadAction::ELoad);
|
|
if (SceneTextures.Depth.Target)
|
|
{
|
|
PassParameters->PS.RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite);
|
|
}
|
|
// VS - General parameters
|
|
if (bIsRadial)
|
|
{
|
|
PassParameters->VS = FDeferredLightVS::GetParameters(View, LightSceneInfo, false);
|
|
}
|
|
else
|
|
{
|
|
PassParameters->VS = FDeferredLightVS::GetParameters(View, false);
|
|
}
|
|
|
|
FDeferredLightOverlapPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FDeferredLightOverlapPS::FRadialAttenuation>(bIsRadial);
|
|
TShaderMapRef<FDeferredLightOverlapPS> PixelShader(View.ShaderMap, PermutationVector);
|
|
InternalRenderLight(GraphBuilder, Scene, View, LightSceneInfo, PixelShader, PassParameters, EStrataTileType::ECount, TEXT("Light::StandardDeferred(Overlap)"));
|
|
}
|
|
// Lighting shader
|
|
else
|
|
{
|
|
FRenderLightParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightParameters>();
|
|
// PS - Generatl parameters
|
|
PassParameters->PS = GetDeferredLightPSParameters(GraphBuilder, Scene, View, LightSceneInfo, SceneTextures.Color.Target, SceneTextures.Depth.Target, SceneTextures.UniformBuffer, View.HairStrandsViewData.UniformBuffer, ScreenShadowMaskTexture, LightingChannelsTexture, bCloudShadow);
|
|
// VS - General parameters
|
|
if (bIsRadial)
|
|
{
|
|
PassParameters->VS = FDeferredLightVS::GetParameters(View, LightSceneInfo, false);
|
|
}
|
|
else // Directional
|
|
{
|
|
PassParameters->VS = FDeferredLightVS::GetParameters(View, false);
|
|
}
|
|
// VS - Strata tile parameters
|
|
if (Strata::IsStrataEnabled())
|
|
{
|
|
// Note: we register all tile types here in order to have all resources tracked properly and being able
|
|
// to create a single pass parameters struct instead of created one for each tile types
|
|
PassParameters->StrataTileSimple = Strata::SetTileParameters(GraphBuilder, View, EStrataTileType::ESingle);
|
|
PassParameters->StrataTileSingle = Strata::SetTileParameters(GraphBuilder, View, EStrataTileType::ESimple);
|
|
PassParameters->StrataTileComplex = Strata::SetTileParameters(GraphBuilder, View, EStrataTileType::EComplex);
|
|
}
|
|
|
|
FDeferredLightPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >(bTransmission);
|
|
PermutationVector.Set< FDeferredLightPS::FHairLighting>(0);
|
|
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >(View.bUsesLightingChannels);
|
|
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >(View.Family->EngineShowFlags.VisualizeLightCulling);
|
|
PermutationVector.Set< FDeferredLightPS::FStrataTileType >(0);
|
|
if (bIsRadial)
|
|
{
|
|
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::FAnistropicMaterials >(bSupportAnisotropyPermutation && !LightSceneInfo->Proxy->IsRectLight());
|
|
PermutationVector.Set < FDeferredLightPS::FAtmosphereTransmittance >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(false);
|
|
}
|
|
else // Directional
|
|
{
|
|
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >(ELightSourceShape::Directional);
|
|
PermutationVector.Set< FDeferredLightPS::FSourceTextureDim >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FAnistropicMaterials >(bSupportAnisotropyPermutation);
|
|
// 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 >(IsLightAtmospherePerPixelTransmittanceEnabled(Scene, View, LightSceneInfo));
|
|
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(PassParameters->PS.CloudShadowEnabled > 0);
|
|
}
|
|
PermutationVector = FDeferredLightPS::RemapPermutation(PermutationVector);
|
|
|
|
// Strata tile rendering:
|
|
// * if the light is directional, then dispatch a set of rect tiles
|
|
// * if the light is radial/local, then dispatch a light geometry with stencil test. The stencil buffer has been prefilled with the tile result (simple/complex)
|
|
// so that the geometry get correctly stencil culled on complex/simple part of the screen
|
|
if (Strata::IsStrataEnabled())
|
|
{
|
|
// Complex tiles
|
|
{
|
|
const EStrataTileType TileType = EStrataTileType::EComplex;
|
|
PermutationVector.Set<FDeferredLightPS::FStrataTileType>(TileType);
|
|
TShaderMapRef< FDeferredLightPS > PixelShader(View.ShaderMap, PermutationVector);
|
|
InternalRenderLight(GraphBuilder, Scene, View, LightSceneInfo, PixelShader, PassParameters, TileType, TEXT("Light::StandardDeferred(Complex)"));
|
|
}
|
|
// Single tiles
|
|
{
|
|
const EStrataTileType TileType = EStrataTileType::ESingle;
|
|
PermutationVector.Set<FDeferredLightPS::FStrataTileType>(TileType);
|
|
TShaderMapRef< FDeferredLightPS > PixelShader(View.ShaderMap, PermutationVector);
|
|
InternalRenderLight(GraphBuilder, Scene, View, LightSceneInfo, PixelShader, PassParameters, TileType, TEXT("Light::StandardDeferred(Single)"));
|
|
}
|
|
// Simple tiles
|
|
{
|
|
const EStrataTileType TileType = EStrataTileType::ESimple;
|
|
PermutationVector.Set<FDeferredLightPS::FStrataTileType>(TileType);
|
|
TShaderMapRef< FDeferredLightPS > PixelShader(View.ShaderMap, PermutationVector);
|
|
InternalRenderLight(GraphBuilder, Scene, View, LightSceneInfo, PixelShader, PassParameters, TileType, TEXT("Light::StandardDeferred(Simple)"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PermutationVector.Set< FDeferredLightPS::FStrataTileType>(0);
|
|
TShaderMapRef< FDeferredLightPS > PixelShader(View.ShaderMap, PermutationVector);
|
|
InternalRenderLight(GraphBuilder, Scene, View, LightSceneInfo, PixelShader, PassParameters, EStrataTileType::ECount, TEXT("Light::StandardDeferred"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Shader parameters for Standard Deferred Light for HairStrands pass. */
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FRenderLightForHairParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightHairVS::FParameters, VS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightPS::FParameters, PS)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FDeferredShadingSceneRenderer::RenderLightForHair(
|
|
FRDGBuilder& GraphBuilder,
|
|
FViewInfo& View,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
const FLightSceneInfo* LightSceneInfo,
|
|
FRDGTextureRef HairShadowMaskTexture,
|
|
FRDGTextureRef LightingChannelsTexture,
|
|
const FHairStrandsTransmittanceMaskData& InTransmittanceMaskData,
|
|
const bool bForwardRendering)
|
|
{
|
|
// Ensure the light is valid for this view
|
|
const bool bHairRenderingEnabled = HairStrands::HasViewHairStrandsData(View);
|
|
if (!bHairRenderingEnabled || !LightSceneInfo->ShouldRenderLight(View) || View.HairStrandsViewData.VisibilityData.SampleLightingTexture == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Sanity check
|
|
check(InTransmittanceMaskData.TransmittanceMask);
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
|
|
INC_DWORD_STAT(STAT_NumLightsUsingStandardDeferred);
|
|
RDG_EVENT_SCOPE(GraphBuilder, "StandardDeferredLighting_Hair");
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
|
|
|
const bool bIsDirectional = LightSceneInfo->Proxy->GetLightType() == LightType_Directional;
|
|
const bool bCloudShadow = bIsDirectional;
|
|
|
|
FRenderLightForHairParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightForHairParameters>();
|
|
// VS - General parameters
|
|
PassParameters->VS.HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View);
|
|
// PS - General parameters
|
|
PassParameters->PS = GetDeferredLightPSParameters(
|
|
GraphBuilder,
|
|
Scene,
|
|
View,
|
|
LightSceneInfo,
|
|
SceneTextures.Color.Target,
|
|
SceneTextures.Depth.Target,
|
|
SceneTextures.UniformBuffer,
|
|
HairStrands::BindHairStrandsViewUniformParameters(View),
|
|
HairShadowMaskTexture,
|
|
LightingChannelsTexture,
|
|
bCloudShadow);
|
|
|
|
// PS - Hair parameters
|
|
const FIntPoint SampleLightingViewportResolution = View.HairStrandsViewData.VisibilityData.SampleLightingViewportResolution;
|
|
PassParameters->PS.HairTransmittanceBuffer = GraphBuilder.CreateSRV(InTransmittanceMaskData.TransmittanceMask, FHairStrandsTransmittanceMaskData::Format);
|
|
PassParameters->PS.HairTransmittanceBufferMaxCount = InTransmittanceMaskData.TransmittanceMask ? InTransmittanceMaskData.TransmittanceMask->Desc.NumElements : 0;
|
|
PassParameters->PS.ShadowChannelMask = FVector4f(1, 1, 1, 1);
|
|
if (HairShadowMaskTexture)
|
|
{
|
|
PassParameters->PS.ScreenShadowMaskSubPixelTexture = HairShadowMaskTexture;
|
|
PassParameters->PS.HairShadowMaskValid = true;
|
|
}
|
|
if (bForwardRendering)
|
|
{
|
|
PassParameters->PS.ShadowChannelMask = FVector4f(0, 0, 0, 0);
|
|
PassParameters->PS.ShadowChannelMask[FMath::Clamp(LightSceneInfo->GetDynamicShadowMapChannel(), 0, 3)] = 1.f;
|
|
}
|
|
PassParameters->PS.RenderTargets[0] = FRenderTargetBinding(View.HairStrandsViewData.VisibilityData.SampleLightingTexture, ERenderTargetLoadAction::ELoad);
|
|
PassParameters->PS.RenderTargets.DepthStencil = FDepthStencilBinding(nullptr, ERenderTargetLoadAction::ENoAction, ERenderTargetLoadAction::ENoAction, FExclusiveDepthStencil::DepthNop_StencilNop);
|
|
|
|
FDeferredLightPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >(View.bUsesLightingChannels);
|
|
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FHairLighting>(1);
|
|
if (bIsDirectional)
|
|
{
|
|
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >(ELightSourceShape::Directional);
|
|
PermutationVector.Set< FDeferredLightPS::FSourceTextureDim >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >(IsLightAtmospherePerPixelTransmittanceEnabled(Scene, View, LightSceneInfo));
|
|
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(PassParameters->PS.CloudShadowEnabled > 0.f);
|
|
}
|
|
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::FAtmosphereTransmittance >(false);
|
|
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(false);
|
|
}
|
|
|
|
TShaderMapRef<FDeferredLightHairVS> VertexShader(View.ShaderMap);
|
|
TShaderMapRef<FDeferredLightPS> PixelShader(View.ShaderMap, PermutationVector);
|
|
|
|
GraphBuilder.AddPass(
|
|
{},
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[this, VertexShader, PixelShader, PassParameters, SampleLightingViewportResolution](FRHICommandList& RHICmdList)
|
|
{
|
|
RHICmdList.SetViewport(0, 0, 0.0f, SampleLightingViewportResolution.X, SampleLightingViewportResolution.Y, 1.0f);
|
|
|
|
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;
|
|
|
|
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, 0);
|
|
|
|
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), PassParameters->VS);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
|
|
RHICmdList.SetStreamSource(0, nullptr, 0);
|
|
RHICmdList.DrawPrimitive(0, 1, 1);
|
|
});
|
|
}
|
|
|
|
// Forward lighting version for hair
|
|
void FDeferredShadingSceneRenderer::RenderLightsForHair(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
FSortedLightSetSceneInfo &SortedLightSet,
|
|
FRDGTextureRef ScreenShadowMaskSubPixelTexture,
|
|
FRDGTextureRef LightingChannelsTexture)
|
|
{
|
|
const TArray<FSortedLightSceneInfo, SceneRenderingAllocator> &SortedLights = SortedLightSet.SortedLights;
|
|
const int32 UnbatchedLightStart = SortedLightSet.UnbatchedLightStart;
|
|
const int32 SimpleLightsEnd = SortedLightSet.SimpleLightsEnd;
|
|
|
|
if (ActiveViewFamily->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 = UnbatchedLightStart; 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, true, ScreenShadowMaskSubPixelTexture);
|
|
}
|
|
|
|
RenderLightForHair(
|
|
GraphBuilder,
|
|
View,
|
|
SceneTextures,
|
|
&LightSceneInfo,
|
|
ScreenShadowMaskSubPixelTexture,
|
|
LightingChannelsTexture,
|
|
TransmittanceMaskData,
|
|
true /*bForwardRendering*/);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FSimpleLightsStandardDeferredParameters, )
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightPS::FParameters, PS)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FDeferredLightVS::FParameters, VS)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static FSimpleLightsStandardDeferredParameters GetRenderLightSimpleParameters(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FScene* Scene,
|
|
const FViewInfo& View,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
const FSimpleLightEntry& SimpleLight,
|
|
const FVector& SimpleLightPosition)
|
|
{
|
|
FSimpleLightsStandardDeferredParameters Out;
|
|
|
|
FRDGTextureRef WhiteDummy = GSystemTextures.GetWhiteDummy(GraphBuilder);
|
|
FRDGTextureRef DepthDummy = GSystemTextures.GetDepthDummy(GraphBuilder);
|
|
FRDGBufferRef BufferDummy = GSystemTextures.GetDefaultBuffer(GraphBuilder, 4, 0u);
|
|
FRDGBufferSRVRef BufferDummySRV = GraphBuilder.CreateSRV(BufferDummy, PF_R32_UINT);
|
|
|
|
// PS - General parameters
|
|
Out.PS.SceneTextures = SceneTextures.UniformBuffer;
|
|
Out.PS.HairStrands = View.HairStrandsViewData.UniformBuffer;
|
|
Out.PS.Strata = Strata::BindStrataGlobalUniformParameters(View);
|
|
Out.PS.LightingChannelsTexture = WhiteDummy;
|
|
Out.PS.LightingChannelsSampler = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Out.PS.CloudShadowAO = GetCloudShadowAOParameters(GraphBuilder, View, nullptr);
|
|
Out.PS.CloudShadowEnabled = 0;
|
|
SetupLightCloudTransmittanceParameters(GraphBuilder, nullptr, View, nullptr, Out.PS.CloudShadow);
|
|
Out.PS.LightAttenuationTexture = WhiteDummy;
|
|
Out.PS.LightAttenuationTextureSampler = TStaticSamplerState<SF_Point, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
|
|
Out.PS.DummyRectLightTextureForCapsuleCompilerWarning = DepthDummy;
|
|
Out.PS.DummyRectLightSamplerForCapsuleCompilerWarning = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Out.PS.IESTextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
Out.PS.IESTexture = GSystemTextures.WhiteDummy->GetRHI();
|
|
Out.PS.View = View.ViewUniformBuffer;
|
|
Out.PS.DeferredLight = CreateDeferredLightUniformBuffer(GraphBuilder, View, SimpleLight, SimpleLightPosition);
|
|
// PS - Hair (default)
|
|
Out.PS.ScreenShadowMaskSubPixelTexture = WhiteDummy;
|
|
Out.PS.HairTransmittanceBuffer = BufferDummySRV;
|
|
Out.PS.HairTransmittanceBufferMaxCount = 0;
|
|
Out.PS.HairShadowMaskValid = false;
|
|
Out.PS.ShadowChannelMask = FVector4f(1, 1, 1, 1);
|
|
// PS - RT/Depth
|
|
Out.PS.RenderTargets[0] = FRenderTargetBinding(SceneTextures.Color.Target, ERenderTargetLoadAction::ELoad);
|
|
if (SceneTextures.Depth.Target)
|
|
{
|
|
Out.PS.RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite);
|
|
}
|
|
|
|
// VS - General parameters (dummy geometry, as the geometry is setup within the pass light loop)
|
|
FSphere SphereLight;
|
|
SphereLight.Center = SimpleLightPosition; // Should we account for LWC Position+Tile here?
|
|
SphereLight.W = SimpleLight.Radius;
|
|
Out.VS = FDeferredLightVS::GetParameters(View, SphereLight, false);
|
|
|
|
return Out;
|
|
}
|
|
|
|
static void InternalRenderSimpleLightsStandardDeferred(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FScene* Scene,
|
|
const FViewInfo& View,
|
|
const uint32 ViewIndex,
|
|
const uint32 NumViews,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
const FSimpleLightArray& SimpleLights,
|
|
EStrataTileType TileType)
|
|
{
|
|
FSimpleLightsStandardDeferredParameters* PassParameters = GraphBuilder.AllocParameters<FSimpleLightsStandardDeferredParameters>();
|
|
*PassParameters = GetRenderLightSimpleParameters(
|
|
GraphBuilder,
|
|
Scene,
|
|
View,
|
|
SceneTextures,
|
|
SimpleLights.InstanceData[0], // Use a dummy light to create the PassParameter buffer. The light data will be
|
|
FVector(0, 0, 0)); // update dynamically with the pass light loop for efficiency purpose
|
|
|
|
FDeferredLightPS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >(ELightSourceShape::Capsule);
|
|
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >(false);
|
|
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::FStrataTileType>(TileType != EStrataTileType::ECount ? TileType : 0);
|
|
TShaderMapRef<FDeferredLightPS> PixelShader(View.ShaderMap, PermutationVector);
|
|
|
|
FDeferredLightVS::FPermutationDomain PermutationVectorVS;
|
|
PermutationVectorVS.Set<FDeferredLightVS::FRadialLight>(true);
|
|
TShaderMapRef<FDeferredLightVS> VertexShader(View.ShaderMap, PermutationVectorVS);
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("Light::DeferredSimpleLights(Strata:%s,Tile:%s)", Strata::IsStrataEnabled() ? TEXT("True") : TEXT("False"), Strata::IsStrataEnabled() ? ToString(TileType) : TEXT("None")),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[&View, &SimpleLights, ViewIndex, NumViews, PassParameters, PixelShader, VertexShader, TileType](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;
|
|
|
|
for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++)
|
|
{
|
|
const FSimpleLightEntry& SimpleLight = SimpleLights.InstanceData[LightIndex];
|
|
|
|
const FSimpleLightPerViewEntry& SimpleLightPerViewData = SimpleLights.GetViewDependentData(LightIndex, ViewIndex, NumViews);
|
|
const FSphere LightBounds(SimpleLightPerViewData.Position, SimpleLight.Radius);
|
|
|
|
|
|
// 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);
|
|
|
|
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();
|
|
|
|
const uint32 StencilRef = SetBoundingGeometryRasterizerAndDepthState(GraphicsPSOInit, View, bCameraInsideLightGeometry, TileType);
|
|
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
|
|
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
|
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
|
|
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
|
|
|
|
// Update the light parameters with a custom uniform buffer
|
|
FDeferredLightUniformStruct DeferredLightUniformsValue = GetSimpleDeferredLightParameters(View, SimpleLight, SimpleLightPerViewData);
|
|
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
|
|
SetUniformBufferParameterImmediate(RHICmdList, RHICmdList.GetBoundPixelShader(), PixelShader->GetUniformBufferParameter<FDeferredLightUniformStruct>(), DeferredLightUniformsValue);
|
|
|
|
// Update vertex shader parameters with custom parameters/uniform buffer
|
|
FDeferredLightVS::FParameters ParametersVS = FDeferredLightVS::GetParameters(View, LightBounds);
|
|
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), ParametersVS /*PassParameters->VS*/);
|
|
|
|
// 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);
|
|
}
|
|
});
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderSimpleLightsStandardDeferred(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FMinimalSceneTextures& SceneTextures,
|
|
const FSimpleLightArray& SimpleLights)
|
|
{
|
|
if (SimpleLights.InstanceData.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
|
|
INC_DWORD_STAT_BY(STAT_NumLightsUsingStandardDeferred, SimpleLights.InstanceData.Num());
|
|
|
|
for (int32 ViewIndex = 0, NumViews = Views.Num(); ViewIndex < NumViews; ViewIndex++)
|
|
{
|
|
const FViewInfo& View = Views[ViewIndex];
|
|
|
|
if (Strata::IsStrataEnabled())
|
|
{
|
|
InternalRenderSimpleLightsStandardDeferred(GraphBuilder, Scene, View, ViewIndex, NumViews, SceneTextures, SimpleLights, EStrataTileType::EComplex);
|
|
InternalRenderSimpleLightsStandardDeferred(GraphBuilder, Scene, View, ViewIndex, NumViews, SceneTextures, SimpleLights, EStrataTileType::ESingle);
|
|
InternalRenderSimpleLightsStandardDeferred(GraphBuilder, Scene, View, ViewIndex, NumViews, SceneTextures, SimpleLights, EStrataTileType::ESimple);
|
|
}
|
|
else
|
|
{
|
|
InternalRenderSimpleLightsStandardDeferred(GraphBuilder, Scene, View, ViewIndex, NumViews, SceneTextures, SimpleLights, EStrataTileType::ECount);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|