// 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,TEXT("DeferredLightVertexShaders"),TEXT("DirectionalVertexMain"),SF_Vertex); IMPLEMENT_SHADER_TYPE(template<>,TDeferredLightVS,TEXT("DeferredLightVertexShaders"),TEXT("RadialVertexMain"),SF_Vertex); /** A pixel shader for rendering the light in a deferred pass. */ template 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(), 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(), 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::GetRHI(), GSceneRenderTargets.GetEffectiveLightAttenuationTexture(true) ); } SetTextureParameter( RHICmdList, ShaderRHI, PreIntegratedBRDF, PreIntegratedBRDFSampler, TStaticSamplerState::GetRHI(), GEngine->PreIntegratedSkinBRDFTexture->Resource->TextureRHI ); { FTextureRHIParamRef TextureRHI = IESTextureResource ? IESTextureResource->TextureRHI : GSystemTextures.WhiteDummy->GetRenderTargetItem().TargetableTexture; SetTextureParameter( RHICmdList, ShaderRHI, IESTexture, IESTextureSampler, TStaticSamplerState::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 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 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(), 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, TEXT("StationaryLightOverlapShaders"), TEXT("OverlapRadialPixelMain"), SF_Pixel); IMPLEMENT_SHADER_TYPE(template<>, TDeferredLightOverlapPS, 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& Views, FSimpleLightArray& SimpleLights) { TArray 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 SortedLights; SortedLights.Empty(Scene->Lights.Num()); bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows && GetShadowQuality() > 0; // Build a list of visible lights. for (TSparseArray::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& LightArray) { for (TSparseArray::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::GetRHI() : TStaticRasterizerState::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::GetRHI() : TStaticRasterizerState::GetRHI()); } RHICmdList.SetDepthStencilState( bCameraInsideLightGeometry ? TStaticDepthStencilState::GetRHI() : TStaticDepthStencilState::GetRHI() ); } template static FVertexDeclarationRHIParamRef GetDeferredLightingVertexDeclaration() { return bRadialAttenuation ? GetVertexDeclarationFVector4() : GFilterVertexDeclaration.VertexDeclarationRHI; } template static void SetShaderTemplLighting( FRHICommandList& RHICmdList, const FViewInfo& View, FShader* VertexShader, const FLightSceneInfo* LightSceneInfo) { if(View.Family->EngineShowFlags.VisualizeLightCulling) { TShaderMapRef > PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration(), VertexShader, *PixelShader); PixelShader->SetParameters(RHICmdList, View, LightSceneInfo); } else { TShaderMapRef > PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration(), VertexShader, *PixelShader); PixelShader->SetParameters(RHICmdList, View, LightSceneInfo); } } template static void SetShaderTemplLightingSimple( FRHICommandList& RHICmdList, const FViewInfo& View, FShader* VertexShader, const FSimpleLightEntry& SimpleLight, const FSimpleLightPerViewEntry& SimpleLightPerViewData) { if(View.Family->EngineShowFlags.VisualizeLightCulling) { TShaderMapRef > PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration(), VertexShader, *PixelShader); PixelShader->SetParametersSimpleLight(RHICmdList, View, SimpleLight, SimpleLightPerViewData); } else { TShaderMapRef > PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration(), 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::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 > VertexShader(View.ShaderMap); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); if (bRenderOverlap) { TShaderMapRef > PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, FeatureLevel, PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration(), *VertexShader, *PixelShader); PixelShader->SetParameters(RHICmdList, View, LightSceneInfo); } else { if(bUseIESTexture) { if (bClearCoatNeeded) { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } else { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } } else { if (bClearCoatNeeded) { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } else { SetShaderTemplLighting(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 > VertexShader(View.ShaderMap); SetBoundingGeometryRasterizerAndDepthState(RHICmdList, View, LightBounds); if (bRenderOverlap) { TShaderMapRef > PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, FeatureLevel, PixelShader->GetBoundShaderState(), GetDeferredLightingVertexDeclaration(), *VertexShader, *PixelShader); PixelShader->SetParameters(RHICmdList, View, LightSceneInfo); } else { if( LightSceneInfo->Proxy->IsInverseSquared() ) { if(bUseIESTexture) { if (bClearCoatNeeded) { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } else { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } } else { if (bClearCoatNeeded) { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } else { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } } } else { if(bUseIESTexture) { if (bClearCoatNeeded) { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } else { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } } else { if (bClearCoatNeeded) { SetShaderTemplLighting(RHICmdList, View, *VertexShader, LightSceneInfo); } else { SetShaderTemplLighting(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::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 > VertexShader(View.ShaderMap); SetBoundingGeometryRasterizerAndDepthState(RHICmdList, View, LightBounds); bool bClearCoatNeeded = (View.ShadingModelMaskInView & (1 << MSM_ClearCoat)) != 0; if (SimpleLight.Exponent == 0) { // inverse squared if (bClearCoatNeeded) { SetShaderTemplLightingSimple(RHICmdList, View, *VertexShader, SimpleLight, SimpleLightPerViewData); } else { SetShaderTemplLightingSimple(RHICmdList, View, *VertexShader, SimpleLight, SimpleLightPerViewData); } } else { // light's exponent, not inverse squared if (bClearCoatNeeded) { SetShaderTemplLightingSimple(RHICmdList, View, *VertexShader, SimpleLight, SimpleLightPerViewData); } else { SetShaderTemplLightingSimple(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); } } }