PDA

View Full Version : Depth Buffer - How do I get the pixel's Z coord



rangers99
02-27-2002, 04:47 AM
I understand that when you use glReadPixels to read the depth buffer, the values are in the range 0.0 to 1.0. However is there a way I can obtain the actual Z-coord (an integer) for each pixel?

marcus256
02-27-2002, 05:14 AM
You need to know the Z-range, usually specified as ZNear and ZFar (e.g. in gluPerspective). You should be able to convert the z-buffer value to a distance value by:

distance = ZNear + (ZFar-ZNear) * z

where z is in the range 0..1.

The znear/zfar information is lost in the z-buffer (you know, you can have several different znear/zfar projections rendered into the same z-buffer), so that is why you have to do it manually as described above.

I'm not sure, there may be some glu function for doing it for you, but I think this way is faster.

Bob
02-27-2002, 05:22 AM
Marcus, the formula is a bit more complicated that that. Since the Z-buffer does not have a linear distribution of values, but logarithmic, you can't "reverse-interpolate" (bah, can't find a better word) the Z value. Here's a piece of code I came up with some time ago.



// -------------------------------
// n: distance to near clip plane
// f: distance to far clip plane
// z: value in depth buffer
// -------------------------------
double convertZ(double n, double f, double z)
{
// Convert Z from [0, 1] to [-1, 1]
double wz = (2.0 * z) - 1.0;

// Inverse projection matrix on the Z coordinate (assuming W=1)
double a = -(f - n) / (2.0 * f * n);
double b = (f + n) / (2.0 * f * n);
return -1.0 / (wz * a + b);
}

fritzlang
02-27-2002, 05:26 AM
This thing has been discussed numerous times here, please use the search function and check the archives. Doing this will keep the forum more clean instead of continusly repeating itself.

To your question:
//read out depth component
GLfloat depth_comp;
glReadPixels( win_xpos, win_ypos, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth_comp);

GLfloat clip_z = (depth_comp - 0.5f) * 2.0f;
GLfloat world_z = 2*far_z*near_z/(clip_z*(far_z-near_z)-(far_z+near_z));

--
world_z is your value, & you can use that with gluUnProject to get whatever you need.

cheers.

rangers99
02-27-2002, 05:44 AM
many thanks.

franz1999
04-30-2005, 08:58 PM
Originally posted by fritzlang:
//read out depth component
GLfloat depth_comp;
glReadPixels( win_xpos, win_ypos, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth_comp);

GLfloat clip_z = (depth_comp - 0.5f) * 2.0f;
GLfloat world_z = 2*far_z*near_z/(clip_z*(far_z-near_z)-(far_z+near_z));
Sorry for digging out this post from the dusty archives, but I am using this formula (it works fine) and I am writing a report. Can anybody help me finding a reference that explains how this formula works? Is there anthing on the red book of openGL that explains this? I can't find a direct correlation between the formula:

z = ((f-n)/2)zd + (n+f)/2

and this one.
Can anyone help me?
thanks,

f.

dorbie
04-30-2005, 11:16 PM
Kenneth E. Hoff III wrote a great explanation titled "Deriving the OpenGL Perspective Depth Transformation"

I have a paper copy but I cannot find a link online anymore. His UNC page is down.

http://www.cs.unc.edu/~hoff/

The wayback archive has it here:

http://web.archive.org/web/20030610074535/cs.unc.edu/~hoff/techrep/perspective.doc

franz1999
05-01-2005, 05:56 AM
Thank you Dorbie for this precious piece of information. Now if I only could make some sense out of it... I'll read it through seeing if I can find the formula that I am using...

Appreciate your help

F.

dorbie
05-02-2005, 11:45 AM
NDCz is normalized device coordinate z and is derived from eye z (using near & far).

So the solution is the opposite of the equation you need. Look at the man page for glFrustum and you'll see the various terms in the transformation. You can expand this out (multiplying a vector through there) and derive the same thing, that is in fact what Hoff does in his tech report.

So this gives you NDCz from EYEz. You then need to solve the same equation for EYEz because you're going the opposite way. i.e. you have NDCz and you want EYEz. Hoff has done this for you too (see new link), although it's pretty terse, that's the trouble with math gurus they can go from A to Z (or even Z to Z) without filling in the gaps for the rest of us :-)

http://web.archive.org/web/20030613211818/cs.unc.edu/~hoff/techrep/openglz.html

franz1999
05-03-2005, 11:45 AM
Interesting... he says that

Z = Zn*Zf / (Zf - z*(Zf-Zn))

where:
z = normalized OpenGL depth-buffer value
as returned by glReadPixels in [0,1]
Z = actual Z value as distance from the plane
through the center-of-projection (Z=0)
Zn = distance from the eye-plane to the near plane (Z=Zn)
Zf = distance from the eye-plane to the far plane (Z=Zf)

my code is (stripped here and there):

glReadPixels( 0, 0, WIN_WIDTH, WIN_HEIGHT, GL_DEPTH_COMPONENT, GL_FLOAT, bitmap);
GLfloat depth_comp = bitmap->getPixel(x,y);
GLfloat clip_z = (depth_comp - 0.5f) * 2.0f;
GLfloat world_z = ((far-near)/2)*clip_z + (far+near)/2;

which is the implementation of the formula:
z = ((f-n)/2)zd + (n+f)/2
that I got from some other post on this forum

this code seem to work (I am using it to build a distance field using the GPU). But here there is no inverse relationship between the z-buffer value and the world-z value...

Do you care to explore how is this possible? I don't see the relationship... maybe it is obvious but I don't see it.

Thanks.

Francesco