Screen position unprojection without W

712 Views Asked by At

I understand the basic concept of how to unproject:

let mut z = 0.0;
gl::ReadPixels(x as i32, y as i32, 1, 1, gl::DEPTH_COMPONENT, gl::FLOAT, &z);

// window position to screen position
let screen_position = self.to_screen_position(x, y);
// screen position to world position
let world_position = self.projection_matrix().invert() *
        Vector4::new(screen_position.x, screen_position.y, z, 1.0);

But this doesn't take the W coordinate properly - when I render things from world space to screen space, they end up with a W != 1, because of the perspective transformation (https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml). When I transform back from screen space to world space (with an assumption of W=1), the objects are in the wrong position.

As I understand it, W is a scaling factor for all the other coordinates. If this is the case, doesn't it mean screen vectors (0, 0, -1, 1) and (0, 0, -2, 2) will map to the same window coordinates, and that unprojecting doesn't necessarily produce unique results without further work?

Thanks!

1

There are 1 best solutions below

0
On

Because of the perspective transformation, you can't really ignore W.

I would suggest looking at the source code for the gluUnproject function here: http://www.opengl.org/wiki/GluProject_and_gluUnProject_code. You'll see that what this does is:

  1. Calculate projection*modelView matrix and invert it.
  2. Multiply a vector made from the screen position (in the code, winZ=0 would correspond to the near plane, winZ=1 to the far plane of your perspective projection; W will always be 1).
  3. Divide the calculated vector's X, Y and Z by W.

Note that if you do it like this, the result's W should be ignored (i.e. assumed to be 1).

You can also look here to see how the transformations in OpenGL work - if you're not familiar with this, I'd suggest reading about Clip coordinates and Normalized Device coordinates.