How to use OpenGL orthographic projection with the depth buffer?
Asked Answered
K

2

6

I've rendered a 3D scene using glFrustum() perspective mode. I then have a 2D object that I place over the 3D scene to act as a label for a particular 3D object. I have calculated the 2D position of the 3D object using using gluProject() at which position I then place my 2D label object. The 2D label object is rendered using glOrtho() orthographic mode. This works perfectly and the 2D label object hovers over the 3D object.

Now, what I want to do is to give the 2D object a z value so that it can be hidden behind other 3D objects in the scene using the depth buffer. I have given the 2D object a z value that I know should be hidden by the depth buffer, but when I render the object it is always visible.

So the question is, why is the 2D object still visible and not hidden?

I did read somewhere that orthographic and perspective projections store incompatible depth buffer values. Is this true, and if so how do I convert between them?

Kragh answered 24/1, 2012 at 16:51 Comment(4)
If you want the label to act like a normal 3D object (hidden behind others and such) why not just draw it like a normal 3D object directly above (or beside, whatever) the object being labeled?Worldling
I don't want it to act like a normal 3D object. I don't want it to be transformed, instead I want it to appear as flat 2D label that always faces the camera and remains the same size on the screen at all times. However, if it is hidden behind something I want it to appear hidden.Kragh
How do you set the 2D object depth? Submitting Z values or using PolygonOffset?Labarum
I am simply setting the object's z value to a large -ve number that should in theory be hidden behind all 3D objects in the sceneKragh
A
11

I don't want it to be transformed, instead I want it to appear as flat 2D label that always faces the camera and remains the same size on the screen at all times. However, if it is hidden behind something I want it to appear hidden.

First, you should have put that in your question; it explains far more about what you're trying to do than your question.

To achieve this, what you need to do is find the z-coordinate in the orthographic projection that matches the z-coordinate in pre-projective space of where you want the label to appear.

When you used gluProject, you got three coordinates back. The Z coordinate is important. What you need to do is reverse-transform the Z coordinate based on the zNear and zFar values you give to glOrtho.

Pedantic note: gluProject doesn't transform the Z coordinate to window space. To do so, it would have to take the glDepthRange parameters. What it really does is assume a depth range of near = 0.0 and far = 1.0.

So our first step is to transform from window space Z to normalized device coordinate (NDC) space Z. We use this simple equation:

ndcZ = (2 * winZ) - 1

Simple enough. Now, we need to go to clip space. Which is a no-op, because with an orthographic projection, the W coordinate is assumed to be 1.0. And the division by W is the difference between clip space and NDC space.

clipZ = ndcZ

But we don't need clip space Z. We need pre-orthographic projection space Z (aka: camera space Z). And that requires the zNear and zFar parameters you gave to glOrtho. To get to camera space, we do this:

cameraZ = ((clipZ + (zFar + zNear)/(zFar - zNear)) * (zFar - zNear))/-2

And you're done. Use that Z position in your rendering. Oh, and make sure your modelview matrix doesn't include any transforms in the Z direction (unless you're using the modelview matrix to apply this Z position to the label, which is fine).

Amadus answered 24/1, 2012 at 17:52 Comment(2)
Just for clarification: That this must be done has its reason that a perspective transformation maps Z values nonlinear to depth buffer values, whereas orthographic projection map Z values linear. So you first need to unproject perspective depth to Z in order to find the eye coordinate which under ortho projection will yield the same depth.Patagonia
Thanks very much, that worked like a charm. I though it was something to do with linear and non-linear depth and you explained it all really well. CheersKragh
K
1

Based on Nicol's answer you can simply set zNear to 0 (which generally makes sense for 2D elements that are acting as part of the GUI) and then you simply have:

cameraZ = -winZ*zFar
Kragh answered 26/1, 2012 at 11:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.