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:
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.
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)
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
#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;
}