Inverse Projection Matrix - Custom HLSL Node

Is this the correct way to construct an inverse projection matrix? Projections and matrices are an elusive concept to me. I’ve basically just been trying to piece together the correct solution based on info I could find on the subject, so I’m not entirely sure it’s 100% correct.

/////
// inputs: CameraPos (from CameraPositionWS)
/////

float2 uv = GetDefaultSceneTextureUV(Parameters, 0);

float depth = SceneTextureLookup(uv, 1, false).r;

// normalized device coordinates
float4 ndcPos;
ndcPos.x = uv.x * 2.0 - 1.0;           
ndcPos.y = (1.0 - uv.y) * 2.0 - 1.0;   
ndcPos.z = depth;                 
ndcPos.w = 1.0;

// translated world space
float4 worldPosTranslated4 = mul(ndcPos, View.ScreenToTranslatedWorld);
float3 worldPosTranslated = worldPosTranslated4.xyz / worldPosTranslated4.w;

// absolute world position
float3 worldPosAbsolute = worldPosTranslated + CameraPos;

Hi. Why exactly do you need an inverse projection matrix? Like what are you trying to achieve with it?

For the sake of learning and understanding spatial awareness in shaders.

I see. Well, I don’t think I know the exact answer you’re looking for. About a month ago, I was trying to get the world position from the UV. For that, I would also need an inverse projection matrix (I thought). In the end, I didn’t find it, but I used a slightly different one that worked. You can check out my answer here to see what I ended up with.

Thanks for sharing that link. I’ve tested mine in a couple depth-aware algorithms too and it seems to work how I expect. I wonder if we’re just using different entry points in the API to achieve a similar result. For example we’re both building Normalized Device Coordinates based on the pixel’s screen position and depth. At which point we both translate from clip space to world space, but use different entry points.

As far as I can tell from the digging I’ve done, View.ScreenToTranslatedWorld and ResolvedView.ClipToTranslatedWorld are functionally equivalent. My assumption is there isn’t a singular entry point because of how they’re exposing HLSL to the Material Editor, but I’m just speculating.

And I think my last two lines are doing the same as your DFFastSubtract() line too. But I can’t find any documentation on it. I’m not sure about the .High component either, high-precision maybe? I wonder if your implementation using this function is safer or more precise for Unreal’s Large World Coordinates. I don’t know.

I’m actually surprised that your code works for you. I mean, I tested it and it didn’t give the result I expected to get. By this I mean it gave a different result than using a pure Absolute World Position node, although I think it should have led to the same result.

They are not really the same. ScreenToTranslatedWorld is a combination of two matrices: ScreenToClip * ClipToTranslatedWorld.

You are right. I don’t remember exactly why I used my logic instead of yours. I think it was just because I didn’t want to use the extra “CameraPos” input pin, wanted to have as little inputs as possible for convenience, that’s all.

My implementation might be a little bit more precise, but I didn’t notice much difference between the two different approaches. As for security, I think both solutions should be equally safe to use.