Movable Objects Not Receiving Shadows From Stationary Light

The problem and our fix if anyone hits this.

FSceneRenderer::InitDynamicShadows builds an array of projected shadows for all lights. (ViewDependentWholeSceneShadows)
A bit further down the call stack, FSceneRenderer::CreatePerObjectProjectedShadow checks that array to see if a primitive is contained within the whole scene shadow; if so, it doesn’t set up a preshadow.
However, it’s checking the whole scene shadows for all lights iterated through so far.
In our case, the first light in the iteration was a directional light with a whole scene shadow.
The second light was a spotlight which should have been creating preshadows; but the shadow receiver was inside the directional light’s whole scene shadow, so the preshadow was skipped.

My fix is in ShadowSetup.cpp, around line 1689 in FSceneRenderer::CreatePerObjectProjectedShadow:

'for (int32 i = 0; i < ViewDependentWholeSceneShadows.Num(); i++)
{
const FProjectedShadowInfo* WholeSceneShadow = ViewDependentWholeSceneShadows[i];

// ENGINE_MOD... [DLP 17 May 2017]: Only consider a whole scene shadow if it actually belongs to this light!
// This fixes movable objects not receiving shadows from stationary lights when there is a directional light in the scene.
if (WholeSceneShadow->GetLightSceneInfoCompact().LightSceneInfo != LightSceneInfo)
{
    continue;
}
// ...ENGINE_MOD [DLP 17 May 2017]

const FVector2D DistanceFadeValues = WholeSceneShadow->GetLightSceneInfo().Proxy->GetDirectionalLightDistanceFadeParameters(Scene->GetFeatureLevel(), WholeSceneShadow->GetLightSceneInfo().IsPrecomputedLightingValid());
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.GetViewOrigin() - 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;
}

}’

1 Like