Summary
Unreal Engine 5.8.2, built from the GitHub source release (tag 5.8.2-release). The shader at fault is byte-for-byte the same in the 5.8.4 source.
A static mesh component with Nanite on, Owner No See on and Cast Hidden Shadow on casts no shadow in its owner’s view. The owner’s camera does not draw the mesh, as intended, but its shadow is also gone. Viewed from any other camera, the same mesh casts its shadow normally. With Nanite switched off (r.Nanite 0, or Disallow Nanite on the component), the shadow appears in the owner’s view as expected. So the same two settings give a shadow without Nanite and none with it.
Use case: a player’s vehicle hull is hidden from its own pilot in first-person (cockpit) view, and should still cast its shadow onto the world and into the cockpit. This is the documented purpose of Cast Hidden Shadow.
What Type of Bug are you experiencing?
Rendering (Graphics / Niagara)
Steps to Reproduce
- A pawn whose static mesh component has Nanite on, Cast Shadow on and Cast Hidden Shadow on. Owner No See is turned on at run time for the owning player’s first-person view.
- One movable directional light with Cast Shadows on. Virtual shadow maps are on, ray-traced shadows off.
- Play as a client, possess the pawn, and place it so the sun is behind it and its shadow should fall on a nearby surface.
- In first-person view (Owner No See on): no shadow from the mesh.
- Switch to a third-person view (Owner No See off): the mesh’s shadow is on the surface.
- Back in first-person view, enter
r.Nanite 0: the mesh’s shadow appears, with virtual shadow maps still on.
Checked by switching paths in the running game: r.Shadow.Virtual.Enable 0 (shadow maps without virtual shadow maps) still gives no shadow; turning MegaLights off changes nothing; only r.Nanite 0 brings the shadow back.
Expected Result
The mesh casts its shadow in the owner’s view, because Cast Hidden Shadow is on.
Observed Result
No shadow in the owner’s view while the mesh is drawn with Nanite.
Affects Versions
5.8
Platform(s)
Windows
Upload an image
Additional Notes
Project settings in play: Nanite on; virtual shadow maps on; Lumen with hardware ray tracing; MegaLights on for the project (not used by the sun: directional lights are left out by default).
Cause, as read in the 5.8.2 source:
Engine/Shaders/Private/Nanite/NaniteInstanceCulling.usf, line 103:if (bIsShadowPass && bCastHiddenShadow) { return true; }lets a hidden-shadow caster through a shadow pass, as intended.- The same file, lines 329 to 340: for each view, any primitive marked hidden in
NaniteOwnershipVisibility.HiddenPrimitivesfor that view’s primary view is skipped (continue). There is no shadow-pass exception and no Cast Hidden Shadow check, so this undoes line 103 for owner-hidden meshes. Engine/Source/Runtime/Renderer/Private/Nanite/NaniteOwnershipVisibilitySceneExtension.cpp, line 111: the hidden mark is(bIsOwnedByView && bIsOwnerNoSee) || (!bIsOwnedByView && bIsOnlyOwnerSee). Cast Hidden Shadow is not considered.- For comparison, the non-Nanite path,
Engine/Source/Runtime/Engine/Private/PrimitiveSceneProxy.cpp,IsShadowCast, skips the owner tests when Cast Hidden Shadow is on. Its comment at line 1665 reads: “This assumes that bCastHiddenShadow trumps the owner see flags.”
A possible fix: in NaniteInstanceCulling.usf, skip the ownership test at lines 329 to 340 in a shadow pass when the primitive casts hidden shadows, as line 103 already does.
Workaround we plan to use: on the owning machine only, while the mesh is owner-hidden, switch the component off Nanite at run time (UStaticMeshComponent::SetForceDisableNanite(true)), so its shadow goes through the non-Nanite path. r.Nanite 0 shows this works; the per-component switch is not yet tested.


