Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/LightRendering.cpp
Daniel Wright f2b2d0e701 Clamp light depth bounds to be valid
[CL 2520168 by Daniel Wright in Main branch]
2015-04-21 18:02:27 -04:00

1033 lines
40 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LightRendering.cpp: Light rendering implementation.
=============================================================================*/
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "SceneFilterRendering.h"
#include "UniformBuffer.h"
#include "ShaderParameters.h"
#include "LightRendering.h"
#include "LightPropagationVolume.h"
#include "SceneUtils.h"
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FDeferredLightUniformStruct,TEXT("DeferredLightUniforms"));
extern int32 GUseTranslucentLightingVolumes;
static int32 bAllowDepthBoundsTest = 1;
static FAutoConsoleVariableRef CVarAllowDepthBoundsTest(
TEXT("r.AllowDepthBoundsTest"),
bAllowDepthBoundsTest,
TEXT("If true, use enable depth bounds test when rendering defered lights.")
);
// Implement a version for directional lights, and a version for point / spot lights
IMPLEMENT_SHADER_TYPE(template<>,TDeferredLightVS<false>,TEXT("DeferredLightVertexShaders"),TEXT("DirectionalVertexMain"),SF_Vertex);
IMPLEMENT_SHADER_TYPE(template<>,TDeferredLightVS<true>,TEXT("DeferredLightVertexShaders"),TEXT("RadialVertexMain"),SF_Vertex);
/** A pixel shader for rendering the light in a deferred pass. */
template<bool bUseIESProfile, bool bRadialAttenuation, bool bInverseSquaredFalloff, bool bVisualizeLightCulling, bool bUseClearCoat>
class TDeferredLightPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(TDeferredLightPS,Global)
public:
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
OutEnvironment.SetDefine(TEXT("USE_IES_PROFILE"), (uint32)bUseIESProfile);
OutEnvironment.SetDefine(TEXT("RADIAL_ATTENUATION"), (uint32)bRadialAttenuation);
OutEnvironment.SetDefine(TEXT("INVERSE_SQUARED_FALLOFF"), (uint32)bInverseSquaredFalloff);
OutEnvironment.SetDefine(TEXT("LIGHT_SOURCE_SHAPE"), 1);
OutEnvironment.SetDefine(TEXT("VISUALIZE_LIGHT_CULLING"), (uint32)bVisualizeLightCulling);
OutEnvironment.SetDefine(TEXT("USE_CLEARCOAT"), (uint32)bUseClearCoat);
}
TDeferredLightPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
DeferredParameters.Bind(Initializer.ParameterMap);
LightAttenuationTexture.Bind(Initializer.ParameterMap, TEXT("LightAttenuationTexture"));
LightAttenuationTextureSampler.Bind(Initializer.ParameterMap, TEXT("LightAttenuationTextureSampler"));
PreIntegratedBRDF.Bind(Initializer.ParameterMap, TEXT("PreIntegratedBRDF"));
PreIntegratedBRDFSampler.Bind(Initializer.ParameterMap, TEXT("PreIntegratedBRDFSampler"));
IESTexture.Bind(Initializer.ParameterMap, TEXT("IESTexture"));
IESTextureSampler.Bind(Initializer.ParameterMap, TEXT("IESTextureSampler"));
}
TDeferredLightPS()
{
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, const FLightSceneInfo* LightSceneInfo)
{
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
SetParametersBase(RHICmdList, ShaderRHI, View, LightSceneInfo->Proxy->GetIESTextureResource());
SetDeferredLightParameters(RHICmdList, ShaderRHI, GetUniformBufferParameter<FDeferredLightUniformStruct>(), LightSceneInfo, View);
}
void SetParametersSimpleLight(FRHICommandList& RHICmdList, const FSceneView& View, const FSimpleLightEntry& SimpleLight, const FSimpleLightPerViewEntry& SimpleLightPerViewData)
{
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
SetParametersBase(RHICmdList, ShaderRHI, View, NULL);
SetSimpleDeferredLightParameters(RHICmdList, ShaderRHI, GetUniformBufferParameter<FDeferredLightUniformStruct>(), SimpleLight, SimpleLightPerViewData, View);
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << DeferredParameters;
Ar << LightAttenuationTexture;
Ar << LightAttenuationTextureSampler;
Ar << PreIntegratedBRDF;
Ar << PreIntegratedBRDFSampler;
Ar << IESTexture;
Ar << IESTextureSampler;
return bShaderHasOutdatedParameters;
}
FGlobalBoundShaderState& GetBoundShaderState()
{
static FGlobalBoundShaderState State;
return State;
}
private:
void SetParametersBase(FRHICommandList& RHICmdList, const FPixelShaderRHIParamRef ShaderRHI, const FSceneView& View, FTexture* IESTextureResource)
{
FGlobalShader::SetParameters(RHICmdList, ShaderRHI,View);
DeferredParameters.Set(RHICmdList, ShaderRHI, View);
if(LightAttenuationTexture.IsBound())
{
SetTextureParameter(
RHICmdList,
ShaderRHI,
LightAttenuationTexture,
LightAttenuationTextureSampler,
TStaticSamplerState<SF_Point,AM_Wrap,AM_Wrap,AM_Wrap>::GetRHI(),
GSceneRenderTargets.GetEffectiveLightAttenuationTexture(true)
);
}
SetTextureParameter(
RHICmdList,
ShaderRHI,
PreIntegratedBRDF,
PreIntegratedBRDFSampler,
TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
GEngine->PreIntegratedSkinBRDFTexture->Resource->TextureRHI
);
{
FTextureRHIParamRef TextureRHI = IESTextureResource ? IESTextureResource->TextureRHI : GSystemTextures.WhiteDummy->GetRenderTargetItem().TargetableTexture;
SetTextureParameter(
RHICmdList,
ShaderRHI,
IESTexture,
IESTextureSampler,
TStaticSamplerState<SF_Bilinear,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
TextureRHI
);
}
}
FDeferredPixelShaderParameters DeferredParameters;
FShaderResourceParameter LightAttenuationTexture;
FShaderResourceParameter LightAttenuationTextureSampler;
FShaderResourceParameter PreIntegratedBRDF;
FShaderResourceParameter PreIntegratedBRDFSampler;
FShaderResourceParameter IESTexture;
FShaderResourceParameter IESTextureSampler;
};
// Typedef is necessary because the C preprocessor thinks the comma in the template parameter list is a comma in the macro parameter list.
#define IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(A, B, C, D, E, EntryName) \
typedef TDeferredLightPS<A,B,C,D,E> TDeferredLightPS##A##B##C##D##E; \
IMPLEMENT_SHADER_TYPE(template<>,TDeferredLightPS##A##B##C##D##E,TEXT("DeferredLightPixelShaders"),EntryName,SF_Pixel);
// Implement a version for each light type, and it's shader permutations
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(true, true, true, false, false, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(true, true, false, false, false, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(true, false, false, false, false, TEXT("DirectionalPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, true, true, false, false, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, true, false, false, false, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, false, false, false, false, TEXT("DirectionalPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, false, false, true, false, TEXT("DirectionalPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, true, false, true, false, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(true, true, true, false, true, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(true, true, false, false, true, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(true, false, false, false, true, TEXT("DirectionalPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, true, true, false, true, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, true, false, false, true, TEXT("RadialPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, false, false, false, true, TEXT("DirectionalPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, false, false, true, true, TEXT("DirectionalPixelMain"));
IMPLEMENT_DEFERREDLIGHT_PIXELSHADER_TYPE(false, true, false, true, true, TEXT("RadialPixelMain"));
/** Shader used to visualize stationary light overlap. */
template<bool bRadialAttenuation>
class TDeferredLightOverlapPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(TDeferredLightOverlapPS,Global)
public:
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
OutEnvironment.SetDefine(TEXT("RADIAL_ATTENUATION"), (uint32)bRadialAttenuation);
}
TDeferredLightOverlapPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
HasValidChannel.Bind(Initializer.ParameterMap, TEXT("HasValidChannel"));
DeferredParameters.Bind(Initializer.ParameterMap);
}
TDeferredLightOverlapPS()
{
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, const FLightSceneInfo* LightSceneInfo)
{
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
FGlobalShader::SetParameters(RHICmdList, ShaderRHI,View);
const float HasValidChannelValue = LightSceneInfo->Proxy->GetPreviewShadowMapChannel() == INDEX_NONE ? 0.0f : 1.0f;
SetShaderValue(RHICmdList, ShaderRHI, HasValidChannel, HasValidChannelValue);
DeferredParameters.Set(RHICmdList, ShaderRHI, View);
SetDeferredLightParameters(RHICmdList, ShaderRHI, GetUniformBufferParameter<FDeferredLightUniformStruct>(), LightSceneInfo, View);
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << HasValidChannel;
Ar << DeferredParameters;
return bShaderHasOutdatedParameters;
}
FGlobalBoundShaderState& GetBoundShaderState()
{
static FGlobalBoundShaderState State;
return State;
}
private:
FShaderParameter HasValidChannel;
FDeferredPixelShaderParameters DeferredParameters;
};
IMPLEMENT_SHADER_TYPE(template<>, TDeferredLightOverlapPS<true>, TEXT("StationaryLightOverlapShaders"), TEXT("OverlapRadialPixelMain"), SF_Pixel);
IMPLEMENT_SHADER_TYPE(template<>, TDeferredLightOverlapPS<false>, TEXT("StationaryLightOverlapShaders"), TEXT("OverlapDirectionalPixelMain"), SF_Pixel);
/** Gathers simple lights from visible primtives in the passed in views. */
void FSceneRenderer::GatherSimpleLights(const FSceneViewFamily& ViewFamily, const TArray<FViewInfo>& Views, FSimpleLightArray& SimpleLights)
{
TArray<const FPrimitiveSceneInfo*, SceneRenderingAllocator> PrimitivesWithSimpleLights;
// Gather visible primitives from all views that might have simple lights
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
for (int32 PrimitiveIndex = 0; PrimitiveIndex < View.VisibleDynamicPrimitives.Num(); PrimitiveIndex++)
{
const FPrimitiveSceneInfo* PrimitiveSceneInfo = View.VisibleDynamicPrimitives[PrimitiveIndex];
const int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
const FPrimitiveViewRelevance& PrimitiveViewRelevance = View.PrimitiveViewRelevanceMap[PrimitiveId];
if (PrimitiveViewRelevance.bHasSimpleLights)
{
// 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 (GEmitDrawEvents)
{
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 = FullLevelName.Mid(LastSlashIndex + 1, FullLevelName.Len() - (LastSlashIndex + 1));
}
LightNameWithLevel = FullLevelName + TEXT(".") + LightProxy->GetComponentName().ToString();
}
#endif
}
uint32 GetShadowQuality();
/** Renders the scene's lighting. */
void FDeferredShadingSceneRenderer::RenderLights(FRHICommandListImmediate& RHICmdList)
{
SCOPED_DRAW_EVENT(RHICmdList, Lights);
if(IsSimpleDynamicLightingEnabled())
{
return;
}
bool bStencilBufferDirty = false; // The stencil buffer should've been cleared to 0 already
SCOPE_CYCLE_COUNTER(STAT_LightingDrawTime);
SCOPE_CYCLE_COUNTER(STAT_LightRendering);
FSimpleLightArray SimpleLights;
GatherSimpleLights(ViewFamily, Views, SimpleLights);
TArray<FSortedLightSceneInfo, SceneRenderingAllocator> SortedLights;
SortedLights.Empty(Scene->Lights.Num());
bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows && GetShadowQuality() > 0;
// Build a list of visible lights.
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
const FLightSceneInfo* const LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
if (LightSceneInfo->ShouldRenderLightViewIndependent()
// Reflection override skips direct specular because it tends to be blindingly bright with a perfectly smooth surface
&& !ViewFamily.EngineShowFlags.ReflectionOverride)
{
// Check if the light is visible in any of the views.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (LightSceneInfo->ShouldRenderLight(Views[ViewIndex]))
{
FSortedLightSceneInfo* SortedLightInfo = new(SortedLights) FSortedLightSceneInfo(LightSceneInfoCompact);
// Check for shadows and light functions.
SortedLightInfo->SortKey.Fields.LightType = LightSceneInfoCompact.LightType;
SortedLightInfo->SortKey.Fields.bTextureProfile = ViewFamily.EngineShowFlags.TexturedLightProfiles && LightSceneInfo->Proxy->GetIESTextureResource();
SortedLightInfo->SortKey.Fields.bShadowed = bDynamicShadows && CheckForProjectedShadows(LightSceneInfo);
SortedLightInfo->SortKey.Fields.bLightFunction = ViewFamily.EngineShowFlags.LightFunctions && CheckForLightFunction(LightSceneInfo);
break;
}
}
}
}
// 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() );
{
SCOPED_DRAW_EVENT(RHICmdList, DirectLighting);
int32 AttenuationLightStart = SortedLights.Num();
int32 SupportedByTiledDeferredLightEnd = SortedLights.Num();
bool bAnyUnsupportedByTiledDeferred = false;
// 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];
bool bDrawShadows = SortedLightInfo.SortKey.Fields.bShadowed;
bool bDrawLightFunction = SortedLightInfo.SortKey.Fields.bLightFunction;
bool bTextureLightProfile = SortedLightInfo.SortKey.Fields.bTextureProfile;
if (bTextureLightProfile && SupportedByTiledDeferredLightEnd == SortedLights.Num())
{
// Mark the first index to not support tiled deferred due to texture light profile
SupportedByTiledDeferredLightEnd = LightIndex;
}
if( bDrawShadows || bDrawLightFunction )
{
AttenuationLightStart = LightIndex;
if (SupportedByTiledDeferredLightEnd == SortedLights.Num())
{
// Mark the first index to not support tiled deferred due to shadowing
SupportedByTiledDeferredLightEnd = LightIndex;
}
break;
}
if (LightIndex < SupportedByTiledDeferredLightEnd)
{
// Directional lights currently not supported by tiled deferred
bAnyUnsupportedByTiledDeferred = bAnyUnsupportedByTiledDeferred
|| (SortedLightInfo.SortKey.Fields.LightType != LightType_Point && SortedLightInfo.SortKey.Fields.LightType != LightType_Spot);
}
}
if(ViewFamily.EngineShowFlags.DirectLighting)
{
SCOPED_DRAW_EVENT(RHICmdList, NonShadowedLights);
INC_DWORD_STAT_BY(STAT_NumUnshadowedLights, AttenuationLightStart);
GSceneRenderTargets.SetLightAttenuationMode(false);
int32 StandardDeferredStart = 0;
if (CanUseTiledDeferred())
{
int32 NumSortedLightsTiledDeferred = SupportedByTiledDeferredLightEnd;
bool bAnyViewIsStereo = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
if (Views[ViewIndex].StereoPass != eSSP_FULL)
{
bAnyViewIsStereo = true;
break;
}
}
// Use tiled deferred shading on any unshadowed lights without a texture light profile
if (!ShouldUseTiledDeferred(SupportedByTiledDeferredLightEnd, SimpleLights.InstanceData.Num()) || bAnyUnsupportedByTiledDeferred || bAnyViewIsStereo)
{
NumSortedLightsTiledDeferred = 0;
}
if (NumSortedLightsTiledDeferred > 0 || SimpleLights.InstanceData.Num() > 0)
{
// Update the range that needs to be processed by standard deferred to exclude the lights done with tiled
StandardDeferredStart = NumSortedLightsTiledDeferred;
RenderTiledDeferredLighting(RHICmdList, SortedLights, NumSortedLightsTiledDeferred, SimpleLights);
}
}
else if (SimpleLights.InstanceData.Num() > 0)
{
GSceneRenderTargets.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite);
RenderSimpleLightsStandardDeferred(RHICmdList, SimpleLights);
}
{
SCOPED_DRAW_EVENT(RHICmdList, StandardDeferredLighting);
// make sure we don't clear the depth
GSceneRenderTargets.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite);
// Draw non-shadowed non-light function lights without changing render targets between them
for (int32 LightIndex = StandardDeferredStart; LightIndex < AttenuationLightStart; LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const FLightSceneInfoCompact& LightSceneInfoCompact = SortedLightInfo.SceneInfo;
const FLightSceneInfo* const LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
// Render the light to the scene color buffer, using a 1x1 white texture as input
RenderLight(RHICmdList, LightSceneInfo, false, false);
}
}
if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering)
{
if (AttenuationLightStart)
{
// Inject non-shadowed, non-light function lights in to the volume.
SCOPED_DRAW_EVENT(RHICmdList, InjectNonShadowedTranslucentLighting);
InjectTranslucentVolumeLightingArray(RHICmdList, SortedLights, AttenuationLightStart);
}
if (SimpleLights.InstanceData.Num() > 0)
{
SCOPED_DRAW_EVENT(RHICmdList, InjectSimpleLightsTranslucentLighting);
InjectSimpleTranslucentVolumeLightingArray(RHICmdList, SimpleLights);
}
}
}
bool bDirectLighting = ViewFamily.EngineShowFlags.DirectLighting;
// Draw shadowed and light function lights
for (int32 LightIndex = AttenuationLightStart; LightIndex < SortedLights.Num(); LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const FLightSceneInfoCompact& LightSceneInfoCompact = SortedLightInfo.SceneInfo;
const FLightSceneInfo& LightSceneInfo = *LightSceneInfoCompact.LightSceneInfo;
bool bDrawShadows = SortedLightInfo.SortKey.Fields.bShadowed;
bool bDrawLightFunction = SortedLightInfo.SortKey.Fields.bLightFunction;
bool bInjectedTranslucentVolume = false;
bool bUsedLightAttenuation = false;
FScopeCycleCounter Context(LightSceneInfo.Proxy->GetStatId());
FString LightNameWithLevel;
GetLightNameForDrawEvent(LightSceneInfo.Proxy, LightNameWithLevel);
SCOPED_DRAW_EVENTF(RHICmdList, EventLightPass, *LightNameWithLevel);
// Do not resolve to scene color texture, this is done lazily
GSceneRenderTargets.FinishRenderingSceneColor(RHICmdList, false);
if (bDrawShadows)
{
INC_DWORD_STAT(STAT_NumShadowedLights);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
View.HeightfieldLightingViewInfo.ClearShadowing(View, RHICmdList, LightSceneInfo);
}
// All shadows render with min blending
bool bClearToWhite = true;
GSceneRenderTargets.BeginRenderingLightAttenuation(RHICmdList, bClearToWhite);
bool bRenderedTranslucentObjectShadows = RenderTranslucentProjectedShadows(RHICmdList, &LightSceneInfo );
// Render non-modulated projected shadows to the attenuation buffer.
RenderProjectedShadows(RHICmdList, &LightSceneInfo, bRenderedTranslucentObjectShadows, bInjectedTranslucentVolume );
bUsedLightAttenuation = true;
}
// Render any reflective shadow maps (if necessary)
if(ViewFamily.EngineShowFlags.GlobalIllumination && LightSceneInfo.Proxy->NeedsLPVInjection())
{
if ( LightSceneInfo.Proxy->HasReflectiveShadowMap() )
{
INC_DWORD_STAT(STAT_NumReflectiveShadowMapLights);
RenderReflectiveShadowMaps(RHICmdList, &LightSceneInfo );
}
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
View.HeightfieldLightingViewInfo.ComputeLighting(View, RHICmdList, LightSceneInfo);
}
// Render light function to the attenuation buffer.
if (bDirectLighting)
{
const bool bLightFunctionRendered = RenderLightFunction(RHICmdList, &LightSceneInfo, bDrawShadows);
bUsedLightAttenuation |= bLightFunctionRendered;
if (ViewFamily.EngineShowFlags.PreviewShadowsIndicator
&& !LightSceneInfo.IsPrecomputedLightingValid()
&& LightSceneInfo.Proxy->HasStaticShadowing())
{
RenderPreviewShadowsIndicator(RHICmdList, &LightSceneInfo, bUsedLightAttenuation);
}
if (!bDrawShadows)
{
INC_DWORD_STAT(STAT_NumLightFunctionOnlyLights);
}
}
if( bUsedLightAttenuation )
{
// Resolve light attenuation buffer
GSceneRenderTargets.FinishRenderingLightAttenuation(RHICmdList);
}
if(bDirectLighting && !bInjectedTranslucentVolume)
{
SCOPED_DRAW_EVENT(RHICmdList, InjectTranslucentVolume);
// Accumulate this light's unshadowed contribution to the translucency lighting volume
InjectTranslucentVolumeLighting(RHICmdList, LightSceneInfo, NULL);
}
GSceneRenderTargets.SetLightAttenuationMode(bUsedLightAttenuation);
GSceneRenderTargets.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite);
// Render the light to the scene color buffer, conditionally using the attenuation buffer or a 1x1 white texture as input
if(bDirectLighting)
{
RenderLight(RHICmdList, &LightSceneInfo, false, true);
}
}
// Do not resolve to scene color texture, this is done lazily
GSceneRenderTargets.FinishRenderingSceneColor(RHICmdList, false);
// Restore the default mode
GSceneRenderTargets.SetLightAttenuationMode(true);
// LPV Direct Light Injection
for (int32 LightIndex = 0; LightIndex < SortedLights.Num(); LightIndex++)
{
const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex];
const FLightSceneInfoCompact& LightSceneInfoCompact = SortedLightInfo.SceneInfo;
const FLightSceneInfo* const LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
// Render any reflective shadow maps (if necessary)
if ( LightSceneInfo && LightSceneInfo->Proxy && LightSceneInfo->Proxy->NeedsLPVInjection() )
{
if ( !LightSceneInfo->Proxy->HasReflectiveShadowMap() )
{
// Inject the light directly into all relevant LPVs
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
FSceneViewState* ViewState = (FSceneViewState*)View.State;
if (ViewState)
{
if (LightSceneInfo->ShouldRenderLight(View))
{
FLightPropagationVolume* Lpv = ViewState->GetLightPropagationVolume();
if (Lpv && LightSceneInfo->Proxy)
{
Lpv->InjectLightDirect(RHICmdList, *LightSceneInfo->Proxy, View);
}
}
}
}
}
}
}
}
}
void FDeferredShadingSceneRenderer::RenderLightArrayForOverlapViewmode(FRHICommandListImmediate& RHICmdList, const TSparseArray<FLightSceneInfoCompact>& LightArray)
{
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(LightArray); LightIt; ++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
const FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
// Nothing to do for black lights.
if(LightSceneInfoCompact.Color.IsAlmostBlack())
{
continue;
}
bool bShouldRender = false;
// Check if the light is visible in any of the views.
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
bShouldRender |= LightSceneInfo->ShouldRenderLight(Views[ViewIndex]);
}
if (bShouldRender
// Only render shadow casting stationary lights
&& LightSceneInfo->Proxy->HasStaticShadowing()
&& !LightSceneInfo->Proxy->HasStaticLighting()
&& LightSceneInfo->Proxy->CastsStaticShadow())
{
RenderLight(RHICmdList, LightSceneInfo, true, false);
}
}
}
void FDeferredShadingSceneRenderer::RenderStationaryLightOverlap(FRHICommandListImmediate& RHICmdList)
{
if (Scene->bIsEditorScene)
{
GSceneRenderTargets.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EUninitializedColorExistingDepth, FExclusiveDepthStencil::DepthRead_StencilWrite);
// Clear to discard base pass values in scene color since we didn't skip that, to have valid scene depths
RHICmdList.Clear(true, FLinearColor::Black, false, (float)ERHIZBuffer::FarPlane, false, 0, FIntRect());
RenderLightArrayForOverlapViewmode(RHICmdList, Scene->Lights);
//Note: making use of FScene::InvisibleLights, which contains lights that haven't been added to the scene in the same way as visible lights
// So code called by RenderLightArrayForOverlapViewmode must be careful what it accesses
RenderLightArrayForOverlapViewmode(RHICmdList, Scene->InvisibleLights);
}
}
/** Sets up rasterizer and depth state for rendering bounding geometry in a deferred pass. */
void SetBoundingGeometryRasterizerAndDepthState(FRHICommandList& RHICmdList, const FViewInfo& View, const FSphere& LightBounds)
{
const bool bCameraInsideLightGeometry = ((FVector)View.ViewMatrices.ViewOrigin - LightBounds.Center).SizeSquared() < FMath::Square(LightBounds.W * 1.05f + View.NearClippingDistance * 2.0f);
if (bCameraInsideLightGeometry)
{
// Render backfaces with depth tests disabled since the camera is inside (or close to inside) the light geometry
RHICmdList.SetRasterizerState(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
RHICmdList.SetRasterizerState(View.bReverseCulling ? TStaticRasterizerState<FM_Solid, CM_CCW>::GetRHI() : TStaticRasterizerState<FM_Solid, CM_CW>::GetRHI());
}
RHICmdList.SetDepthStencilState(
bCameraInsideLightGeometry
? TStaticDepthStencilState<false,CF_Always>::GetRHI()
: TStaticDepthStencilState<false,CF_DepthNearOrEqual>::GetRHI()
);
}
template <bool bRadialAttenuation>
static FVertexDeclarationRHIParamRef GetDeferredLightingVertexDeclaration()
{
return bRadialAttenuation ? GetVertexDeclarationFVector4() : GFilterVertexDeclaration.VertexDeclarationRHI;
}
template<bool bUseIESProfile, bool bRadialAttenuation, bool bInverseSquaredFalloff, bool bHandleClearCoat>
static void SetShaderTemplLighting(
FRHICommandList& RHICmdList,
const FViewInfo& View,
FShader* VertexShader,
const FLightSceneInfo* LightSceneInfo)
{
if(View.Family->EngineShowFlags.VisualizeLightCulling)
{
TShaderMapRef<TDeferredLightPS<false, bRadialAttenuation, false, true, bHandleClearCoat> > PixelShader(View.ShaderMap);
SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration<bRadialAttenuation>(), VertexShader, *PixelShader);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo);
}
else
{
TShaderMapRef<TDeferredLightPS<bUseIESProfile, bRadialAttenuation, bInverseSquaredFalloff, false, bHandleClearCoat> > PixelShader(View.ShaderMap);
SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration<bRadialAttenuation>(), VertexShader, *PixelShader);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo);
}
}
template<bool bUseIESProfile, bool bRadialAttenuation, bool bInverseSquaredFalloff, bool bHandleClearCoat>
static void SetShaderTemplLightingSimple(
FRHICommandList& RHICmdList,
const FViewInfo& View,
FShader* VertexShader,
const FSimpleLightEntry& SimpleLight,
const FSimpleLightPerViewEntry& SimpleLightPerViewData)
{
if(View.Family->EngineShowFlags.VisualizeLightCulling)
{
TShaderMapRef<TDeferredLightPS<false, bRadialAttenuation, false, true, bHandleClearCoat> > PixelShader(View.ShaderMap);
SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration<bRadialAttenuation>(), VertexShader, *PixelShader);
PixelShader->SetParametersSimpleLight(RHICmdList, View, SimpleLight, SimpleLightPerViewData);
}
else
{
TShaderMapRef<TDeferredLightPS<bUseIESProfile, bRadialAttenuation, bInverseSquaredFalloff, false, bHandleClearCoat> > PixelShader(View.ShaderMap);
SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration<bRadialAttenuation>(), VertexShader, *PixelShader);
PixelShader->SetParametersSimpleLight(RHICmdList, View, SimpleLight, SimpleLightPerViewData);
}
}
// Use DBT to allow work culling on shadow lights
void CalculateLightNearFarDepthFromBounds(const FViewInfo& View, const FSphere &LightBounds, float &NearDepth, float &FarDepth)
{
const FMatrix ViewProjection = View.ViewMatrices.GetViewProjMatrix();
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);
}
/**
* Used by RenderLights to render a light to the scene color buffer.
*
* @param LightSceneInfo Represents the current light
* @param LightIndex The light's index into FScene::Lights
* @return true if anything got rendered
*/
void FDeferredShadingSceneRenderer::RenderLight(FRHICommandList& RHICmdList, const FLightSceneInfo* LightSceneInfo, bool bRenderOverlap, bool bIssueDrawEvent)
{
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
INC_DWORD_STAT(STAT_NumLightsUsingStandardDeferred);
SCOPED_CONDITIONAL_DRAW_EVENT(RHICmdList, StandardDeferredLighting, bIssueDrawEvent);
// Use additive blending for color
RHICmdList.SetBlendState(TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI());
bool bStencilDirty = false;
const FSphere LightBounds = LightSceneInfo->Proxy->GetBoundingSphere();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
bool bUseIESTexture = false;
if(View.Family->EngineShowFlags.TexturedLightProfiles)
{
bUseIESTexture = (LightSceneInfo->Proxy->GetIESTextureResource() != 0);
}
// Set the device viewport for the view.
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
bool bClearCoatNeeded = (View.ShadingModelMaskInView & (1 << MSM_ClearCoat)) != 0;
if (LightSceneInfo->Proxy->GetLightType() == LightType_Directional)
{
TShaderMapRef<TDeferredLightVS<false> > VertexShader(View.ShaderMap);
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
if (bRenderOverlap)
{
TShaderMapRef<TDeferredLightOverlapPS<false> > PixelShader(View.ShaderMap);
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration<false>(), *VertexShader, *PixelShader);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo);
}
else
{
if(bUseIESTexture)
{
if (bClearCoatNeeded)
{
SetShaderTemplLighting<true, false, false, true>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
else
{
SetShaderTemplLighting<true, false, false, false>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
}
else
{
if (bClearCoatNeeded)
{
SetShaderTemplLighting<false, false, false, true>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
else
{
SetShaderTemplLighting<false, false, false, true>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
}
}
VertexShader->SetParameters(RHICmdList, View, LightSceneInfo);
// Apply the directional light as a full screen quad
DrawRectangle(
RHICmdList,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Size(),
GSceneRenderTargets.GetBufferSizeXY(),
*VertexShader,
EDRF_UseTriangleOptimization);
}
else
{
TShaderMapRef<TDeferredLightVS<true> > VertexShader(View.ShaderMap);
SetBoundingGeometryRasterizerAndDepthState(RHICmdList, View, LightBounds);
if (bRenderOverlap)
{
TShaderMapRef<TDeferredLightOverlapPS<true> > PixelShader(View.ShaderMap);
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration<true>(), *VertexShader, *PixelShader);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo);
}
else
{
if( LightSceneInfo->Proxy->IsInverseSquared() )
{
if(bUseIESTexture)
{
if (bClearCoatNeeded)
{
SetShaderTemplLighting<true, true, true, true>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
else
{
SetShaderTemplLighting<true, true, true, false>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
}
else
{
if (bClearCoatNeeded)
{
SetShaderTemplLighting<false, true, true, true>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
else
{
SetShaderTemplLighting<false, true, true, false>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
}
}
else
{
if(bUseIESTexture)
{
if (bClearCoatNeeded)
{
SetShaderTemplLighting<true, true, false, true>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
else
{
SetShaderTemplLighting<true, true, false, false>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
}
else
{
if (bClearCoatNeeded)
{
SetShaderTemplLighting<false, true, false, true>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
else
{
SetShaderTemplLighting<false, true, false, false>(RHICmdList, View, *VertexShader, LightSceneInfo);
}
}
}
}
VertexShader->SetParameters(RHICmdList, View, LightSceneInfo);
// NUse DBT to allow work culling on shadow lights
if (bAllowDepthBoundsTest != 0)
{
// 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;
}
// UE4 uses reversed depth, so far < near
RHICmdList.EnableDepthBoundsTest(true,FarDepth,NearDepth);
}
if (LightSceneInfo->Proxy->GetLightType() == LightType_Point)
{
// 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);
}
else if (LightSceneInfo->Proxy->GetLightType() == LightType_Spot)
{
StencilingGeometry::DrawCone(RHICmdList);
}
// Use DBT to allow work culling on shadow lights
if (bAllowDepthBoundsTest != 0)
{
// Turn DBT back off
RHICmdList.EnableDepthBoundsTest(false, 0, 1);
}
}
}
if (bStencilDirty)
{
// Clear the stencil buffer to 0.
RHICmdList.Clear(false, FColor(0, 0, 0), false, (float)ERHIZBuffer::FarPlane, true, 0, FIntRect());
}
}
void FDeferredShadingSceneRenderer::RenderSimpleLightsStandardDeferred(FRHICommandListImmediate& RHICmdList, const FSimpleLightArray& SimpleLights)
{
SCOPE_CYCLE_COUNTER(STAT_DirectLightRenderingTime);
INC_DWORD_STAT_BY(STAT_NumLightsUsingStandardDeferred, SimpleLights.InstanceData.Num());
SCOPED_DRAW_EVENT(RHICmdList, StandardDeferredSimpleLights);
// Use additive blending for color
RHICmdList.SetBlendState(TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI());
const int32 NumViews = Views.Num();
for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++)
{
const FSimpleLightEntry& SimpleLight = SimpleLights.InstanceData[LightIndex];
for (int32 ViewIndex = 0; ViewIndex < NumViews; ViewIndex++)
{
const FSimpleLightPerViewEntry& SimpleLightPerViewData = SimpleLights.GetViewDependentData(LightIndex, ViewIndex, NumViews);
const FSphere LightBounds(SimpleLightPerViewData.Position, SimpleLight.Radius);
FViewInfo& View = Views[ViewIndex];
// Set the device viewport for the view.
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
TShaderMapRef<TDeferredLightVS<true> > VertexShader(View.ShaderMap);
SetBoundingGeometryRasterizerAndDepthState(RHICmdList, View, LightBounds);
bool bClearCoatNeeded = (View.ShadingModelMaskInView & (1 << MSM_ClearCoat)) != 0;
if (SimpleLight.Exponent == 0)
{
// inverse squared
if (bClearCoatNeeded)
{
SetShaderTemplLightingSimple<false, true, true, true>(RHICmdList, View, *VertexShader, SimpleLight, SimpleLightPerViewData);
}
else
{
SetShaderTemplLightingSimple<false, true, true, false>(RHICmdList, View, *VertexShader, SimpleLight, SimpleLightPerViewData);
}
}
else
{
// light's exponent, not inverse squared
if (bClearCoatNeeded)
{
SetShaderTemplLightingSimple<false, true, false, true>(RHICmdList, View, *VertexShader, SimpleLight, SimpleLightPerViewData);
}
else
{
SetShaderTemplLightingSimple<false, true, false, false>(RHICmdList, View, *VertexShader, SimpleLight, SimpleLightPerViewData);
}
}
VertexShader->SetSimpleLightParameters(RHICmdList, View, LightBounds);
// Apply the point or spot light with some approximately bounding geometry,
// So we can get speedups from depth testing and not processing pixels outside of the light's influence.
StencilingGeometry::DrawSphere(RHICmdList);
}
}
}