First Person Rendering - Artifacts in reflections

We are experimenting with First Person Rendering in our project where we currently do not have any world space representation of our character, so reflections should not show anything - however, we’re seeing in our project, and in the First Person Shooter template - that the primitive marked as First Person for it’s First Person Primitive type partially shows up in reflections.

It looks like the tris that are visible in screen space are being used in the reflection.

Is this a known issue, is there a configuration step ive missed, or a way to work around this?

dmrxDfJzc1.png(618 KB)
UnrealEditor_1e6zRcf7G9.mp4(13.1 MB)

Steps to Reproduce

  • Using the First Person Shooter template
  • Create a ‘Chrome’ material, roughness 0.0
  • Apply it to a cube in the level
  • on BP_ShooterCharacter, set Mesh as not visible, to hide the World Space Representation
  • on BP_ShooterWeapon_Pistol set Third Person Mesh to not visible, to hide the World Space Representation
  • The player should have no primitives visible in reflections now
  • Start play and approach the Cube we applied our ‘Chrome’ material to.
  • Note the visual artefacts in the reflection of the First Person primitives, which should not be reflecting.

Hi Justin!

What you’re seeing are indeed screen space reflections (either through Lumen or actual SSR). This is a known issue/limitation, so you didn’t miss any configuration switch. If it’s a big issue and you’re able to modify engine source code, you could change the screen space tracing shader to reject hits on first person pixels. First person pixels can be identified by a bit in the gbuffer when static lighting is disabled in the project settings. We didn’t implement this because it would increase the cost of all screen traces for a feature (first person rendering) that should otherwise be fairly self contained and not impact general performance of the rest of the renderer. Let me know if you’re interested in this solution and I can dig a bit deeper to give you some more details on how to implement this.

Cheers,

Tim

Hi Tim!

Thanks for the response!

Yep, we are able to modify source - so checking the gbuffer bit for first person pixels and ignoring for screen space reflections makes sense!

If you could point me in the direction of the shader to modify and any extra details, that’d be very helpful for me and the team :slight_smile:

Cheers, Justin.

Hi Justin!

Great! So depending on whether you’re using Substrate or the legacy GBuffer path, there’s two different ways of telling whether a pixel is first person:

Substrate uses FSubstratePixelHeader::IsFirstPerson()

and the legacy GBuffer uses IsFirstPerson(FGBufferData)

Be aware that this functionality is guarded behind HasFirstPersonGBufferBit() (on the CPU) and HAS_FIRST_PERSON_GBUFFER_BIT (on the GPU), which is only true if:

  1. It’s not a mobile platform
  2. AND it’s not forward shaded
  3. AND static lighting is DISABLED (Project Settings -> Rendering -> Allow Static Lighting = off)

Now depending on whether you’re seeing the issue in Lumen reflections or old school SSR reflections, you’d need to patch up the respective shader:

For Lumen, you’ll want to look at Engine\Shaders\Private\Lumen\LumenReflectionTracing.usf in ReflectionTraceScreenTexturesCS, right after TraceScreen() and that hair strands logic and before the if (bHit) branch and insert a block of code like this:

#if HAS_FIRST_PERSON_GBUFFER_BIT
 if (bHit)
 {
   const uint2 HitCoord = (uint2)(HitUVz.xy * View.BufferSizeAndInvSize.xy);
 #if SUBSTRATE_GBUFFER_FORMAT == 1
   FSubstrateAddressing HitAddressing = GetSubstratePixelDataByteOffset(HitCoord, uint2(View.BufferSizeAndInvSize.xy), Substrate.MaxBytesPerPixel);
   FSubstratePixelHeader HitHeader   = UnpackSubstrateHeaderIn(Substrate.MaterialTextureArray, HitAddressing, Substrate.TopLayerTexture);
   const bool bHitIsFirstPerson    = HitHeader.IsFirstPerson();
 #else
   const bool bHitIsFirstPerson    = IsFirstPerson(GetGBufferDataFromSceneTextures(HitUVz.xy));
 #endif
   if (bHitIsFirstPerson)
   {
     bHit = false;
     HitUVz = LastVisibleHitUVz;  // rewind so the world-space trace resumes before the FP surface
   }
 }
 #endif

If you’re running with classic SSR, you’d need to insert a similar block of code in Engine\Shaders\Private\SSRT\SSRTReflections.usf around line 310 (in my version of the codebase) for glossy/multi ray reflections and in line 389 for single ray reflections. You might need to use GetGBufferData(HitUVz.xy) here instead of GetGBufferDataFromSceneTextures(HitUVz.xy) and you might also need to pull in the substrate headers if they’re not already included: include “/Engine/Private/Substrate/Substrate.ush”

I hope this helps you with your issue!

Cheers,

Tim

Thanks Tim!

Very helpful.

We’ll have a crack and reach out if we run in to any issues :slight_smile:

Cheers, Justin