Nanite: unused VertexInterpolator node disables Opacity Mask (material renders opaque)

On Nanite meshes, placing a VertexInterpolator node in a material graph without using it (unconnected, or routed through a disabled static switch) silently breaks the Opacity Mask and the material rasterizes as fully opaque. Connecting at least one interpolator restores correct masking. Non-Nanite paths are unaffected.

Turning off r.Material.UseShaderCompilationParameters fixes the issue therefore I suspect that it was introduced in CL 51712304 “Fix conservative parameters leading to excessive amount of shaders being compiled.” submitted by @cristian.bobescu

Root cause summarized by LLM (unverified):

Two independent sources disagree on whether the material uses a vertex interpolator:

  • Presence - FMaterialCachedExpressionData::bHasVertexInterpolator is set by mere node presence in FMaterialCachedExpressionData::UpdateForExpression, it does not trace connectivity or evaluate static switches.
  • Actual usage - FMaterialCompilationOutput::bUsesVertexInterpolator is set only when the interpolator is genuinely compiled into the pixel shader.

With r.Material.UseShaderCompilationParameters=1, the two diverge:

  • Shader compilation uses usage: InitializeShaderLayoutParamsFromCompOutput sets bHasVertexInterpolator = InCompilationOutput.bUsesVertexInterpolator. IsVertexProgrammable therefore decides the material is not vertex-programmable, so only the non-vertex-UV raster permutation is compiled.
  • Runtime bin/relevance selection uses presence: FMaterialResource::HasVertexInterpolator() returns the cached-expression flag -> MaterialRelevance.bUsesVertexInterpolator -> RasterPipeline.bVertexUVs, and PackMaterialBitFlags.

The runtime requests a vertex-UV / vertex-programmable Nanite raster+shading bin that was never compiled, the lookup misses the programmable permutation, and rasterization falls back to the fixed-function opaque path - which never runs the pixel discard. The pre-existing comment at NaniteShared.h:470-473 already notes this class of conservative-flag/static-switch mismatch.

[Attachment Removed]

Hello,

Thanks for reporting this bug! I’ve created the following issue which should be visible publicly soon https://issues.unrealengine.com/issue/UE\-383600

[Attachment Removed]