Clip space coords to window coords, and window coords to clip space coords. Most references point to the GluProject and Gluunproject code points.
note: if someone wants to confirm the positioning of coords for me in regards to the center of a pixel and GL window range in relation to pixels that would be great.
this post will cover-
Clip Space to Window Coords and
Window Coords to Clip Space
references to this form nehe lesson 14, gluUnProject, and gluProject.
CLIP SPACE TO WINDOW COORDS
it’s important to note that clip space x,y,z,w coords are defined as the values of the in_Vertex after they’e gone through your ModelView and Projection Matrices, or Quaternions, or Euler Angle transformations, whatever method you’re using. Not your object space coords. OpenGL coords start at 0,0 for the bottom left corner, or more specifically, -0.5,-0.5, since a pixel is denoted as a 1.0x1.0 pixel box, with 0.5,0.5 being its center.
The formulas for Clip Space to Window Coords are as follows:
gl_FragCoord.x =
(viewport.width * (normalized(gl_Position.x) + 1 ) / 2) + viewport.xposition;
gl_FragCoord.y =
(viewport.height * (normalized(gl_Position.y) + 1 ) / 2) + viewport.yposition;
gl_FragCoord.z = (normalized(gl_Position.z) + 1) / 2;
A quick note on what it means to be ‘normalized’ normalized is simply taking the Clip coordinates (gl_Position) and multiplying it by one over the w value of the Clip coordinate.
so the normalized value of;
normalized(gl_Position.x) =
gl_Position.x * (1 / gl_Position.w); or
gl_Position.x / gl_Position.w;
normalized(gl_Position.y) =
gl_Position.y * (1 / gl_Position.w); or
gl_Position.y / gl_Position.w;
normalized(gl_Position.z) =
gl_Position.z * (1 / gl_Position.w); or
gl_Position.z / gl_Position.w;
As a breakdown of what’s happening:
normalizing more or less reduces the dependency on a set pixel resolution. a ‘normalized’ value of .x more or less says “This vertex is Some-Perecent (0% to 100%, or 0.0 to 1.0)of the way across the screen” instead of saying ‘832 pixels from the left side’ because different resolutions could have different total pixels across.
However this value denotes as if the x and y axis was at the lower left corner. if we drew anything below the x or y axis it would be off screen! We need to move our point to all positive coords to match the window. we do this by adding + 1 to our normalized value. This means values from -1.0 to 0 are now 0.0 to 1.0, and 0.0 to 1.0 values are now 1.0 to 2.0 values. all positive. Unfortunately this means we’re calculating for two screens worth of space (200% of the width!) so to bring it back down to one screen (100%) we divide by two. This gives negative axis screen coords the range 0.0-0.5 and positive axis screen coords 0.5 to 1.0 range.
Once we know what percent from the left side we are, we just have to times that by what we know as the viewport width, this officially takes us to window coordinates. Take your normalized value and times it by the window width.
We’re almost done, if the viewport does NOT start at the leftmost or bottom most side of our screen, we simply have to add where it starts to our end value, like so:
gl_FragCoord.x = windowCoord.x + viewport.x;
gl_FragCoord.y = windowCoord.y + viewport.y;
Lastly, OpenGL modifies the clipspace z coord (gl_Position.z) to provide better depth accuracy at closer range.
the formula is simply:
gl_FragCoord.z = (gl_Position.z + 1) / 2;
WINDOW COORDS TO CLIP SPACE
It can be very very handy to be able to calculate the Clip Space from Window coords. Once you get clip space you can unproject and transform it to get model space coordinates. It’s very handy for lighting calculations, or figuring out where you clicked the mouse, or anything what would requires you to know the X,Y,Z position of a pixel on the screen.
if you know how to get your desired window coordinate and z depth from your application, skip over this next part, I’ll briefly go over how to get it in windows.
Getting GL Window Coords from Windows with the mouse
First thing, we want to get the coordinates under our mouse cursor. Under windows we’ll need a POINT variable to do this.
static POINT mouse;
We’ll need some spots to store our points too.
GLFloat pointX, pointY, pointZ;
next we need to get the Cursor Position, this is done with function call
GetCursorPos(&mouse);
There’s only one problem here, Windows mouse positions start in the upper left, OpenGL coords start in the lower left. To fix our Input we can do one of two things:
ScreenToClient(HWND, &Mouse); (where HWND is your window handle)
or if you know the window height, just take the height minus your mouse.y coord. (so yValue = height - yValue; once you get it out of POINT mouse.y)
lets take the values out of the POINT structure.
pointX = (float)mouse.x;
pointY = (float)mouse.y;
grats, you now have your X and Y values, we still need the depth value however. This is stored in the depth buffer of our OpenGL buffer.
to get it we call:
glReadPixels(pointX, pointY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &pointZ);
Window Coords to Clip Space
Now that we know the coordinates that we want to change to clip space, and have our pointX, pointY, and pointZ values we can start reversing the clipspace to window coordinate values.
We also need to know some information about the viewport, we can get what we need with:
static GLint view[4];
glGetIntegerv(GL_VIEWPORT, view);
this loads window offsetX into view[0];
window offsetY into view[1]; (remember, offset from the bottom, not top)
window width into view[2];
window height into view[3];
We need to modify our input first since the center of a pixel is not pointX, pointY, but rather pointX - 0.5 and pointY - 0.5. so:
pointX -= 0.5;
pointY -= 0.5;
Our first goal is to get back to the normalized values from gl_FragCoord. I adjusted for viewport offset in here as well.
normalized(gl_Position.x) = ((gl_FragCoord.x - view[0]) / view[2] * 2) - 1;
normalized(gl_Position.y) = ((gl_FragCoord.y - view[1]) / view[3] * 2) - 1;
normalized(gl_Position.z) = (gl_FragCoord.z * 2) - 1;
2(gl_FragCoord.x - view.x)*view.width - 1
To be continued…