Calibrated Cameras and gluPerspective

June 18, 2013

After posting my last article relating glFrustum to the intrinsic camera matrix, I receieved some emails asking how the (now deprecated) gluPerspective function relates to the intrinsic matrix. We can show a similar result with gluPerspective as we did with glFrustum, namely that it is the product of a glOrtho matrix and a (modified) intrinsic camera matrix, but in this case the intrinsic matrix has different constraints. I'll be re-using notation and concepts from the previous article, so if you aren't familiar with them, I recommend reading it first.

Decomposing gluPerspective

The matrix generated by gluPerspective is

\[ \begin{align} \left ( \begin{array}{cccc} \frac{f}{\text{aspect}} & 0 & 0 & 0 \\ 0 & f & 0 & 0 \\ 0 & 0 & C' & D' \\ 0 & 0 & -1 & 0 \end{array} \right ) \end{align} \]

where

\[ \begin{align} f &= \cot(fovy/2) \\ C' &= -\frac{far + near}{far - near} \\ D' &= -\frac{2 \; far \; near}{far - near} \\ \end{align} \]

Like with glFrustum, gluPerspective permits no axis skew, but it also restricts the viewing volume to be centered around the camera's principal (viewing) axis. This means that the principal point offsets \(x_0\) and \(y_0\) must be zero, and the matrix generated by glOrtho must be centered, i.e. bottom = -top and left = -right. The Persp matrix corresponding to the intrinsic matrix is:

\[ Persp = \left( \begin{array}{cccc} \alpha & 0 & 0 & 0 \\ 0 & \beta & 0 & 0 \\ 0 & 0 & A & B \\ 0 & 0 & -1 & 0 \end{array} \right) \]

where

\[ \begin{align} A &= near + far \\ B &= near * far \end{align} \]

and the NDC matrix is

\[ \begin{align} NDC &= \left( \begin{array}{cccc} \frac{2}{right - left} & 0 & 0 & t_x \\ 0 & \frac{2}{top - bottom} & 0 & t_y \\ 0 & 0 & -\frac{2}{far - near} & t_z \\ 0 & 0 & 0 & 1 \end{array} \right) \\[1.5em] &= \left( \begin{array}{cccc} \frac{2}{width} & 0 & 0 & 0 \\ 0 & \frac{2}{height} & 0 & 0 \\ 0 & 0 & -\frac{2}{far - near} & t_z \\ 0 & 0 & 0 & 1 \end{array} \right) \end{align} \]

where

\[ \begin{align} t_x &= -\frac{right + left}{right - left} \\ t_y &= -\frac{top + bottom}{top - bottom} \\ t_z &= -\frac{far + near}{far - near} \end{align} \]

It is easy to show that the product \((NDC \times Persp)\) is equivalent to the matrix generated by gluPerspective(fovy, aspect, near, far) with

\[ \begin{align} \text{fovy} &= 2 \text{arctan}\left (\frac{\text{height}}{2 \beta} \right ) \\ \text{aspect} &= \frac{\beta}{\alpha} \frac{\text{width}}{\text{height}}. \end{align} \]

glFrustum vs. gluPerpsective

In my experience, the zero-skew assumption is usually reasonable, so glFrustum can provide a decent approximation to the full intrinsic matrix. However there is quite often a non-negligible principal point offset (~ 2% of the image size), even in high-quality cameras. For this reason, gluPerspective might be a good choice for quick-and-dirty demos, but for the most accurate simulation, you should use the full camera matrix like I described previously.

Posted by Kyle Simek
blog comments powered by Disqus