Geez, Nelson, you started me obsessing over this, and then I couldn't focus until I'd worked it out. :-)

Here we go. Use a fixed-width font.

First, let's look at the projection matrix. Using something like gluPerspective, we have a matrix as follows:
  With these values (for brevity):
    f = cotangent( fovy/2 )
    A = ( zNear+zFar )/( zNear-zFar )
    B = ( 2*zNear*zFar )/( zNear-zFar )
  The generated matrix is
    f/aspect  0   0   0
       0      f   0   0
       0      0   A   B
       0      0  -1   0
(Please note that's column major, even though I'll use row-major vectors below.)

OK, given an eye coordinate at the center of the view with a known z value:
  ec = ( 0, 0, z, 1 )

The transformed clip coordinate is (vector-matrix multiply):
  cc = ( 0, 0, z*A+B, -z )
and the normalized device coordinate (divide by cc w) is:
  ndc = ( 0, 0, (z*A+B) / -z )

Next we need the viewport. To make things easy, let's assume a default depth range of 0.0 to 1.0, and let's also assume 0, 0 for the viewport xy values. That way we only have to deal with the with and height, which is the typical case anyway. The window coordinate is:
  wc = ( 0.5*w, 0.5*h, ( 0.5*(z*A+B) / -z ) )

Looks ugly, but remember, those are all given values, so you can just take your known z distance, your viewport, and your projection matrix and compute that window coordinate with a pencil and a napkin.

Now we know the window coordinate distance subtended by one pixel. That distance is 1.0 by definition. It's the same for x and y, so let's ignore y and just add 1 to x to get this new delta window coordinate:
  wcD = ( 0.5*w+1, 0.5*h, ( 0.5*(z*A+B) / -z ) )

If we back-transform this into eye space, the resulting eye space x value is the distance subtended by one pixel. That's what we're after! We can do this... (I think...)

The delta normalized device coordinate is:
  ndcD = ( ((0.5*w+1) / w * 2) - 1, 0, (z*A+B) / -z )
(Tricky: The multiply by 2 and subtract 1 is needed to put us back into the range -1 to 1.)

The delta clip coordinate is:
  ccD = ( (((0.5*w+1) / w * 2) - 1) * -z, 0, z*A+B, -z )
(Multiply by -z, the inverse of dividing by -z.)

And finally the delta eye coordinate is:
  ecD = ( (((0.5*w+1) / w * 2) - 1) * -z * (aspect/f), 0, z, 1 )
(Multiply by aspect/f, which is the inverse of multiplying by f/aspect, which is what we would've had to do at the top of this email to compute the cc x value.)

In summary, the ec x distance subtended by one pixel in the center of your window at a given z distance is:
    (((0.5*w+1) / w * 2) - 1) * -z * (aspect/f)

To solve, you just need these four values:
  * The viewport width
  * The given z distance
  * The aspect ration in your projection matrix
* And the f value, which is cotangent( fovy/2 ), and fovy also comes from the projection matrix.

There might be some math errors there, so please check over my work. Then run some test values through it to verify.

One thing to keep in mind is that eye space z distance is not the same as world coordinate range. I've created an equation to solve for x distance at the center of the window, and accuracy will degrade if you use this same solution for pixels near the edge of the window.

But hopefully you get the idea and you can adapt this to your project.
   -Paul

_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to