[5.8.3] Instanced Stereo: Volume-domain materials are only voxelized into Volumetric Fog for the left eye (regression from 5.5)

Summary

With Instanced Stereo enabled, primitives using a Volume material domain (meshes injected into Volumetric Fog,
e.g. the Ultra Volumetrics marketplace pack) only appear in the left eye. The right eye shows the
Exponential Height Fog’s own volumetric fog, but none of the volume-material primitives. The same project/content
renders correctly in both eyes in UE 5.5.

What Type of Bug are you experiencing?

Rendering (Graphics / Niagara)

Steps to Reproduce

  1. New map. Add an Exponential Height Fog, enable Volumetric Fog.
  2. Add a mesh (e.g. a cube) with a material whose Material Domain = Volume (any non-zero Extinction/Albedo),
    placed in front of the player start (inside the Volumetric Fog distance).
    • Confirmed with Ultra Volumetrics’ BP_UltraVolumetrics (its Fog cube uses a Volume-domain material).
  3. Project Settings: Instanced Stereo ON.
  4. Play in VR Preview (or package and run with -emulatestereo).

Expected Result

The volume-material fog is visible in both eyes (as in 5.5).

Observed Result

  • The Exponential Height Fog’s volumetric fog renders in both eyes.
  • The Volume-domain primitives render only in the left eye. The right eye has no contribution from them.
  • Disabling volumetric fog (r.VolumetricFog 0) removes the effect from both eyes (so it is the voxelization input, not
    the fog composite). r.Fog.SeparateComposition 0 and r.MegaLights.Volume 0 make no difference.

Affects Versions

5.8

Platform(s)

OpenXR

Upload an image

Additional Notes

Screenshots (same project and map, Ultra Volumetrics fog volume, -emulatestereo, left half = left eye)

  • FogTest_UE5.5_emulatestereo.png - UE 5.5: fog in both eyes.
    On-screen stereo info: “Stereo rendering method: Multi-viewport”.
  • FogTest_UE5.8_emulatestereo.png - UE 5.8: fog in the left eye only.
    On-screen stereo info: “Stereo rendering method: Instanced-Stereo” (the stereo path itself changed between versions).
  • Both builds use the SAME project config (vr.InstancedStereo=True). The label differs only because 5.8’s
    SceneRendering.cpp stats text prints “Instanced-Stereo” for single-pass stereo, while 5.5 printed the
    implementation (“Multi-viewport” = 5.5’s instanced stereo on D3D12). It reflects the bIsMultiViewportEnabled →
    bIsSinglePassStereo refactor, not a different setting.

Root cause (from reading the 5.8 source)

In 5.8 the secondary (right-eye) view no longer computes its own relevance under single-pass/instanced stereo:

  • Engine/Source/Runtime/Renderer/Private/SceneVisibility.cpp, FVisibilityTaskData::LaunchVisibilityTasks (~line 4477):
    secondary views are skipped (if (bIsSinglePassStereo && StereoPass == eSSP_SECONDARY) continue;).
  • Same file, ~line 2367-2400 (if (View.bIsSinglePassStereo) block, comment “Secondary views should not calculate
    relevance”
    ): the primary view copies its results to View.GetInstancedView() - PrimitiveVisibilityMap,
    PrimitiveViewRelevanceMap, StaticMeshVisibilityMap, translucency / custom depth / Substrate flags, etc.
    VolumetricMeshBatches is not copied (neither are HeterogeneousVolumesMeshBatches and SkyMeshBatches), so the
    secondary view’s list stays empty.
  • VolumetricFogVoxelization.cpp, VoxelizeFogVolumePrimitives (~line 706) runs per view and early-outs on
    View.VolumetricMeshBatches.Num() > 0, with instanced stereo forced off for that pass
    (bForceInstanceStereoOff = true), so the right eye’s fog volume never receives the volume primitives.

In 5.5 the secondary view still ran its own relevance (SceneVisibility.cpp ~line 3554-3561: the secondary relevance
command pipe forwards to the primary AND calls Relevance.Context->AddPrimitives(...) for itself), so both views had
their VolumetricMeshBatches.

Suggested fix

In the 5.8 bIsSinglePassStereo copy block, also give the instanced view the volume mesh lists, e.g.
SecondaryView->VolumetricMeshBatches = View.VolumetricMeshBatches; (and likewise
HeterogeneousVolumesMeshBatches, probably SkyMeshBatches), after relevance has been finalized for the primary view.
(The dynamic-mesh path - ComputeDynamicMeshRelevance / GatherDynamicMeshElements, which marks secondary-visible
dynamic primitives on the primary only - likely needs the same treatment.)

Here is a plugin made by claude to fix this issue.

UE5.8_StereoFogFix.zip (3.7 KB)