Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/SceneCore.cpp
Ola Olsson d08db2a87a Revert inset shadow logic to match UE4 (leftover from Reverb)
#rb andrew.lauritzen
#preflight 609e2417423c960001d4c98a

[CL 16327379 by Ola Olsson in ue5-main branch]
2021-05-14 04:01:45 -04:00

397 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
SceneCore.cpp: Core scene implementation.
=============================================================================*/
#include "SceneCore.h"
#include "SceneInterface.h"
#include "SceneManagement.h"
#include "Misc/ConfigCacheIni.h"
#include "Components/ExponentialHeightFogComponent.h"
#include "DepthRendering.h"
#include "SceneHitProxyRendering.h"
#include "VelocityRendering.h"
#include "BasePassRendering.h"
#include "MobileBasePassRendering.h"
#include "RendererModule.h"
#include "ScenePrivate.h"
#include "MaterialShared.h"
#include "HAL/LowLevelMemTracker.h"
int32 GUnbuiltPreviewShadowsInGame = 1;
FAutoConsoleVariableRef CVarUnbuiltPreviewShadowsInGame(
TEXT("r.Shadow.UnbuiltPreviewInGame"),
GUnbuiltPreviewShadowsInGame,
TEXT("Whether to render unbuilt preview shadows in game. When enabled and lighting is not built, expensive preview shadows will be rendered in game. When disabled, lighting in game and editor won't match which can appear to be a bug."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static int64 GAllocateLightPrimitiveInteractionSize = 0;
uint32 FRendererModule::GetNumDynamicLightsAffectingPrimitive(const FPrimitiveSceneInfo* PrimitiveSceneInfo,const FLightCacheInterface* LCI)
{
uint32 NumDynamicLights = 0;
FLightPrimitiveInteraction *LightList = PrimitiveSceneInfo->LightList;
while ( LightList )
{
const FLightSceneInfo* LightSceneInfo = LightList->GetLight();
// Determine the interaction type between the mesh and the light.
FLightInteraction LightInteraction = FLightInteraction::Dynamic();
if(LCI)
{
LightInteraction = LCI->GetInteraction(LightSceneInfo->Proxy);
}
// Don't count light-mapped or irrelevant lights.
if(LightInteraction.GetType() != LIT_CachedIrrelevant && LightInteraction.GetType() != LIT_CachedLightMap)
{
++NumDynamicLights;
}
LightList = LightList->GetNextLight();
}
return NumDynamicLights;
}
/*-----------------------------------------------------------------------------
FLightPrimitiveInteraction
-----------------------------------------------------------------------------*/
/**
* Initialize the memory pool with a default size from the ini file.
* Called at render thread startup. Since the render thread is potentially
* created/destroyed multiple times, must make sure we only do it once.
*/
void FLightPrimitiveInteraction::InitializeMemoryPool()
{
}
/**
* Returns current size of memory pool
*/
uint32 FLightPrimitiveInteraction::GetMemoryPoolSize()
{
return GAllocateLightPrimitiveInteractionSize;
}
void FLightPrimitiveInteraction::Create(FLightSceneInfo* LightSceneInfo,FPrimitiveSceneInfo* PrimitiveSceneInfo)
{
LLM_SCOPE(ELLMTag::SceneRender);
// Attach the light to the primitive's static meshes.
bool bDynamic = true;
bool bRelevant = false;
bool bIsLightMapped = true;
bool bShadowMapped = false;
// Determine the light's relevance to the primitive.
check(PrimitiveSceneInfo->Proxy && LightSceneInfo->Proxy);
PrimitiveSceneInfo->Proxy->GetLightRelevance(LightSceneInfo->Proxy, bDynamic, bRelevant, bIsLightMapped, bShadowMapped);
if (bRelevant && bDynamic
// Don't let lights with static shadowing or static lighting affect primitives that should use static lighting, but don't have valid settings (lightmap res 0, etc)
// This prevents those components with invalid lightmap settings from causing lighting to remain unbuilt after a build
&& !(LightSceneInfo->Proxy->HasStaticShadowing() && PrimitiveSceneInfo->Proxy->HasStaticLighting() && !PrimitiveSceneInfo->Proxy->HasValidSettingsForStaticLighting()))
{
const bool bTranslucentObjectShadow = LightSceneInfo->Proxy->CastsTranslucentShadows() && PrimitiveSceneInfo->Proxy->CastsVolumetricTranslucentShadow();
const bool bInsetObjectShadow =
// Currently only supporting inset shadows on directional lights, but could be made to work with any whole scene shadows
LightSceneInfo->Proxy->GetLightType() == LightType_Directional
&& PrimitiveSceneInfo->Proxy->CastsInsetShadow();
// Movable directional lights determine shadow relevance dynamically based on the view and CSM settings. Interactions are only required for per-object cases.
if (LightSceneInfo->Proxy->GetLightType() != LightType_Directional || LightSceneInfo->Proxy->HasStaticShadowing() || bTranslucentObjectShadow || bInsetObjectShadow)
{
// Create the light interaction.
check(LightSceneInfo->Scene == PrimitiveSceneInfo->Scene);
STAT(FPlatformAtomics::InterlockedAdd(&GAllocateLightPrimitiveInteractionSize, -(int64)LightSceneInfo->Scene->LightPrimitiveInteractionAllocator.GetAllocatedSize()));
new (LightSceneInfo->Scene->LightPrimitiveInteractionAllocator.Allocate()) FLightPrimitiveInteraction(LightSceneInfo, PrimitiveSceneInfo, bDynamic, bIsLightMapped, bShadowMapped, bTranslucentObjectShadow, bInsetObjectShadow);
STAT(FPlatformAtomics::InterlockedAdd(&GAllocateLightPrimitiveInteractionSize, (int64)LightSceneInfo->Scene->LightPrimitiveInteractionAllocator.GetAllocatedSize()));
} //-V773
}
}
void FLightPrimitiveInteraction::Destroy(FLightPrimitiveInteraction* LightPrimitiveInteraction)
{
check(!!LightPrimitiveInteraction->LightSceneInfo->Scene);
FScene* Scene = LightPrimitiveInteraction->LightSceneInfo->Scene;
LightPrimitiveInteraction->~FLightPrimitiveInteraction();
STAT(FPlatformAtomics::InterlockedAdd(&GAllocateLightPrimitiveInteractionSize, -(int64)Scene->LightPrimitiveInteractionAllocator.GetAllocatedSize()));
Scene->LightPrimitiveInteractionAllocator.Free(LightPrimitiveInteraction);
STAT(FPlatformAtomics::InterlockedAdd(&GAllocateLightPrimitiveInteractionSize, (int64)Scene->LightPrimitiveInteractionAllocator.GetAllocatedSize()));
}
extern bool ShouldCreateObjectShadowForStationaryLight(const FLightSceneInfo* LightSceneInfo, const FPrimitiveSceneProxy* PrimitiveSceneProxy, bool bInteractionShadowMapped);
FLightPrimitiveInteraction::FLightPrimitiveInteraction(
FLightSceneInfo* InLightSceneInfo,
FPrimitiveSceneInfo* InPrimitiveSceneInfo,
bool bInIsDynamic,
bool bInLightMapped,
bool bInIsShadowMapped,
bool bInHasTranslucentObjectShadow,
bool bInHasInsetObjectShadow
) :
LightSceneInfo(InLightSceneInfo),
PrimitiveSceneInfo(InPrimitiveSceneInfo),
LightId(InLightSceneInfo->Id),
bLightMapped(bInLightMapped),
bIsDynamic(bInIsDynamic),
bIsShadowMapped(bInIsShadowMapped),
bUncachedStaticLighting(false),
bHasTranslucentObjectShadow(bInHasTranslucentObjectShadow),
bHasInsetObjectShadow(bInHasInsetObjectShadow),
bSelfShadowOnly(false),
bMobileDynamicPointLight(false)
{
// Determine whether this light-primitive interaction produces a shadow.
if(PrimitiveSceneInfo->Proxy->HasStaticLighting())
{
const bool bHasStaticShadow =
LightSceneInfo->Proxy->HasStaticShadowing() &&
LightSceneInfo->Proxy->CastsStaticShadow() &&
PrimitiveSceneInfo->Proxy->CastsStaticShadow();
const bool bHasDynamicShadow =
!LightSceneInfo->Proxy->HasStaticLighting() &&
LightSceneInfo->Proxy->CastsDynamicShadow() &&
PrimitiveSceneInfo->Proxy->CastsDynamicShadow();
bCastShadow = bHasStaticShadow || bHasDynamicShadow;
}
else
{
bCastShadow = LightSceneInfo->Proxy->CastsDynamicShadow() && PrimitiveSceneInfo->Proxy->CastsDynamicShadow();
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if(bCastShadow && bIsDynamic)
{
// Determine the type of dynamic shadow produced by this light.
if (PrimitiveSceneInfo->Proxy->HasStaticLighting()
&& PrimitiveSceneInfo->Proxy->CastsStaticShadow()
// Don't mark unbuilt for movable primitives which were built with lightmaps but moved into a new light's influence
&& PrimitiveSceneInfo->Proxy->GetLightmapType() != ELightmapType::ForceSurface
&& (LightSceneInfo->Proxy->HasStaticLighting() || (LightSceneInfo->Proxy->HasStaticShadowing() && !bInIsShadowMapped)))
{
// Update the game thread's counter of number of uncached static lighting interactions.
bUncachedStaticLighting = true;
if (!GUnbuiltPreviewShadowsInGame && !InLightSceneInfo->Scene->IsEditorScene())
{
bCastShadow = false;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
LightSceneInfo->NumUnbuiltInteractions++;
#endif
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
FPlatformAtomics::InterlockedIncrement(&PrimitiveSceneInfo->Scene->NumUncachedStaticLightingInteractions);
#endif
#if WITH_EDITOR
PrimitiveSceneInfo->Proxy->NumUncachedStaticLightingInteractions++;
#endif
}
}
#endif
bSelfShadowOnly = PrimitiveSceneInfo->Proxy->CastsSelfShadowOnly();
if (bIsDynamic)
{
// Add the interaction to the light's interaction list.
PrevPrimitiveLink = PrimitiveSceneInfo->Proxy->IsMeshShapeOftenMoving() ? &LightSceneInfo->DynamicInteractionOftenMovingPrimitiveList : &LightSceneInfo->DynamicInteractionStaticPrimitiveList;
// mobile movable spotlights / point lights
if (PrimitiveSceneInfo->Scene->GetShadingPath() == EShadingPath::Mobile && LightSceneInfo->Proxy->IsMovable())
{
const uint8 LightType = LightSceneInfo->Proxy->GetLightType();
static const auto MobileEnableMovableSpotLightsVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableMovableSpotLights"));
const bool bIsValidLightType =
LightType == LightType_Rect
|| LightType == LightType_Point
|| (LightType == LightType_Spot && MobileEnableMovableSpotLightsVar->GetValueOnRenderThread());
if( bIsValidLightType )
{
bMobileDynamicPointLight = true;
PrimitiveSceneInfo->NumMobileMovablePointLights++;
// The mobile renderer needs to update the shader bindings of movable point lights uniform buffer, so we have to update any static meshes in drawlists
PrimitiveSceneInfo->BeginDeferredUpdateStaticMeshes();
}
}
}
FlushCachedShadowMapData();
NextPrimitive = *PrevPrimitiveLink;
if(*PrevPrimitiveLink)
{
(*PrevPrimitiveLink)->PrevPrimitiveLink = &NextPrimitive;
}
*PrevPrimitiveLink = this;
// Add the interaction to the primitives' interaction list.
PrevLightLink = &PrimitiveSceneInfo->LightList;
NextLight = *PrevLightLink;
if(*PrevLightLink)
{
(*PrevLightLink)->PrevLightLink = &NextLight;
}
*PrevLightLink = this;
if (HasShadow()
&& LightSceneInfo->bRecordInteractionShadowPrimitives
&& (HasTranslucentObjectShadow() || HasInsetObjectShadow() || ShouldCreateObjectShadowForStationaryLight(LightSceneInfo, PrimitiveSceneInfo->Proxy, IsShadowMapped())))
{
if (LightSceneInfo->InteractionShadowPrimitives.Num() < 16)
{
LightSceneInfo->InteractionShadowPrimitives.Add(this);
}
else
{
LightSceneInfo->bRecordInteractionShadowPrimitives = false;
LightSceneInfo->InteractionShadowPrimitives.Empty();
}
}
}
FLightPrimitiveInteraction::~FLightPrimitiveInteraction()
{
check(IsInRenderingThread());
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// Update the game thread's counter of number of uncached static lighting interactions.
if(bUncachedStaticLighting)
{
LightSceneInfo->NumUnbuiltInteractions--;
FPlatformAtomics::InterlockedDecrement(&PrimitiveSceneInfo->Scene->NumUncachedStaticLightingInteractions);
#if WITH_EDITOR
PrimitiveSceneInfo->Proxy->NumUncachedStaticLightingInteractions--;
#endif
}
#endif
FlushCachedShadowMapData();
// Track mobile movable point light count
if (bMobileDynamicPointLight)
{
PrimitiveSceneInfo->NumMobileMovablePointLights--;
// The mobile renderer needs to use a different shader for movable point lights, so we have to update any static meshes in drawlists
PrimitiveSceneInfo->BeginDeferredUpdateStaticMeshes();
}
// Remove the interaction from the light's interaction list.
if(NextPrimitive)
{
NextPrimitive->PrevPrimitiveLink = PrevPrimitiveLink;
}
*PrevPrimitiveLink = NextPrimitive;
// Remove the interaction from the primitive's interaction list.
if(NextLight)
{
NextLight->PrevLightLink = PrevLightLink;
}
*PrevLightLink = NextLight;
LightSceneInfo->InteractionShadowPrimitives.RemoveSingleSwap(this);
}
void FLightPrimitiveInteraction::FlushCachedShadowMapData()
{
if (LightSceneInfo && PrimitiveSceneInfo && PrimitiveSceneInfo->Proxy && PrimitiveSceneInfo->Scene)
{
if (bCastShadow && !PrimitiveSceneInfo->Proxy->IsMeshShapeOftenMoving())
{
TArray<FCachedShadowMapData>* CachedShadowMapDatas = PrimitiveSceneInfo->Scene->GetCachedShadowMapDatas(LightSceneInfo->Id);
if (CachedShadowMapDatas)
{
for (auto& CachedShadowMapData : *CachedShadowMapDatas)
{
CachedShadowMapData.InvalidateCachedShadow();
}
}
}
}
}
int32 FStaticMeshBatchRelevance::GetStaticMeshCommandInfoIndex(EMeshPass::Type MeshPass) const
{
int32 CommandInfoIndex = CommandInfosBase;
if (!CommandInfosMask.Get(MeshPass))
{
return -1;
}
for (int32 MeshPassIndex = 0; MeshPassIndex < MeshPass; ++MeshPassIndex)
{
if (CommandInfosMask.Get((EMeshPass::Type) MeshPassIndex))
{
++CommandInfoIndex;
}
}
return CommandInfoIndex;
}
/*-----------------------------------------------------------------------------
FStaticMeshBatch
-----------------------------------------------------------------------------*/
FStaticMeshBatch::~FStaticMeshBatch()
{
FScene* Scene = PrimitiveSceneInfo->Scene;
// Remove this static mesh from the scene's list.
Scene->StaticMeshes.RemoveAt(Id);
}
/** Initialization constructor. */
FExponentialHeightFogSceneInfo::FExponentialHeightFogSceneInfo(const UExponentialHeightFogComponent* InComponent):
Component(InComponent),
FogMaxOpacity(InComponent->FogMaxOpacity),
StartDistance(InComponent->StartDistance),
FogCutoffDistance(InComponent->FogCutoffDistance),
DirectionalInscatteringExponent(InComponent->DirectionalInscatteringExponent),
DirectionalInscatteringStartDistance(InComponent->DirectionalInscatteringStartDistance),
DirectionalInscatteringColor(InComponent->DirectionalInscatteringColor)
{
FogData[0].Height = InComponent->GetComponentLocation().Z;
FogData[1].Height = InComponent->GetComponentLocation().Z + InComponent->SecondFogData.FogHeightOffset;
// Scale the densities back down to their real scale
// Artists edit the densities scaled up so they aren't entering in minuscule floating point numbers
FogData[0].Density = InComponent->FogDensity / 1000.0f;
FogData[0].HeightFalloff = InComponent->FogHeightFalloff / 1000.0f;
FogData[1].Density = InComponent->SecondFogData.FogDensity / 1000.0f;
FogData[1].HeightFalloff = InComponent->SecondFogData.FogHeightFalloff / 1000.0f;
FogColor = InComponent->InscatteringColorCubemap ? InComponent->InscatteringTextureTint : InComponent->FogInscatteringColor;
InscatteringColorCubemap = InComponent->InscatteringColorCubemap;
InscatteringColorCubemapAngle = InComponent->InscatteringColorCubemapAngle * (PI / 180.f);
FullyDirectionalInscatteringColorDistance = InComponent->FullyDirectionalInscatteringColorDistance;
NonDirectionalInscatteringColorDistance = InComponent->NonDirectionalInscatteringColorDistance;
bEnableVolumetricFog = InComponent->bEnableVolumetricFog;
VolumetricFogScatteringDistribution = FMath::Clamp(InComponent->VolumetricFogScatteringDistribution, -.99f, .99f);
VolumetricFogAlbedo = FLinearColor(InComponent->VolumetricFogAlbedo);
VolumetricFogEmissive = InComponent->VolumetricFogEmissive;
// Apply a scale so artists don't have to work with tiny numbers.
const float UnitScale = 1.0f / 10000.0f;
VolumetricFogEmissive.R = FMath::Max(VolumetricFogEmissive.R * UnitScale, 0.0f);
VolumetricFogEmissive.G = FMath::Max(VolumetricFogEmissive.G * UnitScale, 0.0f);
VolumetricFogEmissive.B = FMath::Max(VolumetricFogEmissive.B * UnitScale, 0.0f);
VolumetricFogExtinctionScale = FMath::Max(InComponent->VolumetricFogExtinctionScale, 0.0f);
VolumetricFogDistance = FMath::Max(InComponent->VolumetricFogDistance, 0.0f);
VolumetricFogStaticLightingScatteringIntensity = FMath::Max(InComponent->VolumetricFogStaticLightingScatteringIntensity, 0.0f);
bOverrideLightColorsWithFogInscatteringColors = InComponent->bOverrideLightColorsWithFogInscatteringColors;
}