ClipToWorld transform in materials does not behave as expected

On upgrade from UE5.3 to UE5.6 some of the materials that use absolute world location got broken, I traced the problem to ClipToWorld transform not behaving correctly anymore. I guess this is caused by the move from Tile/Offset to DoubleFloat there

This code converts location to clip space correctly, but conversion back to world space fails, see screenshot.

I’ve tried to replace `LWCToFloat` with `DFDemote`, it haven’t change the outcome, and from what I see `LWCToFloat` is an alias to `DFDemote` anyway

float4 ObjectClipPos = mul(float4(0.0f,0.0f,0.0f, 1.0f), LWCToFloat(ResolvedView.WorldToClip));
ObjectClipPos /= ObjectClipPos.w;
float4 NewObjectWorldPos = mul(ObjectClipPos,  LWCToFloat(ResolvedView.ClipToWorld));
NewObjectWorldPos /= NewObjectWorldPos.w;
return NewObjectWorldPos.xyz;

This code worked as expected in 5.3, resulting in all zeroes on the debug node preview. I could fix this particular case by moving to camera-relative transform, but I would welcome suggestions on how to fix ClipToWorld computation to make it correct

[Image Removed]

[Attachment Removed]

Steps to Reproduce

  1. Open any material
  2. Add Custom node
  3. Add code below to the custom node
  4. Connect output to DebugFloat3Values
  5. Preview node on a cube or plane

Expected outcome: values printed are zeroes, or very small

Actual outcome: large values

float4 ObjectClipPos = mul(float4(0.0f,0.0f,0.0f, 1.0f), LWCToFloat(ResolvedView.WorldToClip));
ObjectClipPos /= ObjectClipPos.w;
float4 NewObjectWorldPos = mul(ObjectClipPos, LWCToFloat(ResolvedView.ClipToWorld));
NewObjectWorldPos /= NewObjectWorldPos.w;
return NewObjectWorldPos.xyz;

[Attachment Removed]

Hi there,

Thank you for the clear repro steps. I’ve tested them and I can confirm this happens consistently. With a small amount of digging I wasn’t able to find a clear reason for this. So I’ve reported it as a bug. You should be able to track it here once it has been made publicly available. A engine dev will investigate the bug at a later date. We don’t provide updates on UDN, but progress can be followed on that public issue tracker page. I’m closing this case, but feel free to respond here if you have follow-up questions or want to look into short-term workarounds.

Cheers,

- Louis

[Attachment Removed]

Hi Vasily, this is indeed an issue with FDFInverseMatrix. My recommendation is, in order:

  • Avoid using the Custom node. It disables analytic derivatives and is more likely to break on engine upgrades. We do have the TransformPosition node, which possibly could be used here.
  • Use a relative space (camera-space / local space / translated world space / …) instead of absolute worldspace. For example, ResolvedView.TranslatedWorldToClip.
  • If you want to keep the current structure, this should work:

float4 ClipPos = DFFastMultiplyDemote(DFPromote(float4(0.0f,0.0f,0.0f,1.0f)), ResolvedView.WorldToClip);

ClipPos.xyz /= ClipPos.w;

ClipPos.xyz *= ClipPos.w;

float4 NewWorldPos = DFDemote(DFMultiply(ClipPos, ResolvedView.ClipToWorld));

[Attachment Removed]