OpenGL Math - Projecting Screen space to World space coords
Asked Answered
L

4

24

Time for a little bit of math for the end of the day..

I need to project 4 points of the window size:

<0,0> <1024,768>

Into a world space coordinates so it will form a quadrilateral shape that will later be used for terrain culling - without GluUnproject

For test only, I use mouse coordinates - and try to project them onto the world coords

Laurilaurianne answered 7/10, 2011 at 21:22 Comment(5)
isn't the window size irrelevant? the shape/aspect sure, but what you are after is the specific rectangular cone shape the camera view forms, right?Gono
i found something cool collagefactory.blogspot.com/2010/03/…Calisaya
Breton, the thing i am looking for is to construct a 'bounding box' from actual camera view that i will use later on to find which terrain cells are 'inside' so i can render them. So i need to know where is the top left, top right, bottom left and bottom right located in 3d world space so i can later on find the terrain cells that are within this range.Laurilaurianne
Why do you use camera position in model matrix? This should be model position! If your model is at zero, then you dont need any matrix. For the inverse, it's easy to test. Multiply the result by the source, and it works if you obtain an identity matrixLandbert
And you won't get a bounding box from camera corner point, but a frustumLandbert
L
47

RESOLVED

Here's how to do it exactly, step by step.

  1. Obtain your mouse coordinates within the client area
  2. Get your Projection matrix and View matrix if no Model matrix required.
  3. Multiply Projection * View
  4. Inverse the results of multiplication
  5. Construct a vector4 consisting of

    x = mouseposition.x within a range of window x

    • transform to values between -1 and 1

    y = mouseposition.y within a range of window y

    • transform to values between -1 and 1
    • remember to invert mouseposition.y if needed

    z = the depth value ( this can be obtained with glReadPixel)

    • you can manually go from -1 to 1 ( zNear, zFar )

    w = 1.0

  6. Multiply the vector by inversed matrix created before

  7. Divide result vector by it's w component after matrix multiplication ( perspective division )

        POINT mousePos;
        GetCursorPos(&mousePos);
        ScreenToClient( this->GetWindowHWND(), &mousePos );         
    
        CMatrix4x4 matProjection = m_pCamera->getViewMatrix() *  m_pCamera->getProjectionMatrix() ;
    
        CMatrix4x4 matInverse =  matProjection.inverse();
    
    
        float in[4];
        float winZ = 1.0;
    
    
        in[0]=(2.0f*((float)(mousePos.x-0)/(this->GetResolution().x-0)))-1.0f,
        in[1]=1.0f-(2.0f*((float)(mousePos.y-0)/(this->GetResolution().y-0)));
        in[2]=2.0* winZ -1.0;
        in[3]=1.0;          
    
        CVector4 vIn = CVector4(in[0],in[1],in[2],in[3]);
        pos = vIn * matInverse;
    
        pos.w = 1.0 / pos.w;
    
        pos.x *= pos.w;
        pos.y *= pos.w;
        pos.z *= pos.w;
    
        sprintf(strTitle,"%f %f %f / %f,%f,%f ",m_pCamera->m_vPosition.x,m_pCamera->m_vPosition.y,m_pCamera->m_vPosition.z,pos.x,pos.y,pos.z);
    
        SetWindowText(this->GetWindowHWND(),strTitle);
    
Laurilaurianne answered 7/10, 2011 at 21:22 Comment(3)
You can just do pos.x /= pos.wUmeh
why you calculate the projection by viewMatrix * projectMatrix instead of projectMatrix * viewMatrix?Delapaz
Do not divide by w.Scolecite
A
10

I had to make some adjustments to the answers provided here. But here's the code I ended up with (Note I'm using GLM, that could affect multiplication order). nearResult is the projected point on the near plane and farResult is the projected point on the far plane. I want to perform a ray cast to see what my mouse is hovering over so I convert them to a direction vector which will then originate from my camera's position.

vec3 getRayFromScreenSpace(const vec2 & pos)
{
    mat4 invMat= inverse(m_glData.getPerspective()*m_glData.getView());
    vec4 near = vec4((pos.x - Constants::m_halfScreenWidth) / Constants::m_halfScreenWidth, -1*(pos.y - Constants::m_halfScreenHeight) / Constants::m_halfScreenHeight, -1, 1.0);
    vec4 far = vec4((pos.x - Constants::m_halfScreenWidth) / Constants::m_halfScreenWidth, -1*(pos.y - Constants::m_halfScreenHeight) / Constants::m_halfScreenHeight, 1, 1.0);
    vec4 nearResult = invMat*near;
    vec4 farResult = invMat*far;
    nearResult /= nearResult.w;
    farResult /= farResult.w;
    vec3 dir = vec3(farResult - nearResult );
    return normalize(dir);
}
Amaris answered 7/10, 2011 at 21:22 Comment(1)
This answer is long, but I was able to understand the math instead of just copy and paste, thanksSyck
L
8

Multiply all your matrices. Then invert the result. Point after projection are always in the -1,1. So the four corner screen points are -1,-1; -1,1; 1,-1;1,1. But you still need to choose th z value. If you are in OpenGL, z is between -1 and 1. For directx, the range is 0 to 1. Finally take your points and transform them with the matrix

Landbert answered 8/10, 2011 at 10:45 Comment(5)
This is what i do actually, I do a MVP matrix multiplying projection*(View * model) Then i inverse that, have a look above ( updated first post ) - maybe there's something i'm missing within the Z value..?Laurilaurianne
@Laurilaurianne At the moment your z is -3, I'm sure this isn't intended, as z has to be in [-1,1] (maybe you just want -1?). The rest seems reasonable, but don't forget to invert source.y if your window coordinate system has y going from top to bottom. And I hope VectorMatrixMultiply implicitly assumes a 1 for the w component, and not a 0.Lemoine
Christian, thanks i am now patching what you have pointed out, and also i have updated the main post with CMatrix4x4 operator* method, so you can have a look if it's ok - as this class is not mine, and i don't know if i can trust it in this situation.Laurilaurianne
Your * miss the translation part.Landbert
crazyjul, fixed by implementing new mathlib.Laurilaurianne
L
4

If you have access to the glu libraries, use gluUnProject(winX, winY, winZ, model, projection, viewport, &objX, &objY, &objZ);

winX and winY will be the corners of your screen in pixels. winZ is a number in [0,1] which will specify where between zNear and zFar (clipping planes) the points should fall. objX-Z will hold the results. The middle variables are the relevant matrices. They can be queried if needed.

Lantana answered 8/10, 2011 at 0:59 Comment(2)
Yeah sorry for that, i haven't mentioned i don't want to use gluunproject - i need to operate on matrices only.Laurilaurianne
@Laurilaurianne But the linked documentation explains the implementation of gluUnProject, and as it's extremely easy, you can just write it yourself. See crazyjul's answer.Lemoine

© 2022 - 2024 — McMap. All rights reserved.