Position from Depth, why is Y inverted?

Maybe someone can elaborate. For a more detailed description of the problem (with images) I would invite you to read my post there:

I do have a test-project for a new (cleaned up) deferred pipeline, where I do save the viewspace depth (viewpos.z) in a buffer and want to recreate the (viewspace) position of a pixel from that depth buffer. I use a Corner Frustum together with an aligned Fulscreen Quad in order to get the View Direction of that pixel which is then multiplied by the stored depth from the gbuffer pass.

float depth = tex2D(sampler_depth, input.UV).g;
float3 position = depth * ViewDirection;

For testing purposes I also write the Viewspace Position to a buffer to compare the result. Unfortunately, it turns out, that my calculated position from depth seems to have an inverted y-axis.

I have a feeling, that his may be related to handiness. XNA is right-handed so I guess, Monogame is as well. I think I have everything correctly in that mind, but the results say otherwise.

Can anyone point me where I do the things wrong? i.e. if I invert the y-axis in the CornerFrustum, I get the correct position (according to outputing on screen, which then is equal to the buffer), but if I apply a Projection Matrix to it:

float4 ppos = mul(float4(position,1), Projection);
float2 uv = 0.5f * (ppos.xy / ppos.w) + float2(0.5f, 0.5f);

the result is all mirrored. Looks like, there is something I don’t understand fully … is it handiness? the fact, that viewspace depth is negative in XNA/monogame?

trivia: I use that position for SSAO and my current workaround is to invert the sample kernel as well to accomodate the inverted y-axis - but I am still interested why I do not get the expected result from the reconstruction of the position.

I had a look at your link.

output.ViewDirection = float3(-CornerFrustum.x * input.Position.x, CornerFrustum.y * input.Position.y, CornerFrustum.z);

What’s the justification for using a minus in front of the X, but not in front of the Y? My guess is there is none. If you remove the minus, both your X and Y will be inverted.
As it is your CornerFrustum vector is left handed. Shouldn’t CornerFrustum.Z be negative to make it right-handed? That might just be what you need to flip X and Y.

And as for your question how the uv coodinates can be correct while Y is inverted: When you convert from screen to uv coordinates you always have to flip Y (y=1-y), which you didn’t do.

Their is a bit of a difference between GL and DX that monogame doesn’t fully account for essentially you can inadvertently bypass stuff monogame does to smooth it out. Over simplified explanation is that the apparent coordinate system is top left in one and bottom left in the other. The projection matrix aligns things for clip space which has Y extending from -1 to 1 which if flipped results in 1 to -1 y of course however the Z coordinates remain the same. This gets pretty confusing fast.

Basically just think of it like this, on one Y is top down
on the other you have to convert Y = Height - Y to align them;

This is most clearly seen by Global positions on the shader that you don’t alter. Just sending in the mouse coordinates to both a open gl desktop template vs a dx desktop with the exact same code and making a shader that prints out a glow based on that position.
The results will be that one will work as expect and draw your mouse were you expect the other reverse the y position from the bottom left.

I believe the microsoft recommended way to fix it is to negate the view position and alter the vertices winding order so v0 1 2 is v0 2 1 but im not so sure about that.

I think the projection matrix really should handle this and this is the reason the projection matrix allows you to define Top Bottom Left Right but i don’t think its properly implemented to purposely flip it i dunno maybe it is. I seem to remember trying to flip the bottom and top by using it and it didn’t work right.

I usually just wing in a quick fix on the spot were its needed anyways as there are really multiple ways to deal with it and sometimes its just quicker to slop in a bandaid.

@markus That sounds logical - looks like I was lazy by looking on that quad and didn’t recognize what you mention. I would assume (it’s from a tutorial) that it is done by purpose to flip the UVs, so reprojected UV(1,0) is now in the upper left corner … to accomodate the negative z (as the quad is now flipped uv-wise - but only x-axis) … you really pointed me on something I haven’t completely understood before. thanks.

@willmotil that “bandaiding” is what I do in my currently used deferred implementation, which grew pretty chaotic and is a bit of mess. That’s why I am currently prototyping a cleaner one from scratch and trying to really understand every step it does. That is when I stumbled over that weird issue and questioning my understanding of different coordinate spaces.

Looks like I have something to do now :slight_smile: thank you - will reach back … wanted to use a linear depth anyway, so I guess I am doing this as well …

If you want to see some of the values in the shader for your matrices or other globals.
You can use this pixel shader i made to do it.

I did this just to see if i could but also to see matrix values on the shader.

To use it you add the functions into your shader file and then insert the calls into the end of your pixel shader which intercept and alter the color output. This will only work on static values or static values like matrices which are the same per pixel.

Ex,

// Display the mouse x value.
float valuetotest = MouseLocation.x; 
currentColor = FuncDrawValue(valuetotest, curpixel, currentColor, TextPosition, lineSize, lineColorWhite);

// Display the mouse y value.
float valuetotest2 = MouseLocation.y;
currentColor = FuncDrawValue(valuetotest2, curpixel, currentColor, TextPosition + float2(250.0f, -2.0f), lineSize, lineColorWhite);

// draw the lines.
currentColor = DrawLine(position.xy, currentColor, b, a, lineSize, lineColorRed);
// draw the points a
currentColor = DrawPoint(position, currentColor, a, pointSize, lineColorRed);
// display circular radius about midpoint m.
currentColor = DrawCircleAtRadius(position, currentColor, m, length(c - b) * 0.5f, 3.0f, lineColorYellow);


return currentColor;

I didn’t decode them proper using IEEE 754 so they will be a trivial bit off.