Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 3 of 3

Thread: Extracting camera position from a ModelView Matrix

Hybrid View

  1. #1
    Senior Member OpenGL Pro sqrt[-1]'s Avatar
    Join Date
    Jun 2002
    Location
    Australia
    Posts
    1,000

    Extracting camera position from a ModelView Matrix

    Here are some notes on extracting a camera position from a model-view matrix that I have been playing with.
    All code here uses the GLM math library.

    1) Simple extraction
    If you can assume there is no scaling in the matrix, you can simply:
    Code :
    vec3 ExtractCameraPos_NoScale(const mat4 & a_modelView)
    {
      mat3 rotMat(a_modelView);
      vec3 d(a_modelView[3]);
     
      vec3 retVec = -d * rotMat;
      return retVec;
    }

    2) Inverse Matrix Extraction
    If you don't know if there is scaling in the matrix, you can invert the matrix then pull out the final column.
    Code :
      mat4 viewModel = inverse(modelView);
      vec3 cameraPos(viewModel[3]); // Might have to divide by w if you can't assume w == 1


    3) Plane intersection extraction
    If you have to deal with scaling, but can assume the last row is (0,0,0,1) (every model-view matrix I have seen)

    Code :
    vec3 ExtractCameraPos(const mat4 & a_modelView)
    {
      // Get the 3 basis vector planes at the camera origin and transform them into model space.
      //  
      // NOTE: Planes have to be transformed by the inverse transpose of a matrix
      //       Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix
      //
      //       So for a transform to model space we need to do:
      //            inverse(transpose(inverse(MV)))
      //       This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html
      //
      // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix.
      //  
      mat4 modelViewT = transpose(a_modelView);
     
      // Get plane normals 
      vec3 n1(modelViewT[0]);
      vec3 n2(modelViewT[1]);
      vec3 n3(modelViewT[2]);
     
      // Get plane distances
      float d1(modelViewT[0].w);
      float d2(modelViewT[1].w);
      float d3(modelViewT[2].w);
     
      // Get the intersection of these 3 planes
      // http://paulbourke.net/geometry/3planes/
      vec3 n2n3 = cross(n2, n3);
      vec3 n3n1 = cross(n3, n1);
      vec3 n1n2 = cross(n1, n2);
     
      vec3 top = (n2n3 * d1) + (n3n1 * d2) + (n1n2 * d3);
      float denom = dot(n1, n2n3);
     
      return top / -denom;
    }

    This code probably is the same as hacking an inverse method to not extract all values and assume the last row is 0,0,0,1.

    Here is the complete code used in testing

    Code :
    #include "../glm/glm.hpp"
    #include "../glm/gtc/matrix_transform.hpp"
     
    using namespace glm;
     
    vec3 ExtractCameraPos(const mat4 & a_modelView)
    {
      // Get the 3 basis vector planes at the camera origin and transform them into model space.
      //  
      // NOTE: Planes have to be transformed by the inverse transpose of a matrix
      //       Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix
      //
      //       So for a transform to model space we need to do:
      //            inverse(transpose(inverse(MV)))
      //       This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html
      //
      // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix.
      //  
      mat4 modelViewT = transpose(a_modelView);
     
      // Get plane normals 
      vec3 n1(modelViewT[0]);
      vec3 n2(modelViewT[1]);
      vec3 n3(modelViewT[2]);
     
      // Get plane distances
      float d1(modelViewT[0].w);
      float d2(modelViewT[1].w);
      float d3(modelViewT[2].w);
     
      // Get the intersection of these 3 planes
      // http://paulbourke.net/geometry/3planes/
      vec3 n2n3 = cross(n2, n3);
      vec3 n3n1 = cross(n3, n1);
      vec3 n1n2 = cross(n1, n2);
     
      vec3 top = (n2n3 * d1) + (n3n1 * d2) + (n1n2 * d3);
      float denom = dot(n1, n2n3);
     
      return top / -denom;
    }
     
    vec3 ExtractCameraPos_NoScale(const mat4 & a_modelView)
    {
      mat3 rotMat(a_modelView);
      vec3 d(a_modelView[3]);
     
      vec3 retVec = -d * rotMat;
      return retVec;
    }
     
    int main(int argc, char * argv[])
    {
      vec3 cameraPos(1.234f, 2.657f, 7.865f);
     
      // Create a model view matrix with translations and scales
      mat4 modelView(1.0f);
      modelView = rotate(modelView, 0.5f, vec3(1.0f, 0.0f, 0.0f));
      modelView = rotate(modelView, 0.2f, vec3(0.0f, 1.0f, 0.0f));
      modelView = rotate(modelView, 0.3f, vec3(0.0f, 0.0f, 1.0f));
      modelView = scale(modelView, vec3(2.0f, 3.0f, 4.0f));
     
      modelView = translate(modelView, -cameraPos);
     
      vec3 cameraPos2 = ExtractCameraPos(modelView);
     
      mat4 viewModel = inverse(modelView);
      vec3 cameraPos3(viewModel[3]);
     
      // Do not add scale if using this
      //vec3 cameraPos4 = ExtractCameraPos_NoScale(modelView);
     
        return 0;
    }

  2. #2
    Super Moderator Frequent Contributor Groovounet's Avatar
    Join Date
    Jul 2004
    Posts
    935
    Very nice! I should probably consider adding this to GLM. I create a ticket for that https://github.com/Groovounet/glm/issues/11.

    Thanks!

  3. #3
    Senior Member OpenGL Pro sqrt[-1]'s Avatar
    Join Date
    Jun 2002
    Location
    Australia
    Posts
    1,000
    I was doing some reading of "RealTime Collision Detection" and I think I found a small optimization

    4) Plane intersection extraction - optimization
    Code :
    vec3 ExtractCameraPos2(const mat4 & a_modelView)
    {
      // Get the 3 basis vector planes at the camera origin and transform them into model space.
      //  
      // NOTE: Planes have to be transformed by the inverse transpose of a matrix
      //       Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix
      //
      //       So for a transform to model space we need to do:
      //            inverse(transpose(inverse(MV)))
      //       This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html
      //
      // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix.
      //  
      mat4 modelViewT = transpose(a_modelView);
     
      // Get plane normals 
      vec3 n1(modelViewT[0]);
      vec3 n2(modelViewT[1]);
      vec3 n3(modelViewT[2]);
     
      // Get plane distances
      float d1(modelViewT[0].w);
      float d2(modelViewT[1].w);
      float d3(modelViewT[2].w);
     
      // Get the intersection of these 3 planes 
      // (uisng math from RealTime Collision Detection by Christer Ericson)
      vec3 n2n3 = cross(n2, n3);
      float denom = dot(n1, n2n3);
     
      vec3 top = (n2n3 * d1) + cross(n1, (d3*n2) - (d2*n3));
      return top / -denom;
    }


    5) Plane intersection extraction - alternate
    There are two ways of doing 3 plane intersection tests in RealTime Collision Detection - here is the other way - but it seems that this produces more inaccurate results? (unless I have a bug in the code - or it is a problem with the test values I am using)

    Code :
    vec3 ExtractCameraPos3(const mat4 & a_modelView)
    {
      // Get the 3 basis vector planes at the camera origin and transform them into model space.
      //  
      // NOTE: Planes have to be transformed by the inverse transpose of a matrix
      //       Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix
      //
      //       So for a transform to model space we need to do:
      //            inverse(transpose(inverse(MV)))
      //       This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html
      //
      // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix.
      //  
      mat4 modelViewT = transpose(a_modelView);
     
      // Get plane normals 
      vec3 n1(modelViewT[0]);
      vec3 n2(modelViewT[1]);
      vec3 n3(modelViewT[2]);
     
      // Get plane distances
      float d1(modelViewT[0].w);
      float d2(modelViewT[1].w);
      float d3(modelViewT[2].w);
     
      // Get the intersection of these 3 planes 
      // (uisng math from RealTime Collision Detection by Christer Ericson)
      vec3 n2n3 = cross(n2, n3);
      float denom = dot(n1, n2n3);
     
      vec3 d(d1,d2,d3);
      vec3 v = cross(n1, d);
     
      vec3 top;
      top.x = dot(d, n2n3);
      top.y = dot(n3, v);
      top.z = -dot(n2, v);
     
      return top / -denom;
    }

    Results are (VS2008 - precise float point model used in project settings):

    1.234000 2.657000 7.865000 - Base Camera
    1.234000 2.657000 7.865000 - Plane intersection extraction
    1.234000 2.657000 7.865001 - Plane intersection extraction - optimize
    1.282042 2.507463 7.897709 - Plane intersection extraction alternate
    1.234000 2.657000 7.865001 - Inverse Matrix

    Updated complete code listing
    Code :
    #include "../glm/glm.hpp"
    #include "../glm/gtc/matrix_transform.hpp"
     
    using namespace glm;
     
    vec3 ExtractCameraPos(const mat4 & a_modelView)
    {
      // Get the 3 basis vector planes at the camera origin and transform them into model space.
      //  
      // NOTE: Planes have to be transformed by the inverse transpose of a matrix
      //       Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix
      //
      //       So for a transform to model space we need to do:
      //            inverse(transpose(inverse(MV)))
      //       This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html
      //
      // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix.
      //  
      mat4 modelViewT = transpose(a_modelView);
     
      // Get plane normals 
      vec3 n1(modelViewT[0]);
      vec3 n2(modelViewT[1]);
      vec3 n3(modelViewT[2]);
     
      // Get plane distances
      float d1(modelViewT[0].w);
      float d2(modelViewT[1].w);
      float d3(modelViewT[2].w);
     
      // Get the intersection of these 3 planes
      // http://paulbourke.net/geometry/3planes/
      vec3 n2n3 = cross(n2, n3);
      vec3 n3n1 = cross(n3, n1);
      vec3 n1n2 = cross(n1, n2);
     
      vec3 top = (n2n3 * d1) + (n3n1 * d2) + (n1n2 * d3);
      float denom = dot(n1, n2n3);
     
      return top / -denom;
    }
     
    vec3 ExtractCameraPos2(const mat4 & a_modelView)
    {
      // Get the 3 basis vector planes at the camera origin and transform them into model space.
      //  
      // NOTE: Planes have to be transformed by the inverse transpose of a matrix
      //       Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix
      //
      //       So for a transform to model space we need to do:
      //            inverse(transpose(inverse(MV)))
      //       This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html
      //
      // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix.
      //  
      mat4 modelViewT = transpose(a_modelView);
     
      // Get plane normals 
      vec3 n1(modelViewT[0]);
      vec3 n2(modelViewT[1]);
      vec3 n3(modelViewT[2]);
     
      // Get plane distances
      float d1(modelViewT[0].w);
      float d2(modelViewT[1].w);
      float d3(modelViewT[2].w);
     
      // Get the intersection of these 3 planes 
      // (uisng math from RealTime Collision Detection by Christer Ericson)
      vec3 n2n3 = cross(n2, n3);
      float denom = dot(n1, n2n3);
     
      vec3 top = (n2n3 * d1) + cross(n1, (d3*n2) - (d2*n3));
      return top / -denom;
    }
     
     
    vec3 ExtractCameraPos3(const mat4 & a_modelView)
    {
      // Get the 3 basis vector planes at the camera origin and transform them into model space.
      //  
      // NOTE: Planes have to be transformed by the inverse transpose of a matrix
      //       Nice reference here: http://www.opengl.org/discussion_boards/showthread.php/159564-Clever-way-to-transform-plane-by-matrix
      //
      //       So for a transform to model space we need to do:
      //            inverse(transpose(inverse(MV)))
      //       This equals : transpose(MV) - see Lemma 5 in http://mathrefresher.blogspot.com.au/2007/06/transpose-of-matrix.html
      //
      // As each plane is simply (1,0,0,0), (0,1,0,0), (0,0,1,0) we can pull the data directly from the transpose matrix.
      //  
      mat4 modelViewT = transpose(a_modelView);
     
      // Get plane normals 
      vec3 n1(modelViewT[0]);
      vec3 n2(modelViewT[1]);
      vec3 n3(modelViewT[2]);
     
      // Get plane distances
      float d1(modelViewT[0].w);
      float d2(modelViewT[1].w);
      float d3(modelViewT[2].w);
     
      // Get the intersection of these 3 planes 
      // (uisng math from RealTime Collision Detection by Christer Ericson)
      vec3 n2n3 = cross(n2, n3);
      float denom = dot(n1, n2n3);
     
      vec3 d(d1,d2,d3);
      vec3 v = cross(n1, d);
     
      vec3 top;
      top.x = dot(d, n2n3);
      top.y = dot(n3, v);
      top.z = -dot(n2, v);
     
      return top / -denom;
    }
     
     
    vec3 ExtractCameraPos_NoScale(const mat4 & a_modelView)
    {
      mat3 rotMat(a_modelView);
      vec3 d(a_modelView[3]);
     
      vec3 retVec = -d * rotMat;
      return retVec;
    }
     
     
    void PrintVec3(const vec3 & a_vec)
    {
      printf("%f %f %f", a_vec.x, a_vec.y, a_vec.z); 
    }
     
    int main(int argc, char * argv[])
    {
      vec3 cameraPos(1.234f, 2.657f, 7.865f);
     
      // Create a model view matrix with translations and scales
      mat4 modelView(1.0f);
      modelView = rotate(modelView, 0.5f, vec3(1.0f, 0.0f, 0.0f));
      modelView = rotate(modelView, 0.2f, vec3(0.0f, 1.0f, 0.0f));
      modelView = rotate(modelView, 0.3f, vec3(0.0f, 0.0f, 1.0f));
      modelView = scale(modelView, vec3(2.0f, 3.0f, 4.0f));
     
      modelView = translate(modelView, -cameraPos);
     
      vec3 cameraPos2 = ExtractCameraPos(modelView);
      vec3 cameraPos2a = ExtractCameraPos2(modelView); 
     
      // ExtractCameraPos3 does not seem as accurate as the previous 2 methods
      vec3 cameraPos2b = ExtractCameraPos3(modelView); 
     
      mat4 viewModel = inverse(modelView);
      vec3 cameraPos3(viewModel[3]);
     
      // Do not add scale if using this
      //vec3 cameraPos4 = ExtractCameraPos_NoScale(modelView);
     
      PrintVec3(cameraPos); printf(" - Base Camera\n");
      PrintVec3(cameraPos2); printf(" - Plane intersection extraction\n");
      PrintVec3(cameraPos2a); printf(" - Plane intersection extraction - optimize\n");
      PrintVec3(cameraPos2b); printf(" - Plane intersection extraction alternate\n");
      PrintVec3(cameraPos3); printf(" - Inverse Matrix\n");
     
      return 0;
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •