How to get the camera parameters from OpenGLperspective/modelview matrix? (1)


This is a computer graphics and programming story. While ago, I wrote about a matrix that is generated by a OpenGL function, gluLookat. (http://shitohichiumaya.blogspot.de/2011/01/what-matrix-glulookat-generates-1.html) Why did I write that story? Because I was developing a renderer that is independent from the OpenGL, but some point, I need to overlay OpenGL generated image and our renderer's image. I think not a lot of people need to write a own renderer, therefore, this blog entry maybe not so much refereed. However, there are quite a lot of people visited this page. I don't know what kind of people visiting this pages, I am a bit surprised. This time, our customer has a special system and they need to compute the inverse of the OpenGL functions. Their input is OpenGL's Projection/Modelview matrix and the output is camera parameters. This is more special cases, but, the problem itself may be interested in.


There are interesting visualization system in this world. This time, my customer told us, ``We don't know where our camera is, but we know the matrices. Could you compute your rendering system's camera parameter out of them?'' I was a bit surprised this story, how a system which can manipulate camera doesn't know the camera position? But the story is not so simple. This software is a commercial huge system. One of the software layer can not see the camera parameter because it is simply the system is complex. But they use OpenGL and it is possible to access the perspective/modelview matrices at their software layer. Therefore, they just asked us to extract the camera parameters from these matrices.

This is quite a rare case, though, it is also an interesting puzzle: given OpenGL projection/modelview matrices, how to get the camera parameters? Maybe there are a few people who find this interesting.

Get camera parameter from the OpenGL perspective matrix.

This section, we talk about how to get perspective camera parameters from the OpenGL perspective matrix.

In OpenGL, gluPerspective generates the perspective matrix. (Please refer the OpenGL manual in details.)

void gluPerspective(GLdouble fovyd,  GLdouble aspect,
                    GLdouble zNear, GLdouble zFar);
  • fovyd: Specifies the field of view angle, in degrees, in the y direction. Note: this is degrees, not radian.
  • aspect: Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).
  • zNear: Specifies the distance from the viewer to the near clipping plane (always positive).
  • zFar: Specifies the distance from the viewer to the far clipping plane (always positive).
According to the OpenGL manual, the generated perspective matrix is the following.

   \frac{fr}{asp}  & 0  & 0 & 0  \\
   0               & fr & 0 & 0  \\
   0               & 0  & \frac{z_f + z_n}{z_n - z_f} & -1 \\
   0               & 0  & \frac{2 z_f z_n}{z_n - z_f} & 0  \\
where \(fr = \frac{1}{\tan(\frac{fovy}{2})}\), \(fovy\) is in radian of  \(fovyd\), \(asp\) is aspect ratio, \(z_n\) is the near clipping plane distance, \(z_f\) is the far clipping plane distance. For simplicity, we rewrite the perspective matrix as the following.
   aa & 0  & 0  & 0  \\
   0  & bb & 0  & 0  \\
   0  & 0  & cc & -1 \\
   0  & 0  & dd & 0  \\
 asp  &=& \frac{bb}{aa}\\
 fovy &=& 2 \arctan(\frac{1}{bb})\\
 z_f  &=&\frac{c-1}{c+1} z_n
Here, let \(kk = \frac{c-1}{c+1}\), we get
 z_n &=& \frac{dd(1-kk)}{2k}\\
 z_f &=& \frac{dd(1-kk)}{2}.
Note \(z_n \neq 0\).

My implementation example of those in C++ is the following.

/// get perspective camera parameters
/// from the OpenGL projection matrix
/// \param[out] fovy_rad     field of view in radian
/// \param[out] aspect_ratio aspect ratio
/// \param[out] clip_min     clipping min distance
/// \param[out] clip_max     clipping max distance
void gl_get_camera_parameter_from_perspective_matrix(
    double & fovy_rad,
    double & aspect_ratio,
    double & clip_min,
    double & clip_max)
    GLdouble mat[16];
    glGetDoublev(GL_PROJECTION_MATRIX, mat);

    GLdouble const aa = mat[0];
    GLdouble const bb = mat[5];
    GLdouble const cc = mat[10];
    GLdouble const dd = mat[14];

    aspect_ratio = bb / aa;
    fovy_rad     = 2.0f * atan(1.0f / bb);

    GLdouble const kk = (cc - 1.0f) / (cc + 1.0f);
    clip_min = (dd * (1.0f - kk)) / (2.0f * kk);
    clip_max = kk * clip_min;

For simplicity, I omitted the division by zero check. Of course when OpenGL has been correctly set up, then this should be OK. Though, personally I would put some assertions.

No comments: