You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1033 lines
40 KiB
C++
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);
|
|
}
|
|
}
|
|
}
|