Why Unlit materials cannot write GBuffer

We are implementing a feature that requires objects to tag themselves in the GBuffer during the Base Pass.

To achieve this, we modified the BasePassPixelShader.usf to write Primitive.CustomData into GBuffer.CustomData. Our implementation approach is as follows: We introduced a switch (e.g., a material usage flag) to control this behavior. In the shader, we conditionally declare the necessary macro (e.g., WRITES_CUSTOMDATA_TO_GBUFFER) and set

PIXELSHADEROUTPUT_MRT4=1 (or PIXELSHADEROUTPUT_MRT5, depending on GBUFFER_HAS_VELOCITY). We also ensured the correct render target bindings are set in the pass uniforms. Finally, we fetch the CustomData from GBufferD after the BasePass.

This implementation works correctly for all standard shading models. However, it completely fails for the Unlit shading model.

For Unlit materials, the GBuffer.CustomData output is consistently optimized away, resulting in a value of zero. This occurs even though:

  1. The translated HLSL shader code shows the correct MRT outputs, including the custom data slot.
  2. RenderDoc confirms that the binding for the CustomData render target exists.
  3. As shown in the capture in attachment, a Lit material correctly writes data to GBufferD, while an Unlit material does not, under identical conditions. Furthermore, the disassembled shader instructions do not correspond to the expected logic in the source HLSL.

My questions are:

  1. What is the recommended way to force the Unlit shading model to write to the GBuffer? Is there a specific pipeline or shader file that overrides the standard BasePass behavior for Unlit?
  2. Why does this restriction not cause any errors during the material translation process, even though the final compiled shader optimizes the write out?

Hi there,

This is happening because there is an explicit path to zero out most of the MRT outputs for unlit materials after the EncodeGBufferToMRT step. This is likely here to serve as a hint for the shader compiler for optimization; so it can optimize away a lot the encoding steps for the unlit case. You will need to modify this code as well to ensure that the outputs aren’t getting zeroed out in your case. Here is the code in question, it is in BasePassPixelShader.usf (line 2053, on stock UE 5.5):

if (GBuffer.ShadingModelID == SHADINGMODELID_UNLIT && !SUBSTRATE_ENABLED) // Do not touch what Substrate outputs
{
   Out.MRT[1] = 0;
   SetGBufferForUnlit(Out.MRT[2]);
   Out.MRT[3] = 0;
   Out.MRT[GBUFFER_HAS_VELOCITY ? 5 : 4] = 0;
   Out.MRT[GBUFFER_HAS_VELOCITY ? 6 : 5] = 0;
}

Removing the line: Out.MRT[GBUFFER_HAS_VELOCITY ? 5 : 4] = 0; fixes the output in Renderdoc.

Regards,

Lance Chaney

Hi Chaney,

Thank you very much for your generous help. It seems the answer is quite obvious and my attention was just diverted elsewhere.

No problem.