Mapping Perspective to Screen-Coordinates


i’m looking for an algorithm to compute a ModelMatrix.
I want to achieve the following :

  1. Render a Quad in 2D Ortho Mode.
  2. Switch to Perspective Projection Mode.
  3. Scale/Translate the Quad and render it so that it will appear at EXACTLY the same Screen-Position (and Size) as the 2D-Version.

Problem: How do i calculate the ModelMatrix to do the neccessary scaling/translation ?

I experimented with unprojecting and stuff, but to no avail.
(may be due to my lack of the mathematical background)

I’m using the glm Library for the Math, no fixed function.
View Matrix will be either the Identity-Matrix or a simple translation along Z, so there should be no rotations.

Any Help or Hint is highly appreciated !

Thanks in advance,

You should be able to do this with gluUnproject. gluUnproject takes a screen coordinate and turns it back into the “object” space or “world/camera” space, I guess, if the modelview you pass it is the identity. Do that per vertex for the 2D screen position in (x,y) with some arbitrary z value like .2.

Thanks for your answer.
Using gluUnproject (or the glm equivalent) takes 2D Window-Coordinates and unprojects them into 3D space, so using this method i have to unproject all existing vertices and construct a new Object from them, if i understand correctly.
But my goal is to find a single ModelMatrix that can be applied to the existing vertices.
I begin to wonder if that’s possible at all…

Whether there is a solution to your problem depends on the orientation of the quads you render. If the Z and W coordinates of every vertex are the same before rendering with orthographic projection, then there is a solution to your problem, otherwise there isn’t a solution.

Study this thread:

Thank you for your answer, David.
In the mentioned thread you suggest to “flatten” the Model by using the same Z-Value for all Vertices, is that correct ?
As this may work, it is not the solution i was looking for.

Maybe i should re-specify my problem:
Consider a Quad rendered in 2D Ortho mode.
Its position and size can be directly specified by screen coordinates.
Now consider a box, rendered in a perspective 3D mode.
What i am trying to do is to match the front face of the box (when rendered) with the position of the 2D Quad without flattening or distorting the box.
In other words: Find the coordinates for the front face of the box so that it appears at a certain position on the screen.

I found a tutorial which looks like what i’m after:
Given a value pZ which is the z-coordinate of the front face of the box i can calculate the dimension of the frustrum at pZ.
Given the screen-coordinates pX and pY of the upper left corner of the front face of the box, i should be able to calculate pX’ and pY’ (in 3D space at pZ) by using formulas like
pX’ = pX * frustrumWidth / viewportWidth; and
pY’ = pY * frustrumHeight / viewportHeight;

Ok, i probably have to use formulas looking more like the ones described here :
but the general approach should work, i hope ?

Any comments appreciated, i will post my results as soon as i find time to implement this.

Phew, got it !

Here’s the Code:

// Distance Camera -> Object
float dist = glm::abs(zPos - gCamPos.z);
// FOV from the Projection Matrix (Radians)
float fov = 2 * glm::atan(1 / gProjMatrix[1].y);
// Height and Width of the Frustrum at Distance
float frustHeight = dist * 2 * glm::tan(fov / 2);
float frustWidth = frustHeight * gViewport.z / gViewport.w;
// View Vector
glm::vec3 d = glm::normalize(gCamLookAt - gCamPos);
glm::vec3 right = glm::normalize(glm::cross(d, gCamUp));
// Center of the Frustrum at dist
glm::vec3 pc = gCamPos + d * dist;
// TopLeft Edge of Frustrum
glm::vec3 ptl = pc + (gCamUp * frustHeight/2.0f) - (right * frustWidth/2.0f);
// How "big" is a Pixel at that distance ?
float wScale = glm::abs(frustWidth / gViewport.z);
float hScale = glm::abs(frustHeight / gViewport.w);
// Scale and translate into position. Note that we ignore Z here, because the Objects are always specified with Z=0
modelMatrix = glm::translate(modelMatrix, glm::vec3(ptl.x + (left + width / 2)*wScale, ptl.y - (top + height/2)*hScale, 0.0f));
modelMatrix = glm::scale(modelMatrix, glm::vec3(wScale, -hScale, hScale));
modelMatrix = glm::translate(modelMatrix, glm::vec3(-(left + width / 2), -(top + height / 2), 0.0f));

Thanks again.

Here’s also a Screenshot that shows that my 3D object matches the position of the red reference Quad perfectly:
(Slightly rotated to have a better view of the reference Quad)