2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
/*=============================================================================
ShadowSetup . cpp : Dynamic shadow setup implementation .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "RendererPrivate.h"
# include "ScenePrivate.h"
# include "LightPropagationVolume.h"
static float GMinScreenRadiusForShadowCaster = 0.03f ;
2014-09-25 15:02:14 -04:00
static FAutoConsoleVariableRef CVarMinScreenRadiusForShadowCaster (
2014-03-14 14:13:41 -04:00
TEXT ( " r.Shadow.RadiusThreshold " ) ,
2014-09-03 18:17:19 -04:00
GMinScreenRadiusForShadowCaster ,
TEXT ( " Cull shadow casters if they are too small, value is the minimal screen space bounding sphere radius \n " )
2014-03-14 14:13:41 -04:00
TEXT ( " (default 0.03) " ) ,
ECVF_Scalability | ECVF_RenderThreadSafe
) ;
static float GMinScreenRadiusForShadowCasterRSM = 0.06f ;
static FAutoConsoleVariableRef CVarMinScreenRadiusForShadowCasterRSM (
TEXT ( " r.Shadow.RadiusThresholdRSM " ) ,
GMinScreenRadiusForShadowCasterRSM ,
TEXT ( " Cull shadow casters in the RSM if they are too small, values is the minimal screen space bounding sphere radius \n " )
TEXT ( " (default 0.06) " )
) ;
/** Can be used to visualize preshadow frustums when the shadowfrustums show flag is enabled. */
static TAutoConsoleVariable < int32 > CVarDrawPreshadowFrustum (
TEXT ( " r.Shadow.DrawPreshadowFrustums " ) ,
0 ,
TEXT ( " visualize preshadow frustums when the shadowfrustums show flag is enabled " ) ,
ECVF_RenderThreadSafe
) ;
/** Whether to allow preshadows (static world casting on character), can be disabled for debugging. */
static TAutoConsoleVariable < int32 > CVarAllowPreshadows (
TEXT ( " r.Shadow.Preshadows " ) ,
1 ,
TEXT ( " Whether to allow preshadows (static world casting on character) " ) ,
ECVF_RenderThreadSafe
) ;
/** Whether to allow per object shadows (character casting on world), can be disabled for debugging. */
static TAutoConsoleVariable < int32 > CVarAllowPerObjectShadows (
TEXT ( " r.Shadow.PerObject " ) ,
1 ,
TEXT ( " Whether to render per object shadows (character casting on world) \n " )
TEXT ( " 0: off \n " )
TEXT ( " 1: on (default) " ) ,
ECVF_RenderThreadSafe
) ;
static TAutoConsoleVariable < float > CVarShadowFadeExponent (
TEXT ( " r.Shadow.FadeExponent " ) ,
0.25f ,
TEXT ( " Controls the rate at which shadows are faded out " ) ,
ECVF_RenderThreadSafe ) ;
/**
* Whether preshadows can be cached as an optimization .
* Disabling the caching through this setting is useful when debugging .
*/
static TAutoConsoleVariable < int32 > CVarCachePreshadows (
TEXT ( " r.Shadow.CachePreshadow " ) ,
1 ,
TEXT ( " Whether preshadows can be cached as an optimization " ) ,
ECVF_RenderThreadSafe
) ;
bool ShouldUseCachePreshadows ( )
{
return CVarCachePreshadows . GetValueOnRenderThread ( ) ! = 0 ;
}
/**
* This value specifies how much bounds will be expanded when rendering a cached preshadow ( 0.15 = 15 % larger ) .
* Larger values result in more cache hits , but lower resolution and pull more objects into the depth pass .
*/
static TAutoConsoleVariable < float > CVarPreshadowExpandFraction (
TEXT ( " r.Shadow.PreshadowExpand " ) ,
0.15f ,
TEXT ( " How much bounds will be expanded when rendering a cached preshadow (0.15 = 15% larger) " ) ,
ECVF_RenderThreadSafe
) ;
static TAutoConsoleVariable < float > CVarPreShadowResolutionFactor (
TEXT ( " r.Shadow.PreShadowResolutionFactor " ) ,
0.5f ,
TEXT ( " Mulitplier for preshadow resolution " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < float > CVarShadowTexelsPerPixel (
TEXT ( " r.Shadow.TexelsPerPixel " ) ,
1.27324f ,
2015-03-02 13:07:40 -05:00
TEXT ( " The ratio of subject pixels to shadow texels for per-object shadows " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < float > CVarShadowTexelsPerPixelSpotlight (
TEXT ( " r.Shadow.TexelsPerPixelSpotlight " ) ,
1.27324f ,
TEXT ( " The ratio of subject pixels to shadow texels for spotlights " ) ,
2014-03-14 14:13:41 -04:00
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > CVarPreShadowFadeResolution (
TEXT ( " r.Shadow.PreShadowFadeResolution " ) ,
16 ,
TEXT ( " Resolution in texels below which preshadows are faded out " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > CVarShadowFadeResolution (
TEXT ( " r.Shadow.FadeResolution " ) ,
64 ,
TEXT ( " Resolution in texels below which shadows are faded out " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > CVarMinShadowResolution (
TEXT ( " r.Shadow.MinResolution " ) ,
32 ,
TEXT ( " Minimum dimensions (in texels) allowed for rendering shadow subject depths " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > CVarMinPreShadowResolution (
TEXT ( " r.Shadow.MinPreShadowResolution " ) ,
8 ,
TEXT ( " Minimum dimensions (in texels) allowed for rendering preshadow depths " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > CVarUseConservativeShadowBounds (
TEXT ( " r.Shadow.ConservativeBounds " ) ,
0 ,
TEXT ( " Whether to use safe and conservative shadow frustum creation that wastes some shadowmap space " ) ,
ECVF_RenderThreadSafe ) ;
2015-01-19 17:34:22 -05:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// read and written on the render thread
bool GDumpShadowSetup = false ;
void DumpShadowDumpSetup ( )
{
ENQUEUE_UNIQUE_RENDER_COMMAND (
DumpShadowDumpSetup ,
{
GDumpShadowSetup = true ;
} ) ;
}
FAutoConsoleCommand CmdDumpShadowDumpSetup (
TEXT ( " r.DumpShadows " ) ,
TEXT ( " Dump shadow setup (for developer only, only for non shiping build) " ) ,
FConsoleCommandDelegate : : CreateStatic ( DumpShadowDumpSetup )
) ;
# endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
2014-03-14 14:13:41 -04:00
/**
* Helper function to determine fade alpha value for shadows based on resolution . In the below ASCII art ( 1 ) is
* the MinShadowResolution and ( 2 ) is the ShadowFadeResolution . Alpha will be 0 below the min resolution and 1
* above the fade resolution . In between it is going to be an exponential curve with the values between ( 1 ) and ( 2 )
* being normalized in the 0. .1 range .
*
*
* | / - - - - - - -
* | /
* | /
* 1 - - - - - 2 - - - - - - -
*
* @ param MaxUnclampedResolution Requested resolution , unclamped so it can be below min
* @ param ShadowFadeResolution Resolution at which fade begins
* @ param MinShadowResolution Minimum resolution of shadow
*
* @ return fade value between 0 and 1
*/
2014-08-28 18:15:46 -04:00
float CalculateShadowFadeAlpha ( float MaxUnclampedResolution , int32 ShadowFadeResolution , int32 MinShadowResolution )
2014-03-14 14:13:41 -04:00
{
2014-08-28 18:15:46 -04:00
check ( MaxUnclampedResolution > = 0 ) ;
check ( ShadowFadeResolution > = 0 ) ;
check ( MinShadowResolution > = 0 ) ;
2014-03-14 14:13:41 -04:00
float FadeAlpha = 0.0f ;
// Shadow size is above fading resolution.
if ( MaxUnclampedResolution > ShadowFadeResolution )
{
FadeAlpha = 1.0f ;
}
// Shadow size is below fading resolution but above min resolution.
else if ( MaxUnclampedResolution > MinShadowResolution )
{
const float InverseRange = 1.0f / ( ShadowFadeResolution - MinShadowResolution ) ;
const float FirstFadeValue = FMath : : Pow ( InverseRange , CVarShadowFadeExponent . GetValueOnRenderThread ( ) ) ;
const float SizeRatio = ( float ) ( MaxUnclampedResolution - MinShadowResolution ) * InverseRange ;
// Rescale the fade alpha to reduce the change between no fading and the first value, which reduces popping with small ShadowFadeExponent's
FadeAlpha = ( FMath : : Pow ( SizeRatio , CVarShadowFadeExponent . GetValueOnRenderThread ( ) ) - FirstFadeValue ) / ( 1.0f - FirstFadeValue ) ;
}
return FadeAlpha ;
}
typedef TArray < FVector , TInlineAllocator < 8 > > FBoundingBoxVertexArray ;
/** Stores the indices for an edge of a bounding volume. */
struct FBoxEdge
{
uint16 FirstEdgeIndex ;
uint16 SecondEdgeIndex ;
FBoxEdge ( uint16 InFirst , uint16 InSecond ) :
FirstEdgeIndex ( InFirst ) ,
SecondEdgeIndex ( InSecond )
{ }
} ;
typedef TArray < FBoxEdge , TInlineAllocator < 12 > > FBoundingBoxEdgeArray ;
/**
* Creates an array of vertices and edges for a bounding box .
* @ param Box - The bounding box
* @ param OutVertices - Upon return , the array will contain the vertices of the bounding box .
* @ param OutEdges - Upon return , will contain indices of the edges of the bounding box .
*/
static void GetBoundingBoxVertices ( const FBox & Box , FBoundingBoxVertexArray & OutVertices , FBoundingBoxEdgeArray & OutEdges )
{
OutVertices . Empty ( 8 ) ;
OutVertices . AddUninitialized ( 8 ) ;
for ( int32 X = 0 ; X < 2 ; X + + )
{
for ( int32 Y = 0 ; Y < 2 ; Y + + )
{
for ( int32 Z = 0 ; Z < 2 ; Z + + )
{
OutVertices [ X * 4 + Y * 2 + Z ] = FVector (
X ? Box . Min . X : Box . Max . X ,
Y ? Box . Min . Y : Box . Max . Y ,
Z ? Box . Min . Z : Box . Max . Z
) ;
}
}
}
OutEdges . Empty ( 12 ) ;
OutEdges . AddUninitialized ( 12 ) ;
for ( uint16 X = 0 ; X < 2 ; X + + )
{
uint16 BaseIndex = X * 4 ;
OutEdges [ X * 4 + 0 ] = FBoxEdge ( BaseIndex , BaseIndex + 1 ) ;
OutEdges [ X * 4 + 1 ] = FBoxEdge ( BaseIndex + 1 , BaseIndex + 3 ) ;
OutEdges [ X * 4 + 2 ] = FBoxEdge ( BaseIndex + 3 , BaseIndex + 2 ) ;
OutEdges [ X * 4 + 3 ] = FBoxEdge ( BaseIndex + 2 , BaseIndex ) ;
}
for ( uint16 XEdge = 0 ; XEdge < 4 ; XEdge + + )
{
OutEdges [ 8 + XEdge ] = FBoxEdge ( XEdge , XEdge + 4 ) ;
}
}
/**
* Computes the transform contains a set of bounding box vertices and minimizes the pre - transform volume inside the post - transform clip space .
* @ param ZAxis - The Z axis of the transform .
* @ param Points - The points that represent the bounding volume .
* @ param Edges - The edges of the bounding volume .
* @ param OutAspectRatio - Upon successful return , contains the aspect ratio of the AABB ; the ratio of width : height .
* @ param OutTransform - Upon successful return , contains the transform .
* @ return true if it successfully found a non - zero area projection of the bounding points .
*/
static bool GetBestShadowTransform ( const FVector & ZAxis , const FBoundingBoxVertexArray & Points , const FBoundingBoxEdgeArray & Edges , float & OutAspectRatio , FMatrix & OutTransform )
{
// Find the axis parallel to the edge between any two boundary points with the smallest projection of the bounds onto the axis.
FVector XAxis ( 0 , 0 , 0 ) ;
FVector YAxis ( 0 , 0 , 0 ) ;
FVector Translation ( 0 , 0 , 0 ) ;
float BestProjectedExtent = FLT_MAX ;
bool bValidProjection = false ;
// Cache unaliased pointers to point and edge data
const FVector * RESTRICT PointsPtr = Points . GetData ( ) ;
const FBoxEdge * RESTRICT EdgesPtr = Edges . GetData ( ) ;
const int32 NumPoints = Points . Num ( ) ;
const int32 NumEdges = Edges . Num ( ) ;
// We're always dealing with box geometry here, so we can hint the compiler
ASSUME ( NumPoints = = 8 ) ;
ASSUME ( NumEdges = = 12 ) ;
for ( int32 EdgeIndex = 0 ; EdgeIndex < NumEdges ; + + EdgeIndex )
{
const FVector Point = PointsPtr [ EdgesPtr [ EdgeIndex ] . FirstEdgeIndex ] ;
const FVector OtherPoint = PointsPtr [ EdgesPtr [ EdgeIndex ] . SecondEdgeIndex ] ;
const FVector PointDelta = OtherPoint - Point ;
2014-11-26 10:01:12 -05:00
const FVector TrialXAxis = ( PointDelta - ZAxis * ( PointDelta | ZAxis ) ) . GetSafeNormal ( ) ;
const FVector TrialYAxis = ( ZAxis ^ TrialXAxis ) . GetSafeNormal ( ) ;
2014-03-14 14:13:41 -04:00
// Calculate the size of the projection of the bounds onto this axis and an axis orthogonal to it and the Z axis.
float MinProjectedX = FLT_MAX ;
float MaxProjectedX = - FLT_MAX ;
float MinProjectedY = FLT_MAX ;
float MaxProjectedY = - FLT_MAX ;
for ( int32 ProjectedPointIndex = 0 ; ProjectedPointIndex < NumPoints ; + + ProjectedPointIndex )
{
const float ProjectedX = PointsPtr [ ProjectedPointIndex ] | TrialXAxis ;
MinProjectedX = FMath : : Min ( MinProjectedX , ProjectedX ) ;
MaxProjectedX = FMath : : Max ( MaxProjectedX , ProjectedX ) ;
const float ProjectedY = PointsPtr [ ProjectedPointIndex ] | TrialYAxis ;
MinProjectedY = FMath : : Min ( MinProjectedY , ProjectedY ) ;
MaxProjectedY = FMath : : Max ( MaxProjectedY , ProjectedY ) ;
}
float ProjectedExtentX ;
float ProjectedExtentY ;
if ( CVarUseConservativeShadowBounds . GetValueOnRenderThread ( ) ! = 0 )
{
ProjectedExtentX = 2 * FMath : : Max ( FMath : : Abs ( MaxProjectedX ) , FMath : : Abs ( MinProjectedX ) ) ;
ProjectedExtentY = 2 * FMath : : Max ( FMath : : Abs ( MaxProjectedY ) , FMath : : Abs ( MinProjectedY ) ) ;
}
else
{
ProjectedExtentX = MaxProjectedX - MinProjectedX ;
ProjectedExtentY = MaxProjectedY - MinProjectedY ;
}
const float ProjectedExtent = ProjectedExtentX * ProjectedExtentY ;
if ( ProjectedExtent < BestProjectedExtent - .05f
// Only allow projections with non-zero area
& & ProjectedExtent > DELTA )
{
bValidProjection = true ;
BestProjectedExtent = ProjectedExtent ;
XAxis = TrialXAxis * 2.0f / ProjectedExtentX ;
YAxis = TrialYAxis * 2.0f / ProjectedExtentY ;
// Translating in post-transform clip space can cause the corners of the world space bounds to be outside of the transform generated by this function
// This usually manifests in cinematics where the character's head is near the top of the bounds
if ( CVarUseConservativeShadowBounds . GetValueOnRenderThread ( ) = = 0 )
{
Translation . X = ( MinProjectedX + MaxProjectedX ) * 0.5f ;
Translation . Y = ( MinProjectedY + MaxProjectedY ) * 0.5f ;
}
if ( ProjectedExtentY > ProjectedExtentX )
{
// Always make the X axis the largest one.
Exchange ( XAxis , YAxis ) ;
Exchange ( Translation . X , Translation . Y ) ;
XAxis * = - 1.0f ;
Translation . X * = - 1.0f ;
OutAspectRatio = ProjectedExtentY / ProjectedExtentX ;
}
else
{
OutAspectRatio = ProjectedExtentX / ProjectedExtentY ;
}
}
}
// Only create the shadow if the projected extent of the given points has a non-zero area.
if ( bValidProjection & & BestProjectedExtent > DELTA )
{
OutTransform = FBasisVectorMatrix ( XAxis , YAxis , ZAxis , FVector ( 0 , 0 , 0 ) ) * FTranslationMatrix ( Translation ) ;
return true ;
}
else
{
return false ;
}
}
2015-01-25 13:58:27 -05:00
FProjectedShadowInfo : : FProjectedShadowInfo ( )
2015-01-26 09:27:33 -05:00
: DependentView ( 0 )
2015-01-25 13:58:27 -05:00
, ShadowId ( INDEX_NONE )
, PreShadowTranslation ( 0 , 0 , 0 )
, ShadowBounds ( 0 )
, X ( 0 )
, Y ( 0 )
, ResolutionX ( 0 )
, ResolutionY ( 0 )
, MaxScreenPercent ( 1.0f )
, bAllocated ( false )
, bAllocatedInTranslucentLayout ( false )
, bRendered ( false )
, bAllocatedInPreshadowCache ( false )
, bDepthsCached ( false )
, bDirectionalLight ( false )
, bWholeSceneShadow ( false )
, bReflectiveShadowmap ( false )
, bTranslucentShadow ( false )
, bPreShadow ( false )
, bSelfShadowOnly ( false )
2015-01-26 09:27:33 -05:00
, LightSceneInfo ( 0 )
, ParentSceneInfo ( 0 )
2015-01-25 13:58:27 -05:00
, ShaderDepthBias ( 0.0f )
{
}
2015-01-25 14:16:38 -05:00
bool FProjectedShadowInfo : : SetupPerObjectProjection (
2014-03-14 14:13:41 -04:00
FLightSceneInfo * InLightSceneInfo ,
const FPrimitiveSceneInfo * InParentSceneInfo ,
const FPerObjectProjectedShadowInitializer & Initializer ,
bool bInPreShadow ,
uint32 InResolutionX ,
uint32 MaxShadowResolutionY ,
float InMaxScreenPercent ,
2015-01-23 17:48:57 -05:00
bool bInTranslucentShadow )
2014-03-14 14:13:41 -04:00
{
2015-01-25 13:58:27 -05:00
check ( InParentSceneInfo ) ;
LightSceneInfo = InLightSceneInfo ;
LightSceneInfoCompact = InLightSceneInfo ;
ParentSceneInfo = InParentSceneInfo ;
PreShadowTranslation = Initializer . PreShadowTranslation ;
ShadowBounds = FSphere ( Initializer . SubjectBounds . Origin - Initializer . PreShadowTranslation , Initializer . SubjectBounds . SphereRadius ) ;
ResolutionX = InResolutionX ;
MaxScreenPercent = InMaxScreenPercent ;
bDirectionalLight = InLightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Directional ;
bTranslucentShadow = bInTranslucentShadow ;
bPreShadow = bInPreShadow ;
bSelfShadowOnly = InParentSceneInfo - > Proxy - > CastsSelfShadowOnly ( ) ;
2015-01-23 17:48:57 -05:00
check ( ! CascadeSettings . bRayTracedDistanceField ) ;
2014-03-14 14:13:41 -04:00
const FMatrix WorldToLightScaled = Initializer . WorldToLight * FScaleMatrix ( Initializer . Scales ) ;
2014-09-12 17:21:49 -04:00
2014-03-14 14:13:41 -04:00
// Create an array of the extreme vertices of the subject's bounds.
FBoundingBoxVertexArray BoundsPoints ;
FBoundingBoxEdgeArray BoundsEdges ;
GetBoundingBoxVertices ( Initializer . SubjectBounds . GetBox ( ) , BoundsPoints , BoundsEdges ) ;
// Project the bounding box vertices.
FBoundingBoxVertexArray ProjectedBoundsPoints ;
for ( int32 PointIndex = 0 ; PointIndex < BoundsPoints . Num ( ) ; PointIndex + + )
{
const FVector TransformedBoundsPoint = WorldToLightScaled . TransformPosition ( BoundsPoints [ PointIndex ] ) ;
const float TransformedBoundsPointW = Dot4 ( FVector4 ( 0 , 0 , TransformedBoundsPoint | Initializer . FaceDirection , 1 ) , Initializer . WAxis ) ;
if ( TransformedBoundsPointW > = DELTA )
{
ProjectedBoundsPoints . Add ( TransformedBoundsPoint / TransformedBoundsPointW ) ;
}
else
{
ProjectedBoundsPoints . Add ( FVector ( FLT_MAX , FLT_MAX , FLT_MAX ) ) ;
}
}
// Compute the transform from light-space to shadow-space.
FMatrix LightToShadow ;
float AspectRatio ;
2015-01-25 14:16:38 -05:00
// if this is a valid transform (can be false if the object is around the light)
bool bRet = false ;
2014-03-14 14:13:41 -04:00
2014-11-26 10:01:12 -05:00
if ( GetBestShadowTransform ( Initializer . FaceDirection . GetSafeNormal ( ) , ProjectedBoundsPoints , BoundsEdges , AspectRatio , LightToShadow ) )
2014-03-14 14:13:41 -04:00
{
2015-01-25 14:16:38 -05:00
bRet = true ;
2014-03-14 14:13:41 -04:00
const FMatrix WorldToShadow = WorldToLightScaled * LightToShadow ;
const FBox ShadowSubjectBounds = Initializer . SubjectBounds . GetBox ( ) . TransformBy ( WorldToShadow ) ;
MinSubjectZ = FMath : : Max ( Initializer . MinLightW , ShadowSubjectBounds . Min . Z ) ;
float MaxReceiverZ = FMath : : Min ( MinSubjectZ + Initializer . MaxDistanceToCastInLightW , ( float ) HALF_WORLD_MAX ) ;
// Max can end up smaller than min due to the clamp to HALF_WORLD_MAX above
MaxReceiverZ = FMath : : Max ( MaxReceiverZ , MinSubjectZ + 1 ) ;
MaxSubjectZ = FMath : : Max ( ShadowSubjectBounds . Max . Z , MinSubjectZ + 1 ) ;
const FMatrix SubjectMatrix = WorldToShadow * FShadowProjectionMatrix ( MinSubjectZ , MaxSubjectZ , Initializer . WAxis ) ;
const float MaxSubjectAndReceiverDepth = Initializer . SubjectBounds . GetBox ( ) . TransformBy ( SubjectMatrix ) . Max . Z ;
float MaxSubjectDepth ;
if ( bPreShadow )
{
const FMatrix PreSubjectMatrix = WorldToShadow * FShadowProjectionMatrix ( Initializer . MinLightW , MaxSubjectZ , Initializer . WAxis ) ;
// Preshadow frustum bounds go from the light to the furthest extent of the object in light space
SubjectAndReceiverMatrix = PreSubjectMatrix ;
ReceiverMatrix = SubjectMatrix ;
MaxSubjectDepth = bDirectionalLight ? MaxSubjectAndReceiverDepth : Initializer . SubjectBounds . GetBox ( ) . TransformBy ( PreSubjectMatrix ) . Max . Z ;
}
else
{
const FMatrix PostSubjectMatrix = WorldToShadow * FShadowProjectionMatrix ( MinSubjectZ , MaxReceiverZ , Initializer . WAxis ) ;
SubjectAndReceiverMatrix = SubjectMatrix ;
ReceiverMatrix = PostSubjectMatrix ;
MaxSubjectDepth = MaxSubjectAndReceiverDepth ;
}
InvMaxSubjectDepth = 1.0f / MaxSubjectDepth ;
MinPreSubjectZ = Initializer . MinLightW ;
2014-05-06 06:26:25 -04:00
ResolutionY = FMath : : Min < uint32 > ( FMath : : TruncToInt ( InResolutionX / AspectRatio ) , MaxShadowResolutionY ) ;
2014-03-14 14:13:41 -04:00
// Store the view matrix
// Reorder the vectors to match the main view, since ShadowViewMatrix will be used to override the main view's view matrix during shadow depth rendering
ShadowViewMatrix = Initializer . WorldToLight *
FMatrix (
FPlane ( 0 , 0 , 1 , 0 ) ,
FPlane ( 1 , 0 , 0 , 0 ) ,
FPlane ( 0 , 1 , 0 , 0 ) ,
FPlane ( 0 , 0 , 0 , 1 ) ) ;
GetViewFrustumBounds ( CasterFrustum , SubjectAndReceiverMatrix , true ) ;
2014-08-13 15:29:41 -04:00
InvReceiverMatrix = ReceiverMatrix . InverseFast ( ) ;
2014-03-14 14:13:41 -04:00
GetViewFrustumBounds ( ReceiverFrustum , ReceiverMatrix , true ) ;
2015-01-25 14:16:38 -05:00
UpdateShaderDepthBias ( ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-25 14:16:38 -05:00
return bRet ;
2014-03-14 14:13:41 -04:00
}
2015-01-25 13:58:27 -05:00
void FProjectedShadowInfo : : SetupWholeSceneProjection (
2014-03-14 14:13:41 -04:00
FLightSceneInfo * InLightSceneInfo ,
FViewInfo * InDependentView ,
const FWholeSceneProjectedShadowInitializer & Initializer ,
uint32 InResolutionX ,
uint32 InResolutionY ,
2015-01-23 17:48:57 -05:00
bool bInReflectiveShadowMap )
2014-09-12 17:21:49 -04:00
{
2015-01-25 13:58:27 -05:00
LightSceneInfo = InLightSceneInfo ;
LightSceneInfoCompact = InLightSceneInfo ;
DependentView = InDependentView ;
PreShadowTranslation = Initializer . PreShadowTranslation ;
CascadeSettings = Initializer . CascadeSettings ;
ResolutionX = InResolutionX ;
ResolutionY = InResolutionY ;
bDirectionalLight = InLightSceneInfo - > Proxy - > GetLightType ( ) = = LightType_Directional ;
bWholeSceneShadow = true ;
bReflectiveShadowmap = bInReflectiveShadowMap ;
2014-03-14 14:13:41 -04:00
FVector XAxis , YAxis ;
Initializer . FaceDirection . FindBestAxisVectors ( XAxis , YAxis ) ;
const FMatrix WorldToLightScaled = Initializer . WorldToLight * FScaleMatrix ( Initializer . Scales ) ;
2014-11-26 10:01:12 -05:00
const FMatrix WorldToFace = WorldToLightScaled * FBasisVectorMatrix ( - XAxis , YAxis , Initializer . FaceDirection . GetSafeNormal ( ) , FVector : : ZeroVector ) ;
2014-03-14 14:13:41 -04:00
MaxSubjectZ = WorldToFace . TransformPosition ( Initializer . SubjectBounds . Origin ) . Z + Initializer . SubjectBounds . SphereRadius ;
MinSubjectZ = FMath : : Max ( MaxSubjectZ - Initializer . SubjectBounds . SphereRadius * 2 , Initializer . MinLightW ) ;
2014-05-16 20:34:56 -04:00
if ( bInReflectiveShadowMap )
2014-03-14 14:13:41 -04:00
{
2015-01-23 17:48:57 -05:00
check ( ! CascadeSettings . bOnePassPointLightShadow ) ;
check ( ! CascadeSettings . ShadowSplitIndex ) ;
2014-03-14 14:13:41 -04:00
2014-05-16 20:34:56 -04:00
// Quantise the RSM in shadow texel space
static bool bQuantize = true ;
if ( bQuantize )
{
const FIntPoint ShadowBufferResolution = GSceneRenderTargets . GetReflectiveShadowMapTextureResolution ( ) ;
uint32 ShadowDepthBufferSizeX = ShadowBufferResolution . X ;
uint32 ShadowDepthBufferSizeY = ShadowBufferResolution . Y ;
// Transform the shadow's position into shadowmap space
const FVector TransformedPosition = WorldToFace . TransformPosition ( - PreShadowTranslation ) ;
// Largest amount that the shadowmap will be downsampled to during sampling
// We need to take this into account when snapping to get a stable result
// This corresponds to the maximum kernel filter size used by subsurface shadows in ShadowProjectionPixelShader.usf
static int32 MaxDownsampleFactor = 4 ;
// Determine the distance necessary to snap the shadow's position to the nearest texel
const float SnapX = FMath : : Fmod ( TransformedPosition . X , 2.0f * MaxDownsampleFactor / ShadowDepthBufferSizeX ) ;
const float SnapY = FMath : : Fmod ( TransformedPosition . Y , 2.0f * MaxDownsampleFactor / ShadowDepthBufferSizeY ) ;
// Snap the shadow's position and transform it back into world space
// This snapping prevents sub-texel camera movements which removes view dependent aliasing from the final shadow result
// This only maintains stable shadows under camera translation and rotation
2014-08-13 15:29:41 -04:00
const FVector SnappedWorldPosition = WorldToFace . InverseFast ( ) . TransformPosition ( TransformedPosition - FVector ( SnapX , SnapY , 0.0f ) ) ;
2014-05-16 20:34:56 -04:00
PreShadowTranslation = - SnappedWorldPosition ;
}
ShadowBounds = FSphere ( - PreShadowTranslation , Initializer . SubjectBounds . SphereRadius ) ;
GetViewFrustumBounds ( CasterFrustum , SubjectAndReceiverMatrix , true ) ;
}
else
{
2015-01-19 15:55:17 -05:00
if ( bDirectionalLight )
2014-05-16 20:34:56 -04:00
{
// Limit how small the depth range can be for smaller cascades
// This is needed for shadow modes like subsurface shadows which need depth information outside of the smaller cascade depth range
//@todo - expose this value to the ini
const float DepthRangeClamp = 5000 ;
MaxSubjectZ = FMath : : Max ( MaxSubjectZ , DepthRangeClamp ) ;
MinSubjectZ = FMath : : Min ( MinSubjectZ , - DepthRangeClamp ) ;
const FIntPoint ShadowBufferResolution = GSceneRenderTargets . GetShadowDepthTextureResolution ( ) ;
const uint32 ShadowDepthBufferSizeX = ShadowBufferResolution . X - SHADOW_BORDER * 2 ;
const uint32 ShadowDepthBufferSizeY = ShadowBufferResolution . Y - SHADOW_BORDER * 2 ;
// Transform the shadow's position into shadowmap space
const FVector TransformedPosition = WorldToFace . TransformPosition ( - PreShadowTranslation ) ;
// Largest amount that the shadowmap will be downsampled to during sampling
// We need to take this into account when snapping to get a stable result
// This corresponds to the maximum kernel filter size used by subsurface shadows in ShadowProjectionPixelShader.usf
const int32 MaxDownsampleFactor = 4 ;
// Determine the distance necessary to snap the shadow's position to the nearest texel
const float SnapX = FMath : : Fmod ( TransformedPosition . X , 2.0f * MaxDownsampleFactor / ShadowDepthBufferSizeX ) ;
const float SnapY = FMath : : Fmod ( TransformedPosition . Y , 2.0f * MaxDownsampleFactor / ShadowDepthBufferSizeY ) ;
// Snap the shadow's position and transform it back into world space
// This snapping prevents sub-texel camera movements which removes view dependent aliasing from the final shadow result
// This only maintains stable shadows under camera translation and rotation
2014-08-13 15:29:41 -04:00
const FVector SnappedWorldPosition = WorldToFace . InverseFast ( ) . TransformPosition ( TransformedPosition - FVector ( SnapX , SnapY , 0.0f ) ) ;
2014-05-16 20:34:56 -04:00
PreShadowTranslation = - SnappedWorldPosition ;
}
2015-01-23 17:48:57 -05:00
if ( CascadeSettings . ShadowSplitIndex > = 0 & & bDirectionalLight )
2014-05-16 20:34:56 -04:00
{
checkSlow ( InDependentView ) ;
2015-03-30 17:38:39 -04:00
ShadowBounds = InLightSceneInfo - > Proxy - > GetShadowSplitBounds (
* InDependentView ,
CascadeSettings . bRayTracedDistanceField ? INDEX_NONE : CascadeSettings . ShadowSplitIndex ,
InLightSceneInfo - > IsPrecomputedLightingValid ( ) ,
0 ) ;
2014-05-16 20:34:56 -04:00
}
else
{
ShadowBounds = FSphere ( - Initializer . PreShadowTranslation , Initializer . SubjectBounds . SphereRadius ) ;
}
// Any meshes between the light and the subject can cast shadows, also any meshes inside the subject region
const FMatrix CasterMatrix = WorldToFace * FShadowProjectionMatrix ( Initializer . MinLightW , MaxSubjectZ , Initializer . WAxis ) ;
GetViewFrustumBounds ( CasterFrustum , CasterMatrix , true ) ;
2014-03-14 14:13:41 -04:00
}
check ( MaxSubjectZ > MinSubjectZ ) ;
const float ClampedMaxLightW = FMath : : Min ( MinSubjectZ + Initializer . MaxDistanceToCastInLightW , ( float ) HALF_WORLD_MAX ) ;
MinPreSubjectZ = Initializer . MinLightW ;
2014-05-16 20:34:56 -04:00
SubjectAndReceiverMatrix = WorldToFace * FShadowProjectionMatrix ( MinSubjectZ , MaxSubjectZ , Initializer . WAxis ) ;
ReceiverMatrix = WorldToFace * FShadowProjectionMatrix ( MinSubjectZ , ClampedMaxLightW , Initializer . WAxis ) ;
2014-03-14 14:13:41 -04:00
2014-05-16 20:34:56 -04:00
float MaxSubjectDepth = SubjectAndReceiverMatrix . TransformPosition (
2014-03-14 14:13:41 -04:00
Initializer . SubjectBounds . Origin
2014-08-13 15:29:41 -04:00
+ WorldToLightScaled . InverseFast ( ) . TransformVector ( Initializer . FaceDirection ) * Initializer . SubjectBounds . SphereRadius
2014-03-14 14:13:41 -04:00
) . Z ;
2015-01-23 17:48:57 -05:00
if ( CascadeSettings . bOnePassPointLightShadow )
2014-03-14 14:13:41 -04:00
{
MaxSubjectDepth = Initializer . SubjectBounds . SphereRadius ;
}
InvMaxSubjectDepth = 1.0f / MaxSubjectDepth ;
// Store the view matrix
// Reorder the vectors to match the main view, since ShadowViewMatrix will be used to override the main view's view matrix during shadow depth rendering
ShadowViewMatrix = Initializer . WorldToLight *
FMatrix (
FPlane ( 0 , 0 , 1 , 0 ) ,
FPlane ( 1 , 0 , 0 , 0 ) ,
FPlane ( 0 , 1 , 0 , 0 ) ,
FPlane ( 0 , 0 , 0 , 1 ) ) ;
2014-08-13 15:29:41 -04:00
InvReceiverMatrix = ReceiverMatrix . InverseFast ( ) ;
2014-03-14 14:13:41 -04:00
2014-05-16 20:34:56 -04:00
GetViewFrustumBounds ( ReceiverFrustum , ReceiverMatrix , true ) ;
2014-03-14 14:13:41 -04:00
UpdateShaderDepthBias ( ) ;
}
void FProjectedShadowInfo : : AddSubjectPrimitive ( FPrimitiveSceneInfo * PrimitiveSceneInfo , TArray < FViewInfo > * ViewArray )
{
QUICK_SCOPE_CYCLE_COUNTER ( STAT_AddSubjectPrimitive ) ;
2014-11-21 22:32:16 -05:00
// Ray traced shadows use the GPU managed distance field object buffers, no CPU culling should be used
2015-01-23 17:48:57 -05:00
check ( ! CascadeSettings . bRayTracedDistanceField ) ;
2014-03-14 14:13:41 -04:00
2015-01-29 13:38:57 -05:00
if ( ! ReceiverPrimitives . Contains ( PrimitiveSceneInfo )
// Far cascade only casts from primitives marked for it
& & ( ! CascadeSettings . bFarShadowCascade | | PrimitiveSceneInfo - > Proxy - > CastsFarShadow ( ) ) )
2014-11-21 22:32:16 -05:00
{
2015-01-29 13:38:57 -05:00
const FPrimitiveSceneProxy * Proxy = PrimitiveSceneInfo - > Proxy ;
2014-11-21 22:32:16 -05:00
TArray < FViewInfo * , TInlineAllocator < 1 > > Views ;
const bool bWholeSceneDirectionalShadow = IsWholeSceneDirectionalShadow ( ) ;
if ( bWholeSceneDirectionalShadow )
{
Views . Add ( DependentView ) ;
}
else
{
check ( ViewArray ) ;
for ( int32 ViewIndex = 0 ; ViewIndex < ViewArray - > Num ( ) ; ViewIndex + + )
2014-03-14 14:13:41 -04:00
{
2014-11-21 22:32:16 -05:00
Views . Add ( & ( * ViewArray ) [ ViewIndex ] ) ;
}
}
bool bOpaqueRelevance = false ;
bool bTranslucentRelevance = false ;
bool bShadowRelevance = false ;
uint32 ViewMask = 0 ;
int32 PrimitiveId = PrimitiveSceneInfo - > GetIndex ( ) ;
const auto FeatureLevel = PrimitiveSceneInfo - > Scene - > GetFeatureLevel ( ) ;
for ( int32 ViewIndex = 0 , Num = Views . Num ( ) ; ViewIndex < Num ; ViewIndex + + )
{
FViewInfo & CurrentView = * Views [ ViewIndex ] ;
FPrimitiveViewRelevance & ViewRelevance = CurrentView . PrimitiveViewRelevanceMap [ PrimitiveId ] ;
if ( ! ViewRelevance . bInitializedThisFrame )
{
if ( CurrentView . IsPerspectiveProjection ( ) )
{
// Compute the distance between the view and the primitive.
float DistanceSquared = ( Proxy - > GetBounds ( ) . Origin - CurrentView . ShadowViewMatrices . ViewOrigin ) . SizeSquared ( ) ;
bool bIsDistanceCulled = CurrentView . IsDistanceCulled (
DistanceSquared ,
Proxy - > GetMinDrawDistance ( ) ,
Proxy - > GetMaxDrawDistance ( ) ,
PrimitiveSceneInfo
) ;
if ( bIsDistanceCulled )
{
continue ;
}
}
// Compute the subject primitive's view relevance since it wasn't cached
// Update the main view's PrimitiveViewRelevanceMap
ViewRelevance = PrimitiveSceneInfo - > Proxy - > GetViewRelevance ( & CurrentView ) ;
ViewMask | = ( 1 < < ViewIndex ) ;
}
bOpaqueRelevance | = ViewRelevance . bOpaqueRelevance ;
bTranslucentRelevance | = ViewRelevance . HasTranslucency ( ) ;
bShadowRelevance | = ViewRelevance . bShadowRelevance ;
}
if ( bShadowRelevance )
{
// Update the primitive component's last render time. Allows the component to update when using bCastWhenHidden.
const float CurrentWorldTime = Views [ 0 ] - > Family - > CurrentWorldTime ;
* ( PrimitiveSceneInfo - > ComponentLastRenderTime ) = CurrentWorldTime ;
}
if ( bOpaqueRelevance & & bShadowRelevance )
{
const FBoxSphereBounds & Bounds = Proxy - > GetBounds ( ) ;
bool bDrawingStaticMeshes = false ;
if ( PrimitiveSceneInfo - > StaticMeshes . Num ( ) > 0 )
{
2015-01-19 17:34:22 -05:00
for ( int32 ViewIndex = 0 , ViewCount = Views . Num ( ) ; ViewIndex < ViewCount ; ViewIndex + + )
2014-11-21 22:32:16 -05:00
{
FViewInfo & CurrentView = * Views [ ViewIndex ] ;
const float DistanceSquared = ( Bounds . Origin - CurrentView . ShadowViewMatrices . ViewOrigin ) . SizeSquared ( ) ;
const bool bDrawShadowDepth = FMath : : Square ( Bounds . SphereRadius ) > FMath : : Square ( GMinScreenRadiusForShadowCaster ) * DistanceSquared ;
if ( ! bDrawShadowDepth )
{
// cull object if it's too small to be considered as shadow caster
continue ;
}
// Update visibility for meshes which weren't visible in the main views or were visible with static relevance
if ( ! CurrentView . PrimitiveVisibilityMap [ PrimitiveId ] | | CurrentView . PrimitiveViewRelevanceMap [ PrimitiveId ] . bStaticRelevance )
{
bool bUseExistingVisibility = false ;
if ( ! bReflectiveShadowmap ) // Don't use existing visibility for RSMs
{
for ( int32 MeshIndex = 0 ; MeshIndex < PrimitiveSceneInfo - > StaticMeshes . Num ( ) ; MeshIndex + + )
{
const FStaticMesh & StaticMesh = PrimitiveSceneInfo - > StaticMeshes [ MeshIndex ] ;
bool bMeshIsVisible = CurrentView . StaticMeshShadowDepthMap [ StaticMesh . Id ] & & StaticMesh . CastShadow ;
bUseExistingVisibility = bUseExistingVisibility | | bMeshIsVisible ;
if ( bMeshIsVisible & & bWholeSceneDirectionalShadow )
{
StaticMeshWholeSceneShadowDepthMap [ StaticMesh . Id ] = true ;
StaticMeshWholeSceneShadowBatchVisibility [ StaticMesh . Id ] = CurrentView . StaticMeshBatchVisibility [ StaticMesh . Id ] ;
}
}
}
if ( bUseExistingVisibility )
{
bDrawingStaticMeshes = true ;
}
// Don't overwrite visibility set by the main views
// This is necessary to avoid popping when transitioning between LODs, because on the frame of the transition,
// The old LOD will continue to be drawn even though a different LOD would be chosen by distance.
else
{
2015-04-24 11:20:23 -04:00
FLODMask LODToRender ;
2014-11-21 22:32:16 -05:00
int32 ForcedLODLevel = ( CurrentView . Family - > EngineShowFlags . LOD ) ? GetCVarForceLOD ( ) : 0 ;
// Add the primitive's static mesh elements to the draw lists.
if ( bReflectiveShadowmap )
{
2015-04-24 11:20:23 -04:00
int8 LODToRenderScan = - CHAR_MAX ;
2014-11-21 22:32:16 -05:00
// Force the lowest detail LOD Level in reflective shadow maps.
for ( int32 Index = 0 ; Index < PrimitiveSceneInfo - > StaticMeshes . Num ( ) ; Index + + )
{
2015-04-24 11:20:23 -04:00
LODToRenderScan = FMath : : Max < int8 > ( PrimitiveSceneInfo - > StaticMeshes [ Index ] . LODIndex , LODToRenderScan ) ;
}
if ( LODToRenderScan ! = - CHAR_MAX )
{
LODToRender . SetLOD ( LODToRenderScan ) ;
2014-11-21 22:32:16 -05:00
}
}
else
{
FPrimitiveBounds PrimitiveBounds ;
PrimitiveBounds . Origin = Bounds . Origin ;
PrimitiveBounds . SphereRadius = Bounds . SphereRadius ;
LODToRender = ComputeLODForMeshes ( PrimitiveSceneInfo - > StaticMeshes , CurrentView , PrimitiveBounds . Origin , PrimitiveBounds . SphereRadius , ForcedLODLevel ) ;
}
for ( int32 MeshIndex = 0 ; MeshIndex < PrimitiveSceneInfo - > StaticMeshes . Num ( ) ; MeshIndex + + )
{
const FStaticMesh & StaticMesh = PrimitiveSceneInfo - > StaticMeshes [ MeshIndex ] ;
2015-04-24 11:20:23 -04:00
if ( StaticMesh . CastShadow & & LODToRender . ContainsLOD ( StaticMesh . LODIndex ) )
2014-11-21 22:32:16 -05:00
{
if ( bWholeSceneDirectionalShadow )
{
StaticMeshWholeSceneShadowDepthMap [ StaticMesh . Id ] = true ;
StaticMeshWholeSceneShadowBatchVisibility [ StaticMesh . Id ] = StaticMesh . Elements . Num ( ) = = 1 ? 1 : StaticMesh . VertexFactory - > GetStaticBatchElementVisibility ( * DependentView , & StaticMesh ) ;
}
else
{
CurrentView . StaticMeshShadowDepthMap [ StaticMesh . Id ] = true ;
CurrentView . StaticMeshBatchVisibility [ StaticMesh . Id ] = StaticMesh . Elements . Num ( ) = = 1 ? 1 : StaticMesh . VertexFactory - > GetStaticBatchElementVisibility ( CurrentView , & StaticMesh ) ;
}
bDrawingStaticMeshes = true ;
}
}
}
}
}
}
if ( bDrawingStaticMeshes )
{
if ( ! bWholeSceneDirectionalShadow )
{
// Add the primitive's static mesh elements to the draw lists.
for ( int32 MeshIndex = 0 ; MeshIndex < PrimitiveSceneInfo - > StaticMeshes . Num ( ) ; MeshIndex + + )
{
FStaticMesh & StaticMesh = PrimitiveSceneInfo - > StaticMeshes [ MeshIndex ] ;
if ( StaticMesh . CastShadow )
{
const FMaterialRenderProxy * MaterialRenderProxy = StaticMesh . MaterialRenderProxy ;
const FMaterial * Material = MaterialRenderProxy - > GetMaterial ( FeatureLevel ) ;
const EBlendMode BlendMode = Material - > GetBlendMode ( ) ;
const EMaterialShadingModel ShadingModel = Material - > GetShadingModel ( ) ;
if ( ( ( ! IsTranslucentBlendMode ( BlendMode ) ) & & ShadingModel ! = MSM_Unlit ) | | ( bReflectiveShadowmap & & Material - > ShouldInjectEmissiveIntoLPV ( ) ) )
{
const bool bTwoSided = Material - > IsTwoSided ( ) | | PrimitiveSceneInfo - > Proxy - > CastsShadowAsTwoSided ( ) ;
OverrideWithDefaultMaterialForShadowDepth ( MaterialRenderProxy , Material , bReflectiveShadowmap , FeatureLevel ) ;
SubjectMeshElements . Add ( FShadowStaticMeshElement ( MaterialRenderProxy , Material , & StaticMesh , bTwoSided ) ) ;
}
}
}
}
2014-03-14 14:13:41 -04:00
}
else
{
2014-11-21 22:32:16 -05:00
// Add the primitive to the subject primitive list.
SubjectPrimitives . Add ( PrimitiveSceneInfo ) ;
2014-09-03 18:17:19 -04:00
}
2014-11-21 22:32:16 -05:00
}
2014-09-03 18:17:19 -04:00
2014-11-21 22:32:16 -05:00
// Add translucent shadow casting primitives to SubjectTranslucentPrimitives
if ( bTranslucentRelevance & & bShadowRelevance & & bTranslucentShadow )
{
SubjectTranslucentPrimitives . Add ( PrimitiveSceneInfo ) ;
2014-03-14 14:13:41 -04:00
}
}
}
bool FProjectedShadowInfo : : HasSubjectPrims ( ) const
{
return SubjectPrimitives . Num ( ) > 0 | | SubjectMeshElements . Num ( ) > 0 ;
}
void FProjectedShadowInfo : : AddReceiverPrimitive ( FPrimitiveSceneInfo * PrimitiveSceneInfo )
{
// Add the primitive to the receiver primitive list.
ReceiverPrimitives . Add ( PrimitiveSceneInfo ) ;
}
2014-12-04 12:41:25 -05:00
static TAutoConsoleVariable < int32 > CVarDisableCullShadows (
TEXT ( " foliage.DisableCullShadows " ) ,
0 ,
TEXT ( " First three bits are disable SubjectPrimitives, ReceiverPrimitives, SubjectTranslucentPrimitives " ) ) ;
2014-10-30 17:09:08 -04:00
void FProjectedShadowInfo : : GatherDynamicMeshElements ( FSceneRenderer & Renderer , FVisibleLightInfo & VisibleLightInfo , TArray < const FSceneView * > & ReusedViewsArray )
{
if ( SubjectPrimitives . Num ( ) > 0 | | ReceiverPrimitives . Num ( ) > 0 | | SubjectTranslucentPrimitives . Num ( ) > 0 )
{
// Choose an arbitrary view where this shadow's subject is relevant.
FViewInfo * FoundView = NULL ;
for ( int32 ViewIndex = 0 ; ViewIndex < Renderer . Views . Num ( ) ; ViewIndex + + )
{
FViewInfo * CheckView = & Renderer . Views [ ViewIndex ] ;
const FVisibleLightViewInfo & VisibleLightViewInfo = CheckView - > VisibleLightInfos [ LightSceneInfo - > Id ] ;
FPrimitiveViewRelevance ViewRel = VisibleLightViewInfo . ProjectedShadowViewRelevanceMap [ ShadowId ] ;
if ( ViewRel . bShadowRelevance )
{
FoundView = CheckView ;
break ;
}
}
check ( FoundView & & IsInRenderingThread ( ) ) ;
// Backup properties of the view that we will override
FMatrix OriginalViewMatrix = FoundView - > ViewMatrices . ViewMatrix ;
// Override the view matrix so that billboarding primitives will be aligned to the light
FoundView - > ViewMatrices . ViewMatrix = ShadowViewMatrix ;
// Prevent materials from getting overridden during shadow casting in viewmodes like lighting only
// Lighting only should only affect the material used with direct lighting, not the indirect lighting
FoundView - > bForceShowMaterials = true ;
ReusedViewsArray [ 0 ] = FoundView ;
2014-12-04 12:41:25 -05:00
check ( ! FoundView - > ViewMatrices . GetDynamicMeshElementsShadowCullFrustum ) ;
int32 Disable = 0 ; //CVarDisableCullShadows.GetValueOnRenderThread();
FConvexVolume NoCull ;
if ( IsWholeSceneDirectionalShadow ( ) )
{
FoundView - > ViewMatrices . PreShadowTranslation = FVector ( 0 , 0 , 0 ) ;
FoundView - > ViewMatrices . GetDynamicMeshElementsShadowCullFrustum = ( Disable & 1 ) ? & NoCull : & CascadeSettings . ShadowBoundsAccurate ;
GatherDynamicMeshElementsArray ( FoundView , Renderer , SubjectPrimitives , DynamicSubjectMeshElements , ReusedViewsArray ) ;
FoundView - > ViewMatrices . PreShadowTranslation = PreShadowTranslation ;
}
else
{
FoundView - > ViewMatrices . PreShadowTranslation = PreShadowTranslation ;
FoundView - > ViewMatrices . GetDynamicMeshElementsShadowCullFrustum = ( Disable & 1 ) ? & NoCull : & CasterFrustum ;
GatherDynamicMeshElementsArray ( FoundView , Renderer , SubjectPrimitives , DynamicSubjectMeshElements , ReusedViewsArray ) ;
}
FoundView - > ViewMatrices . GetDynamicMeshElementsShadowCullFrustum = ( Disable & 2 ) ? & NoCull : & ReceiverFrustum ;
2014-10-30 17:09:08 -04:00
GatherDynamicMeshElementsArray ( FoundView , Renderer , ReceiverPrimitives , DynamicReceiverMeshElements , ReusedViewsArray ) ;
2014-12-04 12:41:25 -05:00
FoundView - > ViewMatrices . GetDynamicMeshElementsShadowCullFrustum = ( Disable & 4 ) ? & NoCull : & CasterFrustum ;
2014-10-30 17:09:08 -04:00
GatherDynamicMeshElementsArray ( FoundView , Renderer , SubjectTranslucentPrimitives , DynamicSubjectTranslucentMeshElements , ReusedViewsArray ) ;
2014-12-04 12:41:25 -05:00
FoundView - > ViewMatrices . GetDynamicMeshElementsShadowCullFrustum = nullptr ;
2014-10-30 17:09:08 -04:00
FoundView - > bForceShowMaterials = false ;
FoundView - > ViewMatrices . ViewMatrix = OriginalViewMatrix ;
}
}
void FProjectedShadowInfo : : GatherDynamicMeshElementsArray (
FViewInfo * FoundView ,
FSceneRenderer & Renderer ,
PrimitiveArrayType & PrimitiveArray ,
TArray < FMeshBatchAndRelevance , SceneRenderingAllocator > & OutDynamicMeshElements ,
TArray < const FSceneView * > & ReusedViewsArray )
{
// Simple elements not supported in shadow passes
FSimpleElementCollector DynamicSubjectSimpleElements ;
Renderer . MeshCollector . ClearViewMeshArrays ( ) ;
Renderer . MeshCollector . AddViewMeshArrays ( FoundView , & OutDynamicMeshElements , & DynamicSubjectSimpleElements , Renderer . ViewFamily . GetFeatureLevel ( ) ) ;
const uint32 PrimitiveCount = PrimitiveArray . Num ( ) ;
for ( uint32 PrimitiveIndex = 0 ; PrimitiveIndex < PrimitiveCount ; + + PrimitiveIndex )
{
const FPrimitiveSceneInfo * PrimitiveSceneInfo = PrimitiveArray [ PrimitiveIndex ] ;
const FPrimitiveSceneProxy * PrimitiveSceneProxy = PrimitiveSceneInfo - > Proxy ;
// Lookup the primitive's cached view relevance
FPrimitiveViewRelevance ViewRelevance = FoundView - > PrimitiveViewRelevanceMap [ PrimitiveSceneInfo - > GetIndex ( ) ] ;
if ( ! ViewRelevance . bInitializedThisFrame )
{
// Compute the subject primitive's view relevance since it wasn't cached
ViewRelevance = PrimitiveSceneInfo - > Proxy - > GetViewRelevance ( FoundView ) ;
}
// Only draw if the subject primitive is shadow relevant.
if ( ViewRelevance . bShadowRelevance & & ViewRelevance . bDynamicRelevance )
{
Renderer . MeshCollector . SetPrimitive ( PrimitiveSceneInfo - > Proxy , PrimitiveSceneInfo - > DefaultDynamicHitProxyId ) ;
PrimitiveSceneInfo - > Proxy - > GetDynamicMeshElements ( ReusedViewsArray , Renderer . ViewFamily , 0x1 , Renderer . MeshCollector ) ;
}
}
}
2014-03-14 14:13:41 -04:00
/**
* @ param View view to check visibility in
* @ return true if this shadow info has any subject prims visible in the view
*/
bool FProjectedShadowInfo : : SubjectsVisible ( const FViewInfo & View ) const
{
checkSlow ( ! IsWholeSceneDirectionalShadow ( ) ) ;
for ( int32 PrimitiveIndex = 0 ; PrimitiveIndex < SubjectPrimitives . Num ( ) ; PrimitiveIndex + + )
{
const FPrimitiveSceneInfo * SubjectPrimitiveSceneInfo = SubjectPrimitives [ PrimitiveIndex ] ;
if ( View . PrimitiveVisibilityMap [ SubjectPrimitiveSceneInfo - > GetIndex ( ) ] )
{
return true ;
}
}
return false ;
}
2014-10-30 17:09:08 -04:00
/**
* Clears arrays allocated with the scene rendering allocator .
* Cached preshadows are reused across frames so scene rendering allocations will be invalid .
*/
2014-03-14 14:13:41 -04:00
void FProjectedShadowInfo : : ClearTransientArrays ( )
{
SubjectTranslucentPrimitives . Empty ( ) ;
SubjectPrimitives . Empty ( ) ;
ReceiverPrimitives . Empty ( ) ;
SubjectMeshElements . Empty ( ) ;
2014-10-30 17:09:08 -04:00
DynamicSubjectMeshElements . Empty ( ) ;
DynamicReceiverMeshElements . Empty ( ) ;
DynamicSubjectTranslucentMeshElements . Empty ( ) ;
2014-03-14 14:13:41 -04:00
}
/** Returns a cached preshadow matching the input criteria if one exists. */
TRefCountPtr < FProjectedShadowInfo > FDeferredShadingSceneRenderer : : GetCachedPreshadow (
const FLightPrimitiveInteraction * InParentInteraction ,
const FProjectedShadowInitializer & Initializer ,
const FBoxSphereBounds & Bounds ,
uint32 InResolutionX )
{
if ( ShouldUseCachePreshadows ( ) & & ! Views [ 0 ] . bIsSceneCapture )
{
const FPrimitiveSceneInfo * PrimitiveInfo = InParentInteraction - > GetPrimitiveSceneInfo ( ) ;
const FLightSceneInfo * LightInfo = InParentInteraction - > GetLight ( ) ;
const FSphere QueryBounds ( Bounds . Origin , Bounds . SphereRadius ) ;
for ( int32 ShadowIndex = 0 ; ShadowIndex < Scene - > CachedPreshadows . Num ( ) ; ShadowIndex + + )
{
TRefCountPtr < FProjectedShadowInfo > CachedShadow = Scene - > CachedPreshadows [ ShadowIndex ] ;
// Only reuse a cached preshadow if it was created for the same primitive and light
2015-01-25 13:58:27 -05:00
if ( CachedShadow - > GetParentSceneInfo ( ) = = PrimitiveInfo
& & & CachedShadow - > GetLightSceneInfo ( ) = = LightInfo
2014-03-14 14:13:41 -04:00
// Only reuse if it contains the bounds being queried, with some tolerance
& & QueryBounds . IsInside ( CachedShadow - > ShadowBounds , CachedShadow - > ShadowBounds . W * .04f )
// Only reuse if the resolution matches
& & CachedShadow - > ResolutionX = = InResolutionX
& & CachedShadow - > bAllocated )
{
// Reset any allocations using the scene rendering allocator,
// Since those will point to freed memory now that we are using the shadow on a different frame than it was created on.
CachedShadow - > ClearTransientArrays ( ) ;
return CachedShadow ;
}
}
}
// No matching cached preshadow was found
return NULL ;
}
struct FComparePreshadows
{
FORCEINLINE bool operator ( ) ( const TRefCountPtr < FProjectedShadowInfo > & A , const TRefCountPtr < FProjectedShadowInfo > & B ) const
{
if ( B - > ResolutionX * B - > ResolutionY < A - > ResolutionX * A - > ResolutionY )
{
return true ;
}
return false ;
}
} ;
/** Removes stale shadows and attempts to add new preshadows to the cache. */
void FDeferredShadingSceneRenderer : : UpdatePreshadowCache ( )
{
if ( ShouldUseCachePreshadows ( ) & & ! Views [ 0 ] . bIsSceneCapture )
{
2014-09-18 08:11:59 -04:00
SCOPE_CYCLE_COUNTER ( STAT_UpdatePreshadowCache ) ;
2014-03-14 14:13:41 -04:00
if ( Scene - > PreshadowCacheLayout . GetSizeX ( ) = = 0 )
{
// Initialize the texture layout if necessary
const FIntPoint PreshadowCacheBufferSize = GSceneRenderTargets . GetPreShadowCacheTextureResolution ( ) ;
Scene - > PreshadowCacheLayout = FTextureLayout ( 1 , 1 , PreshadowCacheBufferSize . X , PreshadowCacheBufferSize . Y , false , false ) ;
}
// Iterate through the cached preshadows, removing those that are not going to be rendered this frame
for ( int32 CachedShadowIndex = Scene - > CachedPreshadows . Num ( ) - 1 ; CachedShadowIndex > = 0 ; CachedShadowIndex - - )
{
TRefCountPtr < FProjectedShadowInfo > CachedShadow = Scene - > CachedPreshadows [ CachedShadowIndex ] ;
bool bShadowBeingRenderedThisFrame = false ;
for ( int32 LightIndex = 0 ; LightIndex < VisibleLightInfos . Num ( ) & & ! bShadowBeingRenderedThisFrame ; LightIndex + + )
{
bShadowBeingRenderedThisFrame = VisibleLightInfos [ LightIndex ] . ProjectedPreShadows . Find ( CachedShadow ) ! = INDEX_NONE ;
}
if ( ! bShadowBeingRenderedThisFrame )
{
// Must succeed, since we added it to the layout earlier
verify ( Scene - > PreshadowCacheLayout . RemoveElement (
CachedShadow - > X ,
CachedShadow - > Y ,
CachedShadow - > ResolutionX + SHADOW_BORDER * 2 ,
CachedShadow - > ResolutionY + SHADOW_BORDER * 2 ) ) ;
Scene - > CachedPreshadows . RemoveAt ( CachedShadowIndex ) ;
}
else if ( GSceneRenderTargets . bPreshadowCacheNewlyAllocated )
{
CachedShadow - > bDepthsCached = false ;
}
}
GSceneRenderTargets . bPreshadowCacheNewlyAllocated = false ;
TArray < TRefCountPtr < FProjectedShadowInfo > , SceneRenderingAllocator > UncachedPreShadows ;
// Gather a list of preshadows that can be cached
for ( int32 LightIndex = 0 ; LightIndex < VisibleLightInfos . Num ( ) ; LightIndex + + )
{
for ( int32 ShadowIndex = 0 ; ShadowIndex < VisibleLightInfos [ LightIndex ] . ProjectedPreShadows . Num ( ) ; ShadowIndex + + )
{
TRefCountPtr < FProjectedShadowInfo > CurrentShadow = VisibleLightInfos [ LightIndex ] . ProjectedPreShadows [ ShadowIndex ] ;
checkSlow ( CurrentShadow - > bPreShadow ) ;
if ( ! CurrentShadow - > bAllocatedInPreshadowCache )
{
UncachedPreShadows . Add ( CurrentShadow ) ;
}
}
}
// Sort them from largest to smallest, based on the assumption that larger preshadows will have more objects in their depth only pass
UncachedPreShadows . Sort ( FComparePreshadows ( ) ) ;
for ( int32 ShadowIndex = 0 ; ShadowIndex < UncachedPreShadows . Num ( ) ; ShadowIndex + + )
{
TRefCountPtr < FProjectedShadowInfo > CurrentShadow = UncachedPreShadows [ ShadowIndex ] ;
// Try to find space for the preshadow in the texture layout
if ( Scene - > PreshadowCacheLayout . AddElement (
CurrentShadow - > X ,
CurrentShadow - > Y ,
CurrentShadow - > ResolutionX + SHADOW_BORDER * 2 ,
CurrentShadow - > ResolutionY + SHADOW_BORDER * 2 ) )
{
// Mark the preshadow as existing in the cache
// It must now use the preshadow cache render target to render and read its depths instead of the usual shadow depth buffers
CurrentShadow - > bAllocatedInPreshadowCache = true ;
// Indicate that the shadow's X and Y have been initialized
CurrentShadow - > bAllocated = true ;
Scene - > CachedPreshadows . Add ( CurrentShadow ) ;
}
}
}
}
2014-09-12 17:21:49 -04:00
bool FSceneRenderer : : ShouldCreateObjectShadowForStationaryLight ( const FLightSceneInfo * LightSceneInfo , const FPrimitiveSceneProxy * PrimitiveSceneProxy , bool bInteractionShadowMapped ) const
2014-03-14 14:13:41 -04:00
{
const bool bCreateObjectShadowForStationaryLight =
LightSceneInfo - > bCreatePerObjectShadowsForDynamicObjects
2015-03-30 17:38:39 -04:00
& & LightSceneInfo - > IsPrecomputedLightingValid ( )
2014-03-14 14:13:41 -04:00
& & LightSceneInfo - > Proxy - > GetShadowMapChannel ( ) ! = INDEX_NONE
// Create a per-object shadow if the object does not want static lighting and needs to integrate with the static shadowing of a stationary light
// Or if the object wants static lighting but does not have a built shadowmap (Eg has been moved in the editor)
& & ( ! PrimitiveSceneProxy - > HasStaticLighting ( ) | | ! bInteractionShadowMapped ) ;
return bCreateObjectShadowForStationaryLight ;
}
void FDeferredShadingSceneRenderer : : SetupInteractionShadows (
2014-06-27 11:07:13 -04:00
FRHICommandListImmediate & RHICmdList ,
2014-03-14 14:13:41 -04:00
FLightPrimitiveInteraction * Interaction ,
FVisibleLightInfo & VisibleLightInfo ,
2014-06-03 15:53:13 -04:00
bool bStaticSceneOnly ,
2014-03-14 14:13:41 -04:00
const TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & ViewDependentWholeSceneShadows ,
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & PreShadows )
{
2014-09-18 08:11:59 -04:00
// too high on hit count to leave on
// SCOPE_CYCLE_COUNTER(STAT_SetupInteractionShadows);
2014-03-14 14:13:41 -04:00
FPrimitiveSceneInfo * PrimitiveSceneInfo = Interaction - > GetPrimitiveSceneInfo ( ) ;
FLightSceneProxy * LightProxy = Interaction - > GetLight ( ) - > Proxy ;
extern bool GUseTranslucencyShadowDepths ;
bool bShadowHandledByParent = false ;
if ( PrimitiveSceneInfo - > LightingAttachmentRoot . IsValid ( ) )
{
FAttachmentGroupSceneInfo & AttachmentGroup = Scene - > AttachmentGroups . FindChecked ( PrimitiveSceneInfo - > LightingAttachmentRoot ) ;
bShadowHandledByParent = AttachmentGroup . ParentSceneInfo & & AttachmentGroup . ParentSceneInfo - > Proxy - > LightAttachmentsAsGroup ( ) ;
}
// Shadowing for primitives with a shadow parent will be handled by that shadow parent
if ( ! bShadowHandledByParent )
{
const bool bCreateTranslucentObjectShadow = GUseTranslucencyShadowDepths & & Interaction - > HasTranslucentObjectShadow ( ) ;
const bool bCreateInsetObjectShadow = Interaction - > HasInsetObjectShadow ( ) ;
const bool bCreateObjectShadowForStationaryLight = ShouldCreateObjectShadowForStationaryLight ( Interaction - > GetLight ( ) , PrimitiveSceneInfo - > Proxy , Interaction - > IsShadowMapped ( ) ) ;
if ( Interaction - > HasShadow ( )
2015-02-07 11:26:51 -05:00
// TODO: Handle inset shadows, especially when an object is only casting a self-shadow.
2014-03-14 14:13:41 -04:00
// Only render shadows from objects that use static lighting during a reflection capture, since the reflection capture doesn't update at runtime
2014-06-03 15:53:13 -04:00
& & ( ! bStaticSceneOnly | | PrimitiveSceneInfo - > Proxy - > HasStaticLighting ( ) )
2014-03-14 14:13:41 -04:00
& & ( bCreateTranslucentObjectShadow | | bCreateInsetObjectShadow | | bCreateObjectShadowForStationaryLight ) )
{
// Create projected shadow infos
2014-06-27 11:07:13 -04:00
CreatePerObjectProjectedShadow ( RHICmdList , Interaction , bCreateTranslucentObjectShadow , bCreateInsetObjectShadow | | bCreateObjectShadowForStationaryLight , ViewDependentWholeSceneShadows , PreShadows ) ;
2014-03-14 14:13:41 -04:00
}
}
}
void FDeferredShadingSceneRenderer : : CreatePerObjectProjectedShadow (
2014-06-27 11:07:13 -04:00
FRHICommandListImmediate & RHICmdList ,
2014-03-14 14:13:41 -04:00
FLightPrimitiveInteraction * Interaction ,
bool bCreateTranslucentObjectShadow ,
bool bCreateOpaqueObjectShadow ,
const TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & ViewDependentWholeSceneShadows ,
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & OutPreShadows )
{
check ( bCreateOpaqueObjectShadow | | bCreateTranslucentObjectShadow ) ;
FPrimitiveSceneInfo * PrimitiveSceneInfo = Interaction - > GetPrimitiveSceneInfo ( ) ;
const int32 PrimitiveId = PrimitiveSceneInfo - > GetIndex ( ) ;
FLightSceneInfo * LightSceneInfo = Interaction - > GetLight ( ) ;
FVisibleLightInfo & VisibleLightInfo = VisibleLightInfos [ LightSceneInfo - > Id ] ;
// Check if the shadow is visible in any of the views.
bool bShadowIsPotentiallyVisibleNextFrame = false ;
bool bOpaqueShadowIsVisibleThisFrame = false ;
bool bSubjectIsVisible = false ;
bool bOpaqueRelevance = false ;
bool bTranslucentRelevance = false ;
bool bTranslucentShadowIsVisibleThisFrame = false ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
const FViewInfo & View = Views [ ViewIndex ] ;
// Lookup the primitive's cached view relevance
FPrimitiveViewRelevance ViewRelevance = View . PrimitiveViewRelevanceMap [ PrimitiveId ] ;
if ( ! ViewRelevance . bInitializedThisFrame )
{
// Compute the subject primitive's view relevance since it wasn't cached
ViewRelevance = PrimitiveSceneInfo - > Proxy - > GetViewRelevance ( & View ) ;
}
// Check if the subject primitive is shadow relevant.
const bool bPrimitiveIsShadowRelevant = ViewRelevance . bShadowRelevance ;
// Check if the shadow and preshadow are occluded.
const bool bOpaqueShadowIsOccluded =
! bCreateOpaqueObjectShadow | |
(
! View . bIgnoreExistingQueries & & View . State & &
2014-06-27 11:07:13 -04:00
( ( FSceneViewState * ) View . State ) - > IsShadowOccluded ( RHICmdList , PrimitiveSceneInfo - > PrimitiveComponentId , LightSceneInfo - > Proxy - > GetLightComponent ( ) , INDEX_NONE , false )
2014-03-14 14:13:41 -04:00
) ;
const bool bTranslucentShadowIsOccluded =
! bCreateTranslucentObjectShadow | |
(
! View . bIgnoreExistingQueries & & View . State & &
2014-06-27 11:07:13 -04:00
( ( FSceneViewState * ) View . State ) - > IsShadowOccluded ( RHICmdList , PrimitiveSceneInfo - > PrimitiveComponentId , LightSceneInfo - > Proxy - > GetLightComponent ( ) , INDEX_NONE , true )
2014-03-14 14:13:41 -04:00
) ;
const bool bSubjectIsVisibleInThisView = View . PrimitiveVisibilityMap [ PrimitiveSceneInfo - > GetIndex ( ) ] ;
bSubjectIsVisible | = bSubjectIsVisibleInThisView ;
// The shadow is visible if it is view relevant and unoccluded.
bOpaqueShadowIsVisibleThisFrame | = ( bPrimitiveIsShadowRelevant & & ! bOpaqueShadowIsOccluded ) ;
bTranslucentShadowIsVisibleThisFrame | = ( bPrimitiveIsShadowRelevant & & ! bTranslucentShadowIsOccluded ) ;
bShadowIsPotentiallyVisibleNextFrame | = bPrimitiveIsShadowRelevant ;
bOpaqueRelevance | = ViewRelevance . bOpaqueRelevance ;
bTranslucentRelevance | = ViewRelevance . HasTranslucency ( ) ;
}
if ( ! bOpaqueShadowIsVisibleThisFrame & & ! bTranslucentShadowIsVisibleThisFrame & & ! bShadowIsPotentiallyVisibleNextFrame )
{
// Don't setup the shadow info for shadows which don't need to be rendered or occlusion tested.
return ;
}
TArray < FPrimitiveSceneInfo * , SceneRenderingAllocator > ShadowGroupPrimitives ;
PrimitiveSceneInfo - > GatherLightingAttachmentGroupPrimitives ( ShadowGroupPrimitives ) ;
// Compute the composite bounds of this group of shadow primitives.
FBoxSphereBounds OriginalBounds = ShadowGroupPrimitives [ 0 ] - > Proxy - > GetBounds ( ) ;
for ( int32 ChildIndex = 1 ; ChildIndex < ShadowGroupPrimitives . Num ( ) ; ChildIndex + + )
{
const FPrimitiveSceneInfo * ShadowChild = ShadowGroupPrimitives [ ChildIndex ] ;
OriginalBounds = OriginalBounds + ShadowChild - > Proxy - > GetBounds ( ) ;
}
// Shadowing constants.
const uint32 MinShadowResolution = CVarMinShadowResolution . GetValueOnRenderThread ( ) ;
const uint32 MaxShadowResolutionSetting = GetCachedScalabilityCVars ( ) . MaxShadowResolution ;
const FIntPoint ShadowBufferResolution = GSceneRenderTargets . GetShadowDepthTextureResolution ( ) ;
const uint32 MaxShadowResolution = FMath : : Min < int32 > ( MaxShadowResolutionSetting , ShadowBufferResolution . X ) - SHADOW_BORDER * 2 ;
const uint32 MaxShadowResolutionY = FMath : : Min < int32 > ( MaxShadowResolutionSetting , ShadowBufferResolution . Y ) - SHADOW_BORDER * 2 ;
const int32 ShadowFadeResolution = CVarShadowFadeResolution . GetValueOnRenderThread ( ) ;
// Compute the maximum resolution required for the shadow by any view. Also keep track of the unclamped resolution for fading.
uint32 MaxDesiredResolution = 0 ;
2014-08-28 18:15:46 -04:00
float MaxUnclampedResolution = 0 ;
2014-03-14 14:13:41 -04:00
float MaxScreenPercent = 0 ;
TArray < float , TInlineAllocator < 2 > > ResolutionFadeAlphas ;
TArray < float , TInlineAllocator < 2 > > ResolutionPreShadowFadeAlphas ;
float MaxResolutionFadeAlpha = 0 ;
float MaxResolutionPreShadowFadeAlpha = 0 ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
const FViewInfo & View = Views [ ViewIndex ] ;
// Determine the size of the subject's bounding sphere in this view.
const FVector4 ScreenPosition = View . WorldToScreen ( OriginalBounds . Origin ) ;
2015-01-31 14:20:00 -05:00
const float ScreenRadius = View . ShadowViewMatrices . ScreenScale *
2014-03-14 14:13:41 -04:00
OriginalBounds . SphereRadius /
FMath : : Max ( ScreenPosition . W , 1.0f ) ;
2015-04-24 17:27:27 -04:00
// Early catch for invalid CalculateShadowFadeAlpha()
checkf ( ScreenRadius > = 0.0f , TEXT ( " View.ShadowViewMatrices.ScreenScale %f, OriginalBounds.SphereRadius %f, ScreenPosition.W %f " ) , View . ShadowViewMatrices . ScreenScale , OriginalBounds . SphereRadius , ScreenPosition . W ) ;
2014-03-14 14:13:41 -04:00
const float ScreenPercent = FMath : : Max (
2015-01-31 14:20:00 -05:00
1.0f / 2.0f * View . ShadowViewMatrices . ProjectionScale . X ,
1.0f / 2.0f * View . ShadowViewMatrices . ProjectionScale . Y
2014-03-14 14:13:41 -04:00
) *
OriginalBounds . SphereRadius /
FMath : : Max ( ScreenPosition . W , 1.0f ) ;
MaxScreenPercent = FMath : : Max ( MaxScreenPercent , ScreenPercent ) ;
// Determine the amount of shadow buffer resolution needed for this view.
2014-08-28 18:15:46 -04:00
const float UnclampedResolution = ScreenRadius * CVarShadowTexelsPerPixel . GetValueOnRenderThread ( ) ;
2014-03-14 14:13:41 -04:00
MaxUnclampedResolution = FMath : : Max ( MaxUnclampedResolution , UnclampedResolution ) ;
MaxDesiredResolution = FMath : : Max (
MaxDesiredResolution ,
FMath : : Clamp < uint32 > (
UnclampedResolution ,
FMath : : Min < int32 > ( MinShadowResolution , ShadowBufferResolution . X - SHADOW_BORDER * 2 ) ,
MaxShadowResolution
)
) ;
// Calculate fading based on resolution
const float ViewSpecificAlpha = CalculateShadowFadeAlpha ( UnclampedResolution , ShadowFadeResolution , MinShadowResolution ) ;
MaxResolutionFadeAlpha = FMath : : Max ( MaxResolutionFadeAlpha , ViewSpecificAlpha ) ;
ResolutionFadeAlphas . Add ( ViewSpecificAlpha ) ;
2014-08-28 18:15:46 -04:00
const float ViewSpecificPreShadowAlpha = CalculateShadowFadeAlpha ( UnclampedResolution * CVarPreShadowResolutionFactor . GetValueOnRenderThread ( ) , CVarPreShadowFadeResolution . GetValueOnRenderThread ( ) , CVarMinPreShadowResolution . GetValueOnRenderThread ( ) ) ;
2014-03-14 14:13:41 -04:00
MaxResolutionPreShadowFadeAlpha = FMath : : Max ( MaxResolutionPreShadowFadeAlpha , ViewSpecificPreShadowAlpha ) ;
ResolutionPreShadowFadeAlphas . Add ( ViewSpecificPreShadowAlpha ) ;
}
FBoxSphereBounds Bounds = OriginalBounds ;
const bool bRenderPreShadow =
CVarAllowPreshadows . GetValueOnRenderThread ( )
// Preshadow only affects the subject's pixels
& & bSubjectIsVisible
// Only objects with dynamic lighting should create a preshadow
// Unless we're in the editor and need to preview an object without built lighting
& & ( ! PrimitiveSceneInfo - > Proxy - > HasStaticLighting ( ) | | ! Interaction - > IsShadowMapped ( ) ) ;
if ( bRenderPreShadow & & ShouldUseCachePreshadows ( ) )
{
float PreshadowExpandFraction = FMath : : Max ( CVarPreshadowExpandFraction . GetValueOnRenderThread ( ) , 0.0f ) ;
// If we're creating a preshadow, expand the bounds somewhat so that the preshadow will be cached more often as the shadow caster moves around.
//@todo - only expand the preshadow bounds for this, not the per object shadow.
Bounds . SphereRadius + = ( Bounds . BoxExtent * PreshadowExpandFraction ) . Size ( ) ;
Bounds . BoxExtent * = PreshadowExpandFraction + 1.0f ;
}
// Compute the projected shadow initializer for this primitive-light pair.
FPerObjectProjectedShadowInitializer ShadowInitializer ;
if ( ( MaxResolutionFadeAlpha > 1.0f / 256.0f | | ( bRenderPreShadow & & MaxResolutionPreShadowFadeAlpha > 1.0f / 256.0f ) )
& & LightSceneInfo - > Proxy - > GetPerObjectProjectedShadowInitializer ( Bounds , ShadowInitializer ) )
{
const float MaxFadeAlpha = MaxResolutionFadeAlpha ;
// Only create a shadow from this object if it hasn't completely faded away
if ( CVarAllowPerObjectShadows . GetValueOnRenderThread ( ) & & MaxFadeAlpha > 1.0f / 256.0f )
{
// Round down to the nearest power of two so that resolution changes are always doubling or halving the resolution, which increases filtering stability
// Use the max resolution if the desired resolution is larger than that
const int32 SizeX = MaxDesiredResolution > = MaxShadowResolution ? MaxShadowResolution : ( 1 < < ( FMath : : CeilLogTwo ( MaxDesiredResolution ) - 1 ) ) ;
if ( bOpaqueRelevance & & bCreateOpaqueObjectShadow & & ( bOpaqueShadowIsVisibleThisFrame | | bShadowIsPotentiallyVisibleNextFrame ) )
{
// Create a projected shadow for this interaction's shadow.
2015-01-25 13:58:27 -05:00
FProjectedShadowInfo * ProjectedShadowInfo = new ( FMemStack : : Get ( ) , 1 , 16 ) FProjectedShadowInfo ;
2015-01-25 14:16:38 -05:00
if ( ProjectedShadowInfo - > SetupPerObjectProjection (
2014-03-14 14:13:41 -04:00
LightSceneInfo ,
PrimitiveSceneInfo ,
ShadowInitializer ,
2015-01-25 14:16:38 -05:00
false , // no preshadow
2014-03-14 14:13:41 -04:00
SizeX ,
MaxShadowResolutionY ,
MaxScreenPercent ,
2015-01-25 14:16:38 -05:00
false ) ) // no translucent shadow
2014-03-14 14:13:41 -04:00
{
2015-01-25 14:16:38 -05:00
ProjectedShadowInfo - > FadeAlphas = ResolutionFadeAlphas ;
VisibleLightInfo . MemStackProjectedShadows . Add ( ProjectedShadowInfo ) ;
2014-03-14 14:13:41 -04:00
if ( bOpaqueShadowIsVisibleThisFrame )
{
VisibleLightInfo . AllProjectedShadows . Add ( ProjectedShadowInfo ) ;
2015-01-25 14:16:38 -05:00
for ( int32 ChildIndex = 0 , ChildCount = ShadowGroupPrimitives . Num ( ) ; ChildIndex < ChildCount ; ChildIndex + + )
2014-03-14 14:13:41 -04:00
{
FPrimitiveSceneInfo * ShadowChild = ShadowGroupPrimitives [ ChildIndex ] ;
ProjectedShadowInfo - > AddSubjectPrimitive ( ShadowChild , & Views ) ;
}
}
else if ( bShadowIsPotentiallyVisibleNextFrame )
{
VisibleLightInfo . OccludedPerObjectShadows . Add ( ProjectedShadowInfo ) ;
}
}
}
if ( bTranslucentRelevance
2014-05-08 09:05:50 -04:00
& & Scene - > GetFeatureLevel ( ) > = ERHIFeatureLevel : : SM4
2014-03-14 14:13:41 -04:00
& & bCreateTranslucentObjectShadow
& & ( bTranslucentShadowIsVisibleThisFrame | | bShadowIsPotentiallyVisibleNextFrame ) )
{
// Create a projected shadow for this interaction's shadow.
2015-01-25 13:58:27 -05:00
FProjectedShadowInfo * ProjectedShadowInfo = new ( FMemStack : : Get ( ) , 1 , 16 ) FProjectedShadowInfo ;
2015-01-25 14:16:38 -05:00
if ( ProjectedShadowInfo - > SetupPerObjectProjection (
2014-03-14 14:13:41 -04:00
LightSceneInfo ,
PrimitiveSceneInfo ,
ShadowInitializer ,
2015-01-25 14:16:38 -05:00
false , // no preshadow
2014-11-10 15:40:54 -05:00
// Size was computed for the full res opaque shadow, convert to downsampled translucent shadow size with proper clamping
FMath : : Clamp < int32 > ( SizeX / GSceneRenderTargets . GetTranslucentShadowDownsampleFactor ( ) , 1 , GSceneRenderTargets . GetTranslucentShadowDepthTextureResolution ( ) . X - SHADOW_BORDER * 2 ) ,
FMath : : Clamp < int32 > ( MaxShadowResolutionY / GSceneRenderTargets . GetTranslucentShadowDownsampleFactor ( ) , 1 , GSceneRenderTargets . GetTranslucentShadowDepthTextureResolution ( ) . Y - SHADOW_BORDER * 2 ) ,
2014-03-14 14:13:41 -04:00
MaxScreenPercent ,
2015-01-25 14:16:38 -05:00
true ) ) // translucent shadow
2014-03-14 14:13:41 -04:00
{
2015-01-25 14:16:38 -05:00
ProjectedShadowInfo - > FadeAlphas = ResolutionFadeAlphas ,
VisibleLightInfo . MemStackProjectedShadows . Add ( ProjectedShadowInfo ) ;
2014-03-14 14:13:41 -04:00
if ( bTranslucentShadowIsVisibleThisFrame )
{
VisibleLightInfo . AllProjectedShadows . Add ( ProjectedShadowInfo ) ;
2015-01-25 14:16:38 -05:00
for ( int32 ChildIndex = 0 , ChildCount = ShadowGroupPrimitives . Num ( ) ; ChildIndex < ChildCount ; ChildIndex + + )
2014-03-14 14:13:41 -04:00
{
FPrimitiveSceneInfo * ShadowChild = ShadowGroupPrimitives [ ChildIndex ] ;
ProjectedShadowInfo - > AddSubjectPrimitive ( ShadowChild , & Views ) ;
}
}
else if ( bShadowIsPotentiallyVisibleNextFrame )
{
VisibleLightInfo . OccludedPerObjectShadows . Add ( ProjectedShadowInfo ) ;
}
}
}
}
const float MaxPreFadeAlpha = MaxResolutionPreShadowFadeAlpha ;
// If the subject is visible in at least one view, create a preshadow for static primitives shadowing the subject.
if ( MaxPreFadeAlpha > 1.0f / 256.0f
& & bRenderPreShadow
& & bOpaqueRelevance )
{
// Round down to the nearest power of two so that resolution changes are always doubling or halving the resolution, which increases filtering stability.
2014-05-06 06:26:25 -04:00
int32 PreshadowSizeX = 1 < < ( FMath : : CeilLogTwo ( FMath : : TruncToInt ( MaxDesiredResolution * CVarPreShadowResolutionFactor . GetValueOnRenderThread ( ) ) ) - 1 ) ;
2014-03-14 14:13:41 -04:00
const FIntPoint PreshadowCacheResolution = GSceneRenderTargets . GetPreShadowCacheTextureResolution ( ) ;
checkSlow ( PreshadowSizeX < = PreshadowCacheResolution . X ) ;
bool bIsOutsideWholeSceneShadow = true ;
for ( int32 i = 0 ; i < ViewDependentWholeSceneShadows . Num ( ) ; i + + )
{
const FProjectedShadowInfo * WholeSceneShadow = ViewDependentWholeSceneShadows [ i ] ;
2015-03-30 17:38:39 -04:00
const FVector2D DistanceFadeValues = WholeSceneShadow - > GetLightSceneInfo ( ) . Proxy - > GetDirectionalLightDistanceFadeParameters ( Scene - > GetFeatureLevel ( ) , WholeSceneShadow - > GetLightSceneInfo ( ) . IsPrecomputedLightingValid ( ) ) ;
2014-03-14 14:13:41 -04:00
const float DistanceFromShadowCenterSquared = ( WholeSceneShadow - > ShadowBounds . Center - Bounds . Origin ) . SizeSquared ( ) ;
//@todo - if view dependent whole scene shadows are ever supported in splitscreen,
// We can only disable the preshadow at this point if it is inside a whole scene shadow for all views
const float DistanceFromViewSquared = ( ( FVector ) WholeSceneShadow - > DependentView - > ShadowViewMatrices . ViewOrigin - Bounds . Origin ) . SizeSquared ( ) ;
// Mark the preshadow as inside the whole scene shadow if its bounding sphere is inside the near fade distance
if ( DistanceFromShadowCenterSquared < FMath : : Square ( FMath : : Max ( WholeSceneShadow - > ShadowBounds . W - Bounds . SphereRadius , 0.0f ) )
//@todo - why is this extra threshold required?
& & DistanceFromViewSquared < FMath : : Square ( FMath : : Max ( DistanceFadeValues . X - 200.0f - Bounds . SphereRadius , 0.0f ) ) )
{
bIsOutsideWholeSceneShadow = false ;
break ;
}
}
// Only create opaque preshadows when part of the caster is outside the whole scene shadow.
if ( bIsOutsideWholeSceneShadow )
{
// Try to reuse a preshadow from the cache
TRefCountPtr < FProjectedShadowInfo > ProjectedPreShadowInfo = GetCachedPreshadow ( Interaction , ShadowInitializer , OriginalBounds , PreshadowSizeX ) ;
2015-01-25 13:58:27 -05:00
if ( ! ProjectedPreShadowInfo )
2014-03-14 14:13:41 -04:00
{
// Create a new projected shadow for this interaction's preshadow
// Not using the scene rendering mem stack because this shadow info may need to persist for multiple frames if it gets cached
2015-01-25 13:58:27 -05:00
ProjectedPreShadowInfo = new FProjectedShadowInfo ;
ProjectedPreShadowInfo - > SetupPerObjectProjection (
2014-03-14 14:13:41 -04:00
LightSceneInfo ,
PrimitiveSceneInfo ,
ShadowInitializer ,
2014-11-10 15:40:54 -05:00
true , // preshadow
2014-03-14 14:13:41 -04:00
PreshadowSizeX ,
2014-05-06 06:26:25 -04:00
FMath : : TruncToInt ( MaxShadowResolutionY * CVarPreShadowResolutionFactor . GetValueOnRenderThread ( ) ) ,
2014-03-14 14:13:41 -04:00
MaxScreenPercent ,
2014-11-10 15:40:54 -05:00
false // not translucent shadow
2014-03-14 14:13:41 -04:00
) ;
}
2015-01-25 13:58:27 -05:00
// Update fade alpha on the cached preshadow
ProjectedPreShadowInfo - > FadeAlphas = ResolutionPreShadowFadeAlphas ;
2014-03-14 14:13:41 -04:00
VisibleLightInfo . AllProjectedShadows . Add ( ProjectedPreShadowInfo ) ;
VisibleLightInfo . ProjectedPreShadows . Add ( ProjectedPreShadowInfo ) ;
// Only add to OutPreShadows if the preshadow doesn't already have depths cached,
// Since OutPreShadows is used to generate information only used when rendering the shadow depths.
if ( ! ProjectedPreShadowInfo - > bDepthsCached )
{
OutPreShadows . Add ( ProjectedPreShadowInfo ) ;
}
for ( int32 ChildIndex = 0 ; ChildIndex < ShadowGroupPrimitives . Num ( ) ; ChildIndex + + )
{
FPrimitiveSceneInfo * ShadowChild = ShadowGroupPrimitives [ ChildIndex ] ;
ProjectedPreShadowInfo - > AddReceiverPrimitive ( ShadowChild ) ;
}
}
}
}
}
/** Creates a projected shadow for all primitives affected by a light. If the light doesn't support whole-scene shadows, it returns false.
* @ param LightSceneInfo - The light to create a shadow for .
* @ return true if a whole scene shadow was created
*/
void FDeferredShadingSceneRenderer : : CreateWholeSceneProjectedShadow ( FLightSceneInfo * LightSceneInfo )
{
2014-09-18 08:11:59 -04:00
SCOPE_CYCLE_COUNTER ( STAT_CreateWholeSceneProjectedShadow ) ;
2014-03-14 14:13:41 -04:00
FVisibleLightInfo & VisibleLightInfo = VisibleLightInfos [ LightSceneInfo - > Id ] ;
// Try to create a whole-scene projected shadow initializer for the light.
TArray < FWholeSceneProjectedShadowInitializer , TInlineAllocator < 6 > > ProjectedShadowInitializers ;
if ( LightSceneInfo - > Proxy - > GetWholeSceneProjectedShadowInitializer ( ViewFamily , ProjectedShadowInitializers ) )
{
checkSlow ( ProjectedShadowInitializers . Num ( ) > 0 ) ;
// Shadow resolution constants.
2015-01-23 17:48:57 -05:00
const uint32 EffectiveDoubleShadowBorder = ProjectedShadowInitializers [ 0 ] . CascadeSettings . bOnePassPointLightShadow ? 0 : SHADOW_BORDER * 2 ;
2014-03-14 14:13:41 -04:00
const int32 MinShadowResolution = CVarMinShadowResolution . GetValueOnRenderThread ( ) ;
const int32 MaxShadowResolutionSetting = GetCachedScalabilityCVars ( ) . MaxShadowResolution ;
const FIntPoint ShadowBufferResolution = GSceneRenderTargets . GetShadowDepthTextureResolution ( ) ;
const uint32 MaxShadowResolution = FMath : : Min ( MaxShadowResolutionSetting , ShadowBufferResolution . X ) - EffectiveDoubleShadowBorder ;
const uint32 MaxShadowResolutionY = FMath : : Min ( MaxShadowResolutionSetting , ShadowBufferResolution . Y ) - EffectiveDoubleShadowBorder ;
const int32 ShadowFadeResolution = CVarShadowFadeResolution . GetValueOnRenderThread ( ) ;
// Compute the maximum resolution required for the shadow by any view. Also keep track of the unclamped resolution for fading.
2014-08-28 18:15:46 -04:00
float MaxDesiredResolution = 0 ;
float MaxUnclampedResolution = 0 ;
2014-03-14 14:13:41 -04:00
TArray < float , TInlineAllocator < 2 > > FadeAlphas ;
float MaxFadeAlpha = 0 ;
2014-06-03 15:53:13 -04:00
bool bStaticSceneOnly = false ;
2014-03-14 14:13:41 -04:00
2015-01-19 15:55:17 -05:00
for ( int32 ViewIndex = 0 , ViewCount = Views . Num ( ) ; ViewIndex < ViewCount ; + + ViewIndex )
2014-03-14 14:13:41 -04:00
{
const FViewInfo & View = Views [ ViewIndex ] ;
// Determine the size of the light's bounding sphere in this view.
const FVector4 ScreenPosition = View . WorldToScreen ( LightSceneInfo - > Proxy - > GetOrigin ( ) ) ;
2015-01-31 14:20:00 -05:00
const float ScreenRadius = View . ShadowViewMatrices . ScreenScale *
2014-03-14 14:13:41 -04:00
LightSceneInfo - > Proxy - > GetRadius ( ) /
FMath : : Max ( ScreenPosition . W , 1.0f ) ;
// Determine the amount of shadow buffer resolution needed for this view.
2015-03-02 13:07:40 -05:00
const float UnclampedResolution = ScreenRadius * CVarShadowTexelsPerPixelSpotlight . GetValueOnRenderThread ( ) ;
2014-03-14 14:13:41 -04:00
MaxUnclampedResolution = FMath : : Max ( MaxUnclampedResolution , UnclampedResolution ) ;
MaxDesiredResolution = FMath : : Max (
MaxDesiredResolution ,
2014-08-28 18:15:46 -04:00
FMath : : Clamp < float > (
2014-03-14 14:13:41 -04:00
UnclampedResolution ,
2014-08-28 18:15:46 -04:00
FMath : : Min < float > ( MinShadowResolution , ShadowBufferResolution . X - EffectiveDoubleShadowBorder ) ,
2014-03-14 14:13:41 -04:00
MaxShadowResolution
)
) ;
2014-06-03 15:53:13 -04:00
bStaticSceneOnly = bStaticSceneOnly | | View . bStaticSceneOnly ;
2014-03-14 14:13:41 -04:00
const float FadeAlpha = CalculateShadowFadeAlpha ( MaxUnclampedResolution , ShadowFadeResolution , MinShadowResolution ) ;
MaxFadeAlpha = FMath : : Max ( MaxFadeAlpha , FadeAlpha ) ;
FadeAlphas . Add ( FadeAlpha ) ;
}
if ( MaxFadeAlpha > 1.0f / 256.0f )
{
2015-01-19 15:55:17 -05:00
for ( int32 ShadowIndex = 0 , ShadowCount = ProjectedShadowInitializers . Num ( ) ; ShadowIndex < ShadowCount ; ShadowIndex + + )
2014-03-14 14:13:41 -04:00
{
const FWholeSceneProjectedShadowInitializer & ProjectedShadowInitializer = ProjectedShadowInitializers [ ShadowIndex ] ;
// Round down to the nearest power of two so that resolution changes are always doubling or halving the resolution, which increases filtering stability
// Use the max resolution if the desired resolution is larger than that
int32 SizeX = MaxDesiredResolution > = MaxShadowResolution ? MaxShadowResolution : ( 1 < < ( FMath : : CeilLogTwo ( MaxDesiredResolution ) - 1 ) ) ;
2014-05-06 06:26:25 -04:00
const uint32 DesiredSizeY = FMath : : TruncToInt ( MaxDesiredResolution ) ;
2014-03-14 14:13:41 -04:00
int32 SizeY = DesiredSizeY > = MaxShadowResolutionY ? MaxShadowResolutionY : ( 1 < < ( FMath : : CeilLogTwo ( DesiredSizeY ) - 1 ) ) ;
2015-01-23 17:48:57 -05:00
if ( ProjectedShadowInitializer . CascadeSettings . bOnePassPointLightShadow )
2014-03-14 14:13:41 -04:00
{
// Round to a resolution that is supported for one pass point light shadows
SizeX = SizeY = GSceneRenderTargets . GetCubeShadowDepthZResolution ( GSceneRenderTargets . GetCubeShadowDepthZIndex ( MaxDesiredResolution ) ) ;
}
// Create the projected shadow info.
2015-01-25 13:58:27 -05:00
FProjectedShadowInfo * ProjectedShadowInfo = new ( FMemStack : : Get ( ) , 1 , 16 ) FProjectedShadowInfo ;
ProjectedShadowInfo - > SetupWholeSceneProjection (
2014-03-14 14:13:41 -04:00
LightSceneInfo ,
NULL ,
ProjectedShadowInitializer ,
SizeX ,
SizeY ,
2014-05-16 20:34:56 -04:00
false // no RSM
2014-03-14 14:13:41 -04:00
) ;
2015-01-25 13:58:27 -05:00
2014-05-16 20:34:56 -04:00
ProjectedShadowInfo - > FadeAlphas = FadeAlphas ;
2014-03-14 14:13:41 -04:00
VisibleLightInfo . MemStackProjectedShadows . Add ( ProjectedShadowInfo ) ;
VisibleLightInfo . AllProjectedShadows . Add ( ProjectedShadowInfo ) ;
2015-01-23 17:48:57 -05:00
if ( ProjectedShadowInitializer . CascadeSettings . bOnePassPointLightShadow )
2014-03-14 14:13:41 -04:00
{
const static FVector CubeDirections [ 6 ] =
{
FVector ( - 1 , 0 , 0 ) ,
FVector ( 1 , 0 , 0 ) ,
FVector ( 0 , - 1 , 0 ) ,
FVector ( 0 , 1 , 0 ) ,
FVector ( 0 , 0 , - 1 ) ,
FVector ( 0 , 0 , 1 )
} ;
const static FVector UpVectors [ 6 ] =
{
FVector ( 0 , 1 , 0 ) ,
FVector ( 0 , 1 , 0 ) ,
FVector ( 0 , 0 , - 1 ) ,
FVector ( 0 , 0 , 1 ) ,
FVector ( 0 , 1 , 0 ) ,
FVector ( 0 , 1 , 0 )
} ;
2015-01-25 13:58:27 -05:00
const FLightSceneProxy & LightProxy = * ( ProjectedShadowInfo - > GetLightSceneInfo ( ) . Proxy ) ;
const FMatrix FaceProjection = FPerspectiveMatrix ( PI / 4.0f , 1 , 1 , 1 , LightProxy . GetRadius ( ) ) ;
const FVector LightPosition = LightProxy . GetPosition ( ) ;
2014-03-14 14:13:41 -04:00
ProjectedShadowInfo - > OnePassShadowViewProjectionMatrices . Empty ( 6 ) ;
ProjectedShadowInfo - > OnePassShadowFrustums . Empty ( 6 ) ;
ProjectedShadowInfo - > OnePassShadowFrustums . AddZeroed ( 6 ) ;
const FMatrix ScaleMatrix = FScaleMatrix ( FVector ( 1 , - 1 , 1 ) ) ;
for ( int32 FaceIndex = 0 ; FaceIndex < 6 ; FaceIndex + + )
{
// Create a view projection matrix for each cube face
const FMatrix ShadowViewProjectionMatrix = FLookAtMatrix ( LightPosition , LightPosition + CubeDirections [ FaceIndex ] , UpVectors [ FaceIndex ] ) * ScaleMatrix * FaceProjection ;
ProjectedShadowInfo - > OnePassShadowViewProjectionMatrices . Add ( ShadowViewProjectionMatrix ) ;
// Create a convex volume out of the frustum so it can be used for object culling
GetViewFrustumBounds ( ProjectedShadowInfo - > OnePassShadowFrustums [ FaceIndex ] , ShadowViewProjectionMatrix , false ) ;
}
}
2014-11-21 22:32:16 -05:00
// Ray traced shadows use the GPU managed distance field object buffers, no CPU culling should be used
2015-01-23 17:48:57 -05:00
if ( ! ProjectedShadowInfo - > CascadeSettings . bRayTracedDistanceField )
2014-03-14 14:13:41 -04:00
{
2014-11-21 22:32:16 -05:00
// Add all the shadow casting primitives affected by the light to the shadow's subject primitive list.
for ( FLightPrimitiveInteraction * Interaction = LightSceneInfo - > DynamicPrimitiveList ;
Interaction ;
Interaction = Interaction - > GetNextPrimitive ( ) )
2014-03-14 14:13:41 -04:00
{
2014-11-21 22:32:16 -05:00
if ( Interaction - > HasShadow ( )
2015-02-07 11:26:51 -05:00
// If the primitive only wants to cast a self shadow don't include it in whole scene shadows.
& & ! Interaction - > CastsSelfShadowOnly ( )
2014-11-21 22:32:16 -05:00
& & ( ! bStaticSceneOnly | | Interaction - > GetPrimitiveSceneInfo ( ) - > Proxy - > HasStaticLighting ( ) ) )
{
ProjectedShadowInfo - > AddSubjectPrimitive ( Interaction - > GetPrimitiveSceneInfo ( ) , & Views ) ;
}
2014-03-14 14:13:41 -04:00
}
}
}
}
}
}
2014-09-12 17:21:49 -04:00
void FSceneRenderer : : InitProjectedShadowVisibility ( FRHICommandListImmediate & RHICmdList )
2014-03-14 14:13:41 -04:00
{
2014-09-18 08:11:59 -04:00
SCOPE_CYCLE_COUNTER ( STAT_InitProjectedShadowVisibility ) ;
2014-03-14 14:13:41 -04:00
// Initialize the views' ProjectedShadowVisibilityMaps and remove shadows without subjects.
for ( TSparseArray < FLightSceneInfoCompact > : : TConstIterator LightIt ( Scene - > Lights ) ; LightIt ; + + LightIt )
{
FVisibleLightInfo & VisibleLightInfo = VisibleLightInfos [ LightIt . GetIndex ( ) ] ;
// Allocate the light's projected shadow visibility and view relevance maps for this view.
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
FVisibleLightViewInfo & VisibleLightViewInfo = View . VisibleLightInfos [ LightIt . GetIndex ( ) ] ;
VisibleLightViewInfo . ProjectedShadowVisibilityMap . Init ( false , VisibleLightInfo . AllProjectedShadows . Num ( ) ) ;
VisibleLightViewInfo . ProjectedShadowViewRelevanceMap . Empty ( VisibleLightInfo . AllProjectedShadows . Num ( ) ) ;
VisibleLightViewInfo . ProjectedShadowViewRelevanceMap . AddZeroed ( VisibleLightInfo . AllProjectedShadows . Num ( ) ) ;
}
for ( int32 ShadowIndex = 0 ; ShadowIndex < VisibleLightInfo . AllProjectedShadows . Num ( ) ; ShadowIndex + + )
{
FProjectedShadowInfo & ProjectedShadowInfo = * VisibleLightInfo . AllProjectedShadows [ ShadowIndex ] ;
// Assign the shadow its id.
ProjectedShadowInfo . ShadowId = ShadowIndex ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
2014-10-30 17:09:08 -04:00
2014-03-14 14:13:41 -04:00
if ( ProjectedShadowInfo . DependentView & & ProjectedShadowInfo . DependentView ! = & View )
{
// The view dependent projected shadow is valid for this view if it's the
// right eye and the projected shadow is being rendered for the left eye.
2014-10-30 17:09:08 -04:00
const bool bIsValidForView = View . StereoPass = = eSSP_RIGHT_EYE
2014-03-14 14:13:41 -04:00
& & Views . IsValidIndex ( ViewIndex - 1 )
& & Views [ ViewIndex - 1 ] . StereoPass = = eSSP_LEFT_EYE
2014-07-14 14:04:46 -04:00
& & ProjectedShadowInfo . FadeAlphas . IsValidIndex ( ViewIndex )
2014-03-14 14:13:41 -04:00
& & ProjectedShadowInfo . FadeAlphas [ ViewIndex ] = = 1.0f ;
2014-10-30 17:09:08 -04:00
2014-03-14 14:13:41 -04:00
if ( ! bIsValidForView )
{
continue ;
}
}
2014-10-30 17:09:08 -04:00
2014-03-14 14:13:41 -04:00
FVisibleLightViewInfo & VisibleLightViewInfo = View . VisibleLightInfos [ LightIt . GetIndex ( ) ] ;
if ( VisibleLightViewInfo . bInViewFrustum )
{
// Compute the subject primitive's view relevance. Note that the view won't necessarily have it cached,
// since the primitive might not be visible.
FPrimitiveViewRelevance ViewRelevance ;
2015-01-25 13:58:27 -05:00
if ( ProjectedShadowInfo . GetParentSceneInfo ( ) )
2014-03-14 14:13:41 -04:00
{
2015-01-25 13:58:27 -05:00
ViewRelevance = ProjectedShadowInfo . GetParentSceneInfo ( ) - > Proxy - > GetViewRelevance ( & View ) ;
2014-03-14 14:13:41 -04:00
}
else
{
ViewRelevance . bDrawRelevance = ViewRelevance . bStaticRelevance = ViewRelevance . bDynamicRelevance = ViewRelevance . bShadowRelevance = true ;
}
VisibleLightViewInfo . ProjectedShadowViewRelevanceMap [ ShadowIndex ] = ViewRelevance ;
// Check if the subject primitive's shadow is view relevant.
const bool bPrimitiveIsShadowRelevant = ViewRelevance . bShadowRelevance ;
// Check if the shadow and preshadow are occluded.
const bool bShadowIsOccluded =
! View . bIgnoreExistingQueries & &
View . State & &
( ( FSceneViewState * ) View . State ) - > IsShadowOccluded (
2014-06-27 11:07:13 -04:00
RHICmdList ,
2015-01-25 13:58:27 -05:00
ProjectedShadowInfo . GetParentSceneInfo ( ) ?
ProjectedShadowInfo . GetParentSceneInfo ( ) - > PrimitiveComponentId :
2014-03-14 14:13:41 -04:00
FPrimitiveComponentId ( ) ,
2015-01-25 13:58:27 -05:00
ProjectedShadowInfo . GetLightSceneInfo ( ) . Proxy - > GetLightComponent ( ) ,
2015-01-23 17:48:57 -05:00
ProjectedShadowInfo . CascadeSettings . ShadowSplitIndex ,
2014-03-14 14:13:41 -04:00
ProjectedShadowInfo . bTranslucentShadow
) ;
// The shadow is visible if it is view relevant and unoccluded.
if ( bPrimitiveIsShadowRelevant & & ! bShadowIsOccluded )
{
VisibleLightViewInfo . ProjectedShadowVisibilityMap [ ShadowIndex ] = true ;
}
// Draw the shadow frustum.
if ( bPrimitiveIsShadowRelevant & & ! bShadowIsOccluded & & ! ProjectedShadowInfo . bReflectiveShadowmap )
{
bool bDrawPreshadowFrustum = CVarDrawPreshadowFrustum . GetValueOnRenderThread ( ) ! = 0 ;
if ( ( ViewFamily . EngineShowFlags . ShadowFrustums )
& & ( ( bDrawPreshadowFrustum & & ProjectedShadowInfo . bPreShadow ) | | ( ! bDrawPreshadowFrustum & & ! ProjectedShadowInfo . bPreShadow ) ) )
{
FViewElementPDI ShadowFrustumPDI ( & Views [ ViewIndex ] , NULL ) ;
if ( ProjectedShadowInfo . IsWholeSceneDirectionalShadow ( ) )
{
// Get split color
FColor Color = FColor : : White ;
2015-01-23 17:48:57 -05:00
switch ( ProjectedShadowInfo . CascadeSettings . ShadowSplitIndex )
2014-03-14 14:13:41 -04:00
{
case 0 : Color = FColor : : Red ; break ;
case 1 : Color = FColor : : Yellow ; break ;
case 2 : Color = FColor : : Green ; break ;
case 3 : Color = FColor : : Blue ; break ;
}
const FMatrix ViewMatrix = View . ViewMatrices . ViewMatrix ;
const FMatrix ProjectionMatrix = View . ViewMatrices . ProjMatrix ;
const FVector4 ViewOrigin = View . ViewMatrices . ViewOrigin ;
float AspectRatio = ProjectionMatrix . M [ 1 ] [ 1 ] / ProjectionMatrix . M [ 0 ] [ 0 ] ;
float ActualFOV = ( ViewOrigin . W > 0.0f ) ? FMath : : Atan ( 1.0f / ProjectionMatrix . M [ 0 ] [ 0 ] ) : PI / 4.0f ;
float Near = ProjectedShadowInfo . CascadeSettings . SplitNear ;
float Mid = ProjectedShadowInfo . CascadeSettings . FadePlaneOffset ;
float Far = ProjectedShadowInfo . CascadeSettings . SplitFar ;
// Camera Subfrustum
2015-01-23 11:17:06 -05:00
DrawFrustumWireframe ( & ShadowFrustumPDI , ( ViewMatrix * FPerspectiveMatrix ( ActualFOV , AspectRatio , 1.0f , Near , Mid ) ) . Inverse ( ) , Color , 0 ) ;
DrawFrustumWireframe ( & ShadowFrustumPDI , ( ViewMatrix * FPerspectiveMatrix ( ActualFOV , AspectRatio , 1.0f , Mid , Far ) ) . Inverse ( ) , FColor : : White , 0 ) ;
2014-03-14 14:13:41 -04:00
// Subfrustum Sphere Bounds
DrawWireSphere ( & ShadowFrustumPDI , FTransform ( ProjectedShadowInfo . ShadowBounds . Center ) , Color , ProjectedShadowInfo . ShadowBounds . W , 40 , 0 ) ;
// Shadow Map Projection Bounds
2015-01-23 11:17:06 -05:00
DrawFrustumWireframe ( & ShadowFrustumPDI , ProjectedShadowInfo . SubjectAndReceiverMatrix . Inverse ( ) * FTranslationMatrix ( - ProjectedShadowInfo . PreShadowTranslation ) , Color , 0 ) ;
2014-03-14 14:13:41 -04:00
}
else
{
ProjectedShadowInfo . RenderFrustumWireframe ( & ShadowFrustumPDI ) ;
}
}
}
}
}
}
}
2015-01-19 17:34:22 -05:00
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if ( GDumpShadowSetup )
{
GDumpShadowSetup = false ;
UE_LOG ( LogRenderer , Display , TEXT ( " Dump Shadow Setup: " ) ) ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
UE_LOG ( LogRenderer , Display , TEXT ( " View %d/%d " ) , ViewIndex , Views . Num ( ) ) ;
uint32 LightIndex = 0 ;
for ( TSparseArray < FLightSceneInfoCompact > : : TConstIterator LightIt ( Scene - > Lights ) ; LightIt ; + + LightIt , + + LightIndex )
{
FVisibleLightInfo & VisibleLightInfo = VisibleLightInfos [ LightIt . GetIndex ( ) ] ;
FVisibleLightViewInfo & VisibleLightViewInfo = View . VisibleLightInfos [ LightIt . GetIndex ( ) ] ;
UE_LOG ( LogRenderer , Display , TEXT ( " Light %d/%d: " ) , LightIndex , Scene - > Lights . Num ( ) ) ;
for ( int32 ShadowIndex = 0 , ShadowCount = VisibleLightInfo . AllProjectedShadows . Num ( ) ; ShadowIndex < ShadowCount ; ShadowIndex + + )
{
FProjectedShadowInfo & ProjectedShadowInfo = * VisibleLightInfo . AllProjectedShadows [ ShadowIndex ] ;
if ( VisibleLightViewInfo . bInViewFrustum )
{
2015-01-23 11:17:06 -05:00
UE_LOG ( LogRenderer , Display , TEXT ( " Shadow %d/%d: ShadowId=%d " ) , ShadowIndex , ShadowCount , ProjectedShadowInfo . ShadowId ) ;
2015-01-19 17:34:22 -05:00
UE_LOG ( LogRenderer , Display , TEXT ( " WholeSceneDir=%d SplitIndex=%d near=%f far=%f " ) ,
ProjectedShadowInfo . IsWholeSceneDirectionalShadow ( ) ,
2015-01-23 17:48:57 -05:00
ProjectedShadowInfo . CascadeSettings . ShadowSplitIndex ,
2015-01-19 17:34:22 -05:00
ProjectedShadowInfo . CascadeSettings . SplitNear ,
ProjectedShadowInfo . CascadeSettings . SplitFar ) ;
2015-01-23 11:17:06 -05:00
UE_LOG ( LogRenderer , Display , TEXT ( " bDistField=%d bFarShadows=%d Bounds=%f,%f,%f,%f " ) ,
2015-01-23 17:48:57 -05:00
ProjectedShadowInfo . CascadeSettings . bRayTracedDistanceField ,
2015-01-23 11:17:06 -05:00
ProjectedShadowInfo . CascadeSettings . bFarShadowCascade ,
ProjectedShadowInfo . ShadowBounds . Center . X ,
ProjectedShadowInfo . ShadowBounds . Center . Y ,
ProjectedShadowInfo . ShadowBounds . Center . Z ,
ProjectedShadowInfo . ShadowBounds . W ) ;
UE_LOG ( LogRenderer , Display , TEXT ( " SplitFadeRegion=%f .. %f FadePlaneOffset=%f FadePlaneLength=%f " ) ,
ProjectedShadowInfo . CascadeSettings . SplitNearFadeRegion ,
ProjectedShadowInfo . CascadeSettings . SplitFarFadeRegion ,
ProjectedShadowInfo . CascadeSettings . FadePlaneOffset ,
ProjectedShadowInfo . CascadeSettings . FadePlaneLength ) ;
2015-01-19 17:34:22 -05:00
}
}
}
}
}
# endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
2014-03-14 14:13:41 -04:00
}
2014-10-30 17:09:08 -04:00
void FSceneRenderer : : GatherShadowDynamicMeshElements ( )
{
2014-11-25 17:56:43 -05:00
TArray < const FSceneView * > ReusedViewsArray ;
ReusedViewsArray . AddZeroed ( 1 ) ;
for ( TSparseArray < FLightSceneInfoCompact > : : TConstIterator LightIt ( Scene - > Lights ) ; LightIt ; + + LightIt )
2014-10-30 17:09:08 -04:00
{
2014-11-25 17:56:43 -05:00
FVisibleLightInfo & VisibleLightInfo = VisibleLightInfos [ LightIt . GetIndex ( ) ] ;
2014-10-30 17:09:08 -04:00
2014-11-25 17:56:43 -05:00
for ( int32 ShadowIndex = 0 ; ShadowIndex < VisibleLightInfo . AllProjectedShadows . Num ( ) ; ShadowIndex + + )
2014-10-30 17:09:08 -04:00
{
2014-11-25 17:56:43 -05:00
FProjectedShadowInfo & ProjectedShadowInfo = * VisibleLightInfo . AllProjectedShadows [ ShadowIndex ] ;
2014-10-30 17:09:08 -04:00
2014-11-25 17:56:43 -05:00
ProjectedShadowInfo . GatherDynamicMeshElements ( * this , VisibleLightInfo , ReusedViewsArray ) ;
2014-10-30 17:09:08 -04:00
}
}
}
2014-09-12 17:21:49 -04:00
inline void FSceneRenderer : : GatherShadowsForPrimitiveInner (
2014-03-14 14:13:41 -04:00
const FPrimitiveSceneInfoCompact & PrimitiveSceneInfoCompact ,
const TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & PreShadows ,
const TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & ViewDependentWholeSceneShadows ,
2014-06-03 15:53:13 -04:00
bool bStaticSceneOnly )
2014-03-14 14:13:41 -04:00
{
if ( PrimitiveSceneInfoCompact . bCastDynamicShadow )
{
FPrimitiveSceneInfo * RESTRICT PrimitiveSceneInfo = PrimitiveSceneInfoCompact . PrimitiveSceneInfo ;
FPrimitiveSceneProxy * RESTRICT PrimitiveProxy = PrimitiveSceneInfoCompact . Proxy ;
const FBoxSphereBounds & PrimitiveBounds = PrimitiveSceneInfoCompact . Bounds ;
// Check if the primitive is a subject for any of the preshadows.
// Only allow preshadows from lightmapped primitives that cast both dynamic and static shadows.
if ( PreShadows . Num ( ) & & PrimitiveProxy - > CastsStaticShadow ( ) & & PrimitiveProxy - > HasStaticLighting ( ) )
{
for ( int32 ShadowIndex = 0 , Num = PreShadows . Num ( ) ; ShadowIndex < PreShadows . Num ( ) ; ShadowIndex + + )
{
FProjectedShadowInfo * RESTRICT ProjectedShadowInfo = PreShadows [ ShadowIndex ] ;
// Check if this primitive is in the shadow's frustum.
bool bInFrustum = ProjectedShadowInfo - > CasterFrustum . IntersectBox ( PrimitiveBounds . Origin , ProjectedShadowInfo - > PreShadowTranslation , PrimitiveBounds . BoxExtent ) ;
2015-01-25 13:58:27 -05:00
if ( bInFrustum & & ProjectedShadowInfo - > GetLightSceneInfoCompact ( ) . AffectsPrimitive ( PrimitiveSceneInfoCompact ) )
2014-03-14 14:13:41 -04:00
{
// Add this primitive to the shadow.
ProjectedShadowInfo - > AddSubjectPrimitive ( PrimitiveSceneInfo , & Views ) ;
}
}
}
if ( PrimitiveSceneInfoCompact . bCastDynamicShadow | | PrimitiveSceneInfoCompact . bAffectDynamicIndirectLighting )
{
for ( int32 ShadowIndex = 0 , Num = ViewDependentWholeSceneShadows . Num ( ) ; ShadowIndex < Num ; ShadowIndex + + )
{
FProjectedShadowInfo * RESTRICT ProjectedShadowInfo = ViewDependentWholeSceneShadows [ ShadowIndex ] ;
if ( ProjectedShadowInfo - > bReflectiveShadowmap & & ! PrimitiveSceneInfoCompact . bAffectDynamicIndirectLighting )
{
continue ;
}
if ( ! ProjectedShadowInfo - > bReflectiveShadowmap & & ! PrimitiveSceneInfoCompact . bCastDynamicShadow )
{
continue ;
}
2015-01-25 13:58:27 -05:00
FLightSceneProxy * RESTRICT LightProxy = ProjectedShadowInfo - > GetLightSceneInfo ( ) . Proxy ;
2014-03-14 14:13:41 -04:00
const FVector LightDirection = LightProxy - > GetDirection ( ) ;
const FVector PrimitiveToShadowCenter = ProjectedShadowInfo - > ShadowBounds . Center - PrimitiveBounds . Origin ;
// Project the primitive's bounds origin onto the light vector
const float ProjectedDistanceFromShadowOriginAlongLightDir = PrimitiveToShadowCenter | LightDirection ;
// Calculate the primitive's squared distance to the cylinder's axis
const float PrimitiveDistanceFromCylinderAxisSq = ( - LightDirection * ProjectedDistanceFromShadowOriginAlongLightDir + PrimitiveToShadowCenter ) . SizeSquared ( ) ;
// Include all primitives for movable lights, but only statically shadowed primitives from a light with static shadowing,
// Since lights with static shadowing still create per-object shadows for primitives without static shadowing.
2015-03-30 17:38:39 -04:00
if ( ( ! LightProxy - > HasStaticLighting ( ) | | ! ProjectedShadowInfo - > GetLightSceneInfo ( ) . IsPrecomputedLightingValid ( ) )
2014-03-14 14:13:41 -04:00
// Check if this primitive is in the shadow's cylinder
& & PrimitiveDistanceFromCylinderAxisSq < FMath : : Square ( ProjectedShadowInfo - > ShadowBounds . W + PrimitiveBounds . SphereRadius )
// Check if the primitive is closer than the cylinder cap toward the light
2015-01-22 20:21:43 -05:00
// next line is commented as it breaks large world shadows, if this was meant to be an optimization we should think about a better solution
//// && ProjectedDistanceFromShadowOriginAlongLightDir - PrimitiveBounds.SphereRadius < -ProjectedShadowInfo->MinPreSubjectZ
2014-03-14 14:13:41 -04:00
// If the primitive is further along the cone axis than the shadow bounds origin,
// Check if the primitive is inside the spherical cap of the cascade's bounds
& & ! ( ProjectedDistanceFromShadowOriginAlongLightDir < 0
& & PrimitiveToShadowCenter . SizeSquared ( ) > FMath : : Square ( ProjectedShadowInfo - > ShadowBounds . W + PrimitiveBounds . SphereRadius ) ) )
{
const bool bInFrustum = ProjectedShadowInfo - > CascadeSettings . ShadowBoundsAccurate . IntersectBox ( PrimitiveBounds . Origin , PrimitiveBounds . BoxExtent ) ;
if ( bInFrustum )
{
// Distance culling for RSMs
float MinScreenRadiusForShadowCaster = GMinScreenRadiusForShadowCaster ;
2014-09-03 18:17:19 -04:00
if ( ProjectedShadowInfo - > bReflectiveShadowmap )
2014-03-14 14:13:41 -04:00
{
MinScreenRadiusForShadowCaster = GMinScreenRadiusForShadowCasterRSM ;
}
bool bScreenSpaceSizeCulled = false ;
check ( ProjectedShadowInfo - > DependentView ) ;
if ( ProjectedShadowInfo - > DependentView )
{
const float DistanceSquared = ( PrimitiveBounds . Origin - ProjectedShadowInfo - > DependentView - > ShadowViewMatrices . ViewOrigin ) . SizeSquared ( ) ;
bScreenSpaceSizeCulled = FMath : : Square ( PrimitiveBounds . SphereRadius ) < FMath : : Square ( MinScreenRadiusForShadowCaster ) * DistanceSquared ;
}
2015-01-25 13:58:27 -05:00
if ( ProjectedShadowInfo - > GetLightSceneInfoCompact ( ) . AffectsPrimitive ( PrimitiveSceneInfoCompact )
2014-03-14 14:13:41 -04:00
// Exclude primitives that will create their own per-object shadow, except when rendering RSMs
& & ( ! PrimitiveProxy - > CastsInsetShadow ( ) | | ProjectedShadowInfo - > bReflectiveShadowmap )
// Exclude primitives that will create a per-object shadow from a stationary light
2015-01-25 13:58:27 -05:00
& & ! ShouldCreateObjectShadowForStationaryLight ( & ProjectedShadowInfo - > GetLightSceneInfo ( ) , PrimitiveSceneInfo - > Proxy , true )
2014-03-14 14:13:41 -04:00
// Only render shadows from objects that use static lighting during a reflection capture, since the reflection capture doesn't update at runtime
2014-06-03 15:53:13 -04:00
& & ( ! bStaticSceneOnly | | PrimitiveProxy - > HasStaticLighting ( ) )
2014-03-14 14:13:41 -04:00
& & ! bScreenSpaceSizeCulled )
{
// Add this primitive to the shadow.
ProjectedShadowInfo - > AddSubjectPrimitive ( PrimitiveSceneInfo , NULL ) ;
}
}
}
}
}
}
}
2014-09-12 17:21:49 -04:00
void FSceneRenderer : : GatherShadowPrimitives (
2014-03-14 14:13:41 -04:00
const TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & PreShadows ,
const TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & ViewDependentWholeSceneShadows ,
2014-06-03 15:53:13 -04:00
bool bStaticSceneOnly
2014-03-14 14:13:41 -04:00
)
{
SCOPE_CYCLE_COUNTER ( STAT_GatherShadowPrimitivesTime ) ;
if ( PreShadows . Num ( ) | | ViewDependentWholeSceneShadows . Num ( ) )
{
for ( int32 ShadowIndex = 0 , Num = ViewDependentWholeSceneShadows . Num ( ) ; ShadowIndex < Num ; ShadowIndex + + )
{
FProjectedShadowInfo * ProjectedShadowInfo = ViewDependentWholeSceneShadows [ ShadowIndex ] ;
checkSlow ( ProjectedShadowInfo - > DependentView ) ;
// Initialize the whole scene shadow's depth map with the shadow independent depth map from the view
ProjectedShadowInfo - > StaticMeshWholeSceneShadowDepthMap . Init ( false , Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
ProjectedShadowInfo - > StaticMeshWholeSceneShadowBatchVisibility . AddZeroed ( Scene - > StaticMeshes . GetMaxIndex ( ) ) ;
}
// Find primitives that are in a shadow frustum in the octree.
for ( FScenePrimitiveOctree : : TConstIterator < SceneRenderingAllocator > PrimitiveOctreeIt ( Scene - > PrimitiveOctree ) ;
PrimitiveOctreeIt . HasPendingNodes ( ) ;
PrimitiveOctreeIt . Advance ( ) )
{
const FScenePrimitiveOctree : : FNode & PrimitiveOctreeNode = PrimitiveOctreeIt . GetCurrentNode ( ) ;
const FOctreeNodeContext & PrimitiveOctreeNodeContext = PrimitiveOctreeIt . GetCurrentContext ( ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( STAT_ShadowOctreeTraversal ) ;
// Find children of this octree node that may contain relevant primitives.
FOREACH_OCTREE_CHILD_NODE ( ChildRef )
{
if ( PrimitiveOctreeNode . HasChild ( ChildRef ) )
{
// Check that the child node is in the frustum for at least one shadow.
const FOctreeNodeContext ChildContext = PrimitiveOctreeNodeContext . GetChildContext ( ChildRef ) ;
bool bIsInFrustum = false ;
// Check for subjects of preshadows.
if ( ! bIsInFrustum )
{
for ( int32 ShadowIndex = 0 , Num = PreShadows . Num ( ) ; ShadowIndex < Num ; ShadowIndex + + )
{
FProjectedShadowInfo * ProjectedShadowInfo = PreShadows [ ShadowIndex ] ;
// Check if this primitive is in the shadow's frustum.
if ( ProjectedShadowInfo - > CasterFrustum . IntersectBox (
ChildContext . Bounds . Center + ProjectedShadowInfo - > PreShadowTranslation ,
ChildContext . Bounds . Extent
) )
{
bIsInFrustum = true ;
break ;
}
}
}
if ( ! bIsInFrustum )
{
for ( int32 ShadowIndex = 0 , Num = ViewDependentWholeSceneShadows . Num ( ) ; ShadowIndex < Num ; ShadowIndex + + )
{
FProjectedShadowInfo * ProjectedShadowInfo = ViewDependentWholeSceneShadows [ ShadowIndex ] ;
// Check if this primitive is in the shadow's frustum.
if ( ProjectedShadowInfo - > CasterFrustum . IntersectBox (
ChildContext . Bounds . Center + ProjectedShadowInfo - > PreShadowTranslation ,
ChildContext . Bounds . Extent
) )
{
bIsInFrustum = true ;
break ;
}
}
}
if ( bIsInFrustum )
{
// If the child node was in the frustum of at least one preshadow, push it on
// the iterator's pending node stack.
PrimitiveOctreeIt . PushChild ( ChildRef ) ;
}
}
}
}
// Check all the primitives in this octree node.
for ( FScenePrimitiveOctree : : ElementConstIt NodePrimitiveIt ( PrimitiveOctreeNode . GetElementIt ( ) ) ; NodePrimitiveIt ; + + NodePrimitiveIt )
{
// gather the shadows for this one primitive
2014-06-03 15:53:13 -04:00
GatherShadowsForPrimitiveInner ( * NodePrimitiveIt , PreShadows , ViewDependentWholeSceneShadows , bStaticSceneOnly ) ;
2014-03-14 14:13:41 -04:00
}
}
for ( int32 ShadowIndex = 0 , Num = PreShadows . Num ( ) ; ShadowIndex < Num ; ShadowIndex + + )
{
FProjectedShadowInfo * ProjectedShadowInfo = PreShadows [ ShadowIndex ] ;
//@todo - sort other shadow types' subject mesh elements?
// Probably needed for good performance with non-dominant whole scene shadows (spotlightmovable)
ProjectedShadowInfo - > SortSubjectMeshElements ( ) ;
}
}
}
2014-11-21 22:32:16 -05:00
void FSceneRenderer : : AddViewDependentWholeSceneShadowsForView (
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & ShadowInfos ,
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > & ShadowInfosThatNeedCulling ,
FVisibleLightInfo & VisibleLightInfo ,
FLightSceneInfo & LightSceneInfo )
2014-09-12 17:21:49 -04:00
{
2014-09-18 08:11:59 -04:00
SCOPE_CYCLE_COUNTER ( STAT_AddViewDependentWholeSceneShadowsForView ) ;
2014-09-12 17:21:49 -04:00
// Allow each view to create a whole scene view dependent shadow
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
TArray < float , TInlineAllocator < 2 > > FadeAlphas ;
FadeAlphas . Init ( 0.0f , Views . Num ( ) ) ;
FadeAlphas [ ViewIndex ] = 1.0f ;
if ( View . StereoPass = = eSSP_LEFT_EYE
& & Views . IsValidIndex ( ViewIndex + 1 )
& & Views [ ViewIndex + 1 ] . StereoPass = = eSSP_RIGHT_EYE )
{
FadeAlphas [ ViewIndex + 1 ] = 1.0f ;
}
// If rendering in stereo mode we render shadow depths only for the left eye, but project for both eyes!
if ( View . StereoPass ! = eSSP_RIGHT_EYE )
{
2015-03-30 17:38:39 -04:00
const bool bExtraDistanceFieldCascade = LightSceneInfo . Proxy - > ShouldCreateRayTracedCascade ( View . GetFeatureLevel ( ) , LightSceneInfo . IsPrecomputedLightingValid ( ) ) ;
2015-01-23 11:17:06 -05:00
2015-03-30 17:38:39 -04:00
const int32 ProjectionCount = LightSceneInfo . Proxy - > GetNumViewDependentWholeSceneShadows ( View , LightSceneInfo . IsPrecomputedLightingValid ( ) ) + ( bExtraDistanceFieldCascade ? 1 : 0 ) ;
2015-01-23 11:17:06 -05:00
checkSlow ( INDEX_NONE = = - 1 ) ;
// todo: this code can be simplified by computing all the distances in one place - avoiding some redundant work and complexity
for ( int32 Index = 0 ; Index < ProjectionCount ; Index + + )
2014-09-12 17:21:49 -04:00
{
FWholeSceneProjectedShadowInitializer ProjectedShadowInitializer ;
2015-01-23 11:17:06 -05:00
int32 LocalIndex = Index ;
// Indexing like this puts the raytraced shadow cascade last (might not be needed)
if ( bExtraDistanceFieldCascade & & LocalIndex + 1 = = ProjectionCount )
{
LocalIndex = INDEX_NONE ;
}
2015-03-30 17:38:39 -04:00
if ( LightSceneInfo . Proxy - > GetViewDependentWholeSceneProjectedShadowInitializer ( View , LocalIndex , LightSceneInfo . IsPrecomputedLightingValid ( ) , ProjectedShadowInitializer ) )
2014-09-12 17:21:49 -04:00
{
const FIntPoint ShadowBufferResolution = GSceneRenderTargets . GetShadowDepthTextureResolution ( ) ;
// Create the projected shadow info.
2015-01-25 13:58:27 -05:00
FProjectedShadowInfo * ProjectedShadowInfo = new ( FMemStack : : Get ( ) , 1 , 16 ) FProjectedShadowInfo ;
ProjectedShadowInfo - > SetupWholeSceneProjection (
2014-09-12 17:21:49 -04:00
& LightSceneInfo ,
& View ,
ProjectedShadowInitializer ,
//@todo - remove the shadow border for whole scene shadows
ShadowBufferResolution . X - SHADOW_BORDER * 2 ,
ShadowBufferResolution . Y - SHADOW_BORDER * 2 ,
false // no RSM
) ;
ProjectedShadowInfo - > FadeAlphas = FadeAlphas ;
FVisibleLightInfo & LightViewInfo = VisibleLightInfos [ LightSceneInfo . Id ] ;
VisibleLightInfo . MemStackProjectedShadows . Add ( ProjectedShadowInfo ) ;
VisibleLightInfo . AllProjectedShadows . Add ( ProjectedShadowInfo ) ;
ShadowInfos . Add ( ProjectedShadowInfo ) ;
2014-11-21 22:32:16 -05:00
// Ray traced shadows use the GPU managed distance field object buffers, no CPU culling needed
2015-01-23 17:48:57 -05:00
if ( ! ProjectedShadowInfo - > CascadeSettings . bRayTracedDistanceField )
2014-11-21 22:32:16 -05:00
{
ShadowInfosThatNeedCulling . Add ( ProjectedShadowInfo ) ;
}
2014-09-12 17:21:49 -04:00
}
}
FSceneViewState * ViewState = ( FSceneViewState * ) View . State ;
if ( ViewState )
{
FLightPropagationVolume * LightPropagationVolume = ViewState - > GetLightPropagationVolume ( ) ;
if ( LightPropagationVolume & & View . FinalPostProcessSettings . LPVIntensity > 0 )
{
// Generate the RSM shadow info
FWholeSceneProjectedShadowInitializer ProjectedShadowInitializer ;
FLightPropagationVolume & Lpv = * LightPropagationVolume ;
if ( LightSceneInfo . Proxy - > GetViewDependentRsmWholeSceneProjectedShadowInitializer ( View , Lpv . GetBoundingBox ( ) , ProjectedShadowInitializer ) )
{
2015-01-19 15:55:17 -05:00
// moved out from the FProjectedShadowInfo constructor
2015-01-23 17:48:57 -05:00
ProjectedShadowInitializer . CascadeSettings . ShadowSplitIndex = 0 ;
2015-01-19 15:55:17 -05:00
2014-09-12 17:21:49 -04:00
const FIntPoint ShadowBufferResolution = GSceneRenderTargets . GetReflectiveShadowMapTextureResolution ( ) ;
// Create the projected shadow info.
2015-01-25 13:58:27 -05:00
FProjectedShadowInfo * ProjectedShadowInfo = new ( FMemStack : : Get ( ) , 1 , 16 ) FProjectedShadowInfo ;
ProjectedShadowInfo - > SetupWholeSceneProjection (
2014-09-12 17:21:49 -04:00
& LightSceneInfo ,
& View ,
ProjectedShadowInitializer ,
ShadowBufferResolution . X ,
ShadowBufferResolution . Y ,
2015-01-25 13:58:27 -05:00
true ) ; // RSM
2014-09-12 17:21:49 -04:00
FVisibleLightInfo & LightViewInfo = VisibleLightInfos [ LightSceneInfo . Id ] ;
VisibleLightInfo . MemStackProjectedShadows . Add ( ProjectedShadowInfo ) ;
VisibleLightInfo . AllProjectedShadows . Add ( ProjectedShadowInfo ) ;
VisibleLightInfo . ReflectiveShadowMaps . Add ( ProjectedShadowInfo ) ;
ShadowInfos . Add ( ProjectedShadowInfo ) ; // or separate list?
2014-11-21 22:32:16 -05:00
// Ray traced shadows use the GPU managed distance field object buffers, no CPU culling needed
2015-01-23 17:48:57 -05:00
if ( ! ProjectedShadowInfo - > CascadeSettings . bRayTracedDistanceField )
2014-11-21 22:32:16 -05:00
{
ShadowInfosThatNeedCulling . Add ( ProjectedShadowInfo ) ;
}
2014-09-12 17:21:49 -04:00
}
}
}
}
}
}
void FForwardShadingSceneRenderer : : InitDynamicShadows ( FRHICommandListImmediate & RHICmdList )
{
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > ViewDependentWholeSceneShadows ;
2014-11-21 22:32:16 -05:00
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > ViewDependentWholeSceneShadowsThatNeedCulling ;
2014-09-12 17:21:49 -04:00
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > PreShadows ;
{
SCOPE_CYCLE_COUNTER ( STAT_InitDynamicShadowsTime ) ;
for ( TSparseArray < FLightSceneInfoCompact > : : TConstIterator LightIt ( Scene - > Lights ) ; LightIt ; + + LightIt )
{
const FLightSceneInfoCompact & LightSceneInfoCompact = * LightIt ;
FLightSceneInfo * LightSceneInfo = LightSceneInfoCompact . LightSceneInfo ;
FVisibleLightInfo & VisibleLightInfo = VisibleLightInfos [ LightSceneInfo - > Id ] ;
// Only consider lights that may have shadows.
if ( LightSceneInfo - > ShouldRenderViewIndependentWholeSceneShadows ( ) )
{
// see if the light is visible in any view
bool bIsVisibleInAnyView = false ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
// View frustums are only checked when lights have visible primitives or have modulated shadows,
// so we don't need to check for that again here
bIsVisibleInAnyView = LightSceneInfo - > ShouldRenderLight ( Views [ ViewIndex ] ) ;
if ( bIsVisibleInAnyView )
{
break ;
}
}
if ( bIsVisibleInAnyView )
{
2014-11-21 22:32:16 -05:00
AddViewDependentWholeSceneShadowsForView ( ViewDependentWholeSceneShadows , ViewDependentWholeSceneShadowsThatNeedCulling , VisibleLightInfo , * LightSceneInfo ) ;
2014-09-12 17:21:49 -04:00
}
2015-03-02 08:00:04 -05:00
const int32 NumShadows = FMath : : Min ( VisibleLightInfo . AllProjectedShadows . Num ( ) , MAX_FORWARD_SHADOWCASCADES ) ;
2014-09-12 17:21:49 -04:00
if ( NumShadows > 0 )
{
//create the shadow depth texture and/or surface
const FIntPoint ShadowBufferResolution = GSceneRenderTargets . GetShadowDepthTextureResolution ( ) ;
const int32 MaxWide = GMaxShadowDepthBufferSizeX / ShadowBufferResolution . X ;
const int32 MaxHigh = GMaxShadowDepthBufferSizeY / ShadowBufferResolution . Y ;
const int32 NumWide = FMath : : Min ( NumShadows , MaxWide ) ;
const int32 NumHigh = FMath : : Min ( ( ( NumShadows - 1 ) / MaxWide ) + 1 , MaxHigh ) ;
const FIntPoint AtlasShadowBufferResolution ( ShadowBufferResolution . X * NumWide , ShadowBufferResolution . Y * NumHigh ) ;
GSceneRenderTargets . AllocateForwardShadingShadowDepthTarget ( AtlasShadowBufferResolution ) ;
// Allocate atlas shadow texture space to the shadows.
FTextureLayout ShadowLayout ( 1 , 1 , AtlasShadowBufferResolution . X , AtlasShadowBufferResolution . Y , false , false ) ;
for ( int32 ShadowIndex = 0 ; ShadowIndex < VisibleLightInfo . AllProjectedShadows . Num ( ) ; ShadowIndex + + )
{
FProjectedShadowInfo * ProjectedShadowInfo = VisibleLightInfo . AllProjectedShadows [ ShadowIndex ] ;
if ( ! ProjectedShadowInfo - > bRendered )
{
if ( ShadowLayout . AddElement (
ProjectedShadowInfo - > X ,
ProjectedShadowInfo - > Y ,
ProjectedShadowInfo - > ResolutionX + SHADOW_BORDER * 2 ,
ProjectedShadowInfo - > ResolutionY + SHADOW_BORDER * 2 )
)
{
ProjectedShadowInfo - > bAllocated = true ;
}
}
}
}
}
}
// Calculate visibility of the projected shadows.
InitProjectedShadowVisibility ( RHICmdList ) ;
}
// Gathers the list of primitives used to draw various shadow types
2014-11-21 22:32:16 -05:00
GatherShadowPrimitives ( PreShadows , ViewDependentWholeSceneShadowsThatNeedCulling , false ) ;
2014-10-30 17:09:08 -04:00
// Generate mesh element arrays from shadow primitive arrays
GatherShadowDynamicMeshElements ( ) ;
2014-09-12 17:21:49 -04:00
}
2014-06-27 11:07:13 -04:00
void FDeferredShadingSceneRenderer : : InitDynamicShadows ( FRHICommandListImmediate & RHICmdList )
2014-03-14 14:13:41 -04:00
{
SCOPE_CYCLE_COUNTER ( STAT_DynamicShadowSetupTime ) ;
2014-06-03 15:53:13 -04:00
bool bStaticSceneOnly = false ;
2014-03-14 14:13:41 -04:00
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
FViewInfo & View = Views [ ViewIndex ] ;
2014-06-03 15:53:13 -04:00
bStaticSceneOnly = bStaticSceneOnly | | View . bStaticSceneOnly ;
2014-03-14 14:13:41 -04:00
}
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > PreShadows ;
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > ViewDependentWholeSceneShadows ;
2014-11-21 22:32:16 -05:00
TArray < FProjectedShadowInfo * , SceneRenderingAllocator > ViewDependentWholeSceneShadowsThatNeedCulling ;
2014-03-14 14:13:41 -04:00
{
SCOPE_CYCLE_COUNTER ( STAT_InitDynamicShadowsTime ) ;
for ( TSparseArray < FLightSceneInfoCompact > : : TConstIterator LightIt ( Scene - > Lights ) ; LightIt ; + + LightIt )
{
const FLightSceneInfoCompact & LightSceneInfoCompact = * LightIt ;
FLightSceneInfo * LightSceneInfo = LightSceneInfoCompact . LightSceneInfo ;
2014-09-18 08:11:59 -04:00
FScopeCycleCounter Context ( LightSceneInfo - > Proxy - > GetStatId ( ) ) ;
2014-03-14 14:13:41 -04:00
FVisibleLightInfo & VisibleLightInfo = VisibleLightInfos [ LightSceneInfo - > Id ] ;
// Only consider lights that may have shadows.
if ( LightSceneInfoCompact . bCastStaticShadow | | LightSceneInfoCompact . bCastDynamicShadow )
{
// see if the light is visible in any view
bool bIsVisibleInAnyView = false ;
for ( int32 ViewIndex = 0 ; ViewIndex < Views . Num ( ) ; ViewIndex + + )
{
// View frustums are only checked when lights have visible primitives or have modulated shadows,
// so we don't need to check for that again here
bIsVisibleInAnyView = LightSceneInfo - > ShouldRenderLight ( Views [ ViewIndex ] ) ;
if ( bIsVisibleInAnyView )
{
break ;
}
}
if ( bIsVisibleInAnyView )
{
static const auto AllowStaticLightingVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.AllowStaticLighting " ) ) ;
const bool bAllowStaticLighting = ( ! AllowStaticLightingVar | | AllowStaticLightingVar - > GetValueOnRenderThread ( ) ! = 0 ) ;
// Only create whole scene shadows for lights that don't precompute shadowing (movable lights)
const bool bCreateShadowForMovableLight =
LightSceneInfoCompact . bCastDynamicShadow
& & ( ! LightSceneInfo - > Proxy - > HasStaticShadowing ( ) | | ! bAllowStaticLighting ) ;
// Also create a whole scene shadow for lights with precomputed shadows that are unbuilt
const bool bCreateShadowToPreviewStaticLight =
LightSceneInfo - > Proxy - > HasStaticShadowing ( )
& & LightSceneInfoCompact . bCastStaticShadow
2015-03-30 17:38:39 -04:00
& & ! LightSceneInfo - > IsPrecomputedLightingValid ( ) ;
2014-03-14 14:13:41 -04:00
// Create a whole scene shadow for lights that want static shadowing but didn't get assigned to a valid shadowmap channel due to overlap
const bool bCreateShadowForOverflowStaticShadowing =
LightSceneInfo - > Proxy - > HasStaticShadowing ( )
& & ! LightSceneInfo - > Proxy - > HasStaticLighting ( )
& & LightSceneInfoCompact . bCastStaticShadow
2015-03-30 17:38:39 -04:00
& & LightSceneInfo - > IsPrecomputedLightingValid ( )
2014-03-14 14:13:41 -04:00
& & LightSceneInfo - > Proxy - > GetShadowMapChannel ( ) = = INDEX_NONE ;
if ( bCreateShadowForMovableLight | | bCreateShadowToPreviewStaticLight | | bCreateShadowForOverflowStaticShadowing )
{
// Try to create a whole scene projected shadow.
CreateWholeSceneProjectedShadow ( LightSceneInfo ) ;
}
// Allow movable and stationary lights to create CSM, or static lights that are unbuilt
2015-01-29 13:38:57 -05:00
if ( ( ! LightSceneInfo - > Proxy - > HasStaticLighting ( ) & & LightSceneInfoCompact . bCastDynamicShadow ) | | bCreateShadowToPreviewStaticLight )
2014-03-14 14:13:41 -04:00
{
2014-11-21 22:32:16 -05:00
AddViewDependentWholeSceneShadowsForView ( ViewDependentWholeSceneShadows , ViewDependentWholeSceneShadowsThatNeedCulling , VisibleLightInfo , * LightSceneInfo ) ;
2014-03-14 14:13:41 -04:00
// Look for individual primitives with a dynamic shadow.
for ( FLightPrimitiveInteraction * Interaction = LightSceneInfo - > DynamicPrimitiveList ;
Interaction ;
Interaction = Interaction - > GetNextPrimitive ( )
)
{
2014-06-27 11:07:13 -04:00
SetupInteractionShadows ( RHICmdList , Interaction , VisibleLightInfo , bStaticSceneOnly , ViewDependentWholeSceneShadows , PreShadows ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
}
// Calculate visibility of the projected shadows.
2014-06-27 11:07:13 -04:00
InitProjectedShadowVisibility ( RHICmdList ) ;
2014-03-14 14:13:41 -04:00
}
// Clear old preshadows and attempt to add new ones to the cache
UpdatePreshadowCache ( ) ;
// Gathers the list of primitives used to draw various shadow types
2014-11-21 22:32:16 -05:00
GatherShadowPrimitives ( PreShadows , ViewDependentWholeSceneShadowsThatNeedCulling , bStaticSceneOnly ) ;
2014-10-30 17:09:08 -04:00
// Generate mesh element arrays from shadow primitive arrays
GatherShadowDynamicMeshElements ( ) ;
2014-03-14 14:13:41 -04:00
}