PDA

View Full Version : Convert value from Z buffer to Z coordinate



nam_simaa
05-23-2010, 10:12 AM
I started messing with the NeHe lesson 06, which draws a spinning cube.

I wrote some code to print the value from Z buffer in the middle of viewport:


/* This is C, NOT C++! */

int viewport[4];
GLfloat depth = 0.0f;

/* Query window size */
glGetIntegerv( GL_VIEWPORT, viewport );

/* Read 1x1 area from z buffer at centre of viewport */
glReadPixels( viewport[2] / 2, viewport[3] / 2, 1, 1,
GL_DEPTH_COMPONENT, GL_FLOAT, &depth );

printf( "%f\n", depth );


The value printed is not the "real" distance to the spinning cube, it is something between 0.96 and 0.98. The cube is about 4.5 units away.

The problem is: How to get the distance to the cube from Z buffer in world coordinates?

I mean a Z coordinate similar to the value (winZ) gluProject returns:



GLint gluProject( ..., GLdouble* winZ );


Am I clear?

Dark Photon
05-23-2010, 12:31 PM
The value printed is not the "real" distance to the spinning cube, it is something between 0.96 and 0.98.
Right. This is window-space Z.


The problem is: How to get the distance to the cube from Z buffer in world coordinates?
Presumably you mean eye coordinates, since you just care about the distance from the world to the eye.

One way is to run this X,Y,Z point through gluUnProject, or do the same thing in your code.

Another is to just look at the PROJECTION transform (orthographic or perspective -- see the end of this page (http://glprogramming.com/red/appendixf.html)), write an equation of window-space Z in terms of eye-space Z, and solve for the latter.

If you do that for PERSPECTIVE projection, assuming eye_coord.w == 1, and assuming glDepthRange (0,1), then you get:


float z_eye = gl_ProjectionMatrix[3].z/(z_window * -2.0 + 1.0 - gl_ProjectionMatrix[2].z);

For ORTHOGRAPHIC with glDepthRange(0,1), you get:


z_eye = ( z_viewport * 2.0 - 1.0 - gl_ProjectionMatrix[3].z ) / gl_ProjectionMatrix[2].z

Keep in mind these are eye-space Zs, so to get a positive Z distance, negate them.

Note that this gives you Z-distance only, not radial distance. For radial, use the UnProject route (or similar) to back-transform window X,Y,Z to a 3D eye-space point, then take the magnitude of the resultant point.

mhagain
05-23-2010, 02:44 PM
You don't even need to go through the depth buffer if all that you want is the distance to the cube. You know what position the camera is at and you know what position the cube is at, so just use a standard 3D distance calculation to get the result.

nam_simaa
05-25-2010, 07:27 AM
gluUnProject only works with orthographic projection, it involves slow matrix multiplications, and I don't need x/y coordinates.

This code doesn't work properly:


static float test()
{
GLdouble projection[16];
GLfloat depth;
float distance;

glGetDoublev( GL_PROJECTION_MATRIX, projection );
glReadPixels( 320, 240, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );

distance = projection[11] / ( depth * -2.0 + 1.0 - projection[10] ) * -1;
printf( "Distance: %f\n", distance );

return distance;
}

It returns the distance, but the smallest value was 16 and largest 49,000. My znear is 0.1 and zfar 100.0, so the return value should be in that range.

I want to get a single scalar value, which I can use to quickly convert all values in Z buffer to eye Z coordinates.

But Z buffer is not linear, and I don't know the math behind it. Help!

Dark Photon
05-25-2010, 05:08 PM
gluUnProject only works with orthographic projection
Not true. It works for perspective as well. You just have to provide the correct matrices.

* http://nehe.gamedev.net/data/articles/article.asp?article=13

Dark Photon
05-25-2010, 05:14 PM
This code doesn't work properly:


static float test()
{
GLdouble projection[16];
GLfloat depth;
float distance;

glGetDoublev( GL_PROJECTION_MATRIX, projection );
glReadPixels( 320, 240, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );

distance = projection[11] / ( depth * -2.0 + 1.0 - projection[10] ) * -1;
printf( "Distance: %f\n", distance );

return distance;
}

It returns the distance, but the smallest value was 16 and largest 49,000. My znear is 0.1 and zfar 100.0, so the return value should be in that range.
There may be other bugs in your translation of the formula I gave to the code above, but here are the ones immediately obvious to me:
projection[11] != gl_ProjectionMatrix[3].z (3*4+2=?)Remember OpenGL/GLSL is column-major, not row-major like C/C++.

You lucked out on the other one because both the row and column indices were 2 ;)