Search code examples
c++openglglm-mathprojection-matrixfrustum

How to retrieve the camera origin/position from the view projection matrix? (OpenGL)


Is there any way to retrieve the camera origin/position from the view projection matrix? (OpenGL)

I'm trying to draw the camera's frustum and I have no problems calculating corners for the Far and the Near planes.

But I can't figure out how to retrieve a point that would represent the camera origin.

I was expecting to retrieve this by multiplying the Homogeneous clip-space coordinates:

glm::vec4(0.f, 0.f, 0.f, 1.f)

But I'm getting somewhat that looks to be like ~inverted camera position. (Marked as X in the screenshot) Included all the source code below.

enter image description here

    // Homogeneous points for source cube in clip-space.
    std::array<glm::vec4, 9> corners =
    {
        // Far plane
        glm::vec4(-1.f,-1.f, 1.f, 1.f), // bottom-left
        glm::vec4( 1.f,-1.f, 1.f, 1.f), // bottom-right
        glm::vec4( 1.f, 1.f, 1.f, 1.f), // top-right
        glm::vec4(-1.f, 1.f, 1.f, 1.f), // top-left

        // Near plane
        glm::vec4(-1.f,-1.f,-1.f, 1.f), // bottom-left
        glm::vec4( 1.f,-1.f,-1.f, 1.f), // bottom-right
        glm::vec4( 1.f, 1.f,-1.f, 1.f), // top-right
        glm::vec4(-1.f, 1.f,-1.f, 1.f), // top-left

        // Camera/screen center position.
    //  glm::vec4(0.f, 0.f, 0.f, 1.f) // EDIT
        glm::vec4(0.f, 0.f,-1.f, 0.f) // SOLVED
    };

    const auto invMatrix(glm::inverse(viewProjectionMatrix));

    for (U32 i = 0; i < corners.size(); i++)
    {
        corners[i] = invMatrix * corners[i]; // 4x4 * 4x1 matrix/vector multiplication.
        corners[i] /= corners[i].w; // Homogeneous to euclidean/cartesian conversion
    }

    // Far plane.
    this->AddLine(corners[0], corners[1], rColor);
    this->AddLine(corners[1], corners[2], rColor);
    this->AddLine(corners[2], corners[3], rColor);
    this->AddLine(corners[3], corners[0], rColor);

    // Near plane.
    this->AddLine(corners[4], corners[5], rColor);
    this->AddLine(corners[5], corners[6], rColor);
    this->AddLine(corners[6], corners[7], rColor);
    this->AddLine(corners[7], corners[4], rColor);

    // Connection from Near rectangle to the Far rectangle.
    this->AddLine(corners[0], corners[4], rColor);
    this->AddLine(corners[1], corners[5], rColor);
    this->AddLine(corners[2], corners[6], rColor);
    this->AddLine(corners[3], corners[7], rColor);

    // X
    this->AddLine(corners[4], corners[8], rColor);
    this->AddLine(corners[5], corners[8], rColor);
    this->AddLine(corners[6], corners[8], rColor);
    this->AddLine(corners[7], corners[8], rColor);

[Solved]

The problem was that I needed to use glm::vec4(0.f, 0.f,-1.f, 0.f) instead of glm::vec4(0.f, 0.f, 0.f, 1.f).

Here are the desired results: enter image description here


Solution

  • In the general case, if you only have a composed viewProjection matrix, you cannot deduce the camera origin from that. You will need additional information.

    If you know that your projection is orthogonal (or any kind of parallel projection actually), there is no meaningful concept of a camera position anyway, and the origin is just some (more or less) arbitrary point you chose. To reconstruct it, you will need the parameters for the projection to decompose viewProjection into view and projection. There might be some shortcuts compared to doing a full construction of the proj but this will depend on the properties of the actual projection parameters which you might know in advance / can assume.

    In the case of a perspective projection, the situation is actually simpler: The center of projection will be projected to some point in infinity, but we exactly know its location in homogeneous clip space. It is just proj * vec4(0,0,0,1) for a typical projection matrix, which is just the last column of proj. Assuming default GL conventions, this will be something like (0, 0, alpha, 0). The absolute value of alpha doesn't matter since this is only a direction anyway, just the sign will matter. Again, assuming default GL conventions, alpha will actually be negative, so the projection center will be transformed to (0,0,-1,0) in clip space. Therefore, you can simply inverte the calculations and go from clip space back to world space: p_center = inverse(viewProj) * vec4(0,0,-1,0). Don't forget that the result is in homogeneous coordinates still, so you will need to divide by the resulting w to get a meaningful 3D coordinate.