An inclusive reference of transformation matrices for basic OpenGL work. It is intended as a quick reference while coding OpenGL without libraries.

There are many tutorials and guides online that explain how to use these matrices and mathematic fundamentals. If you are new to the subject, I recommend reading this article, which is an overview of the whole process explained in a cognitive way.

If you are looking for a deeper explanation of affine/homogenous mathematics and how these matrices are constructed, I highly recommend Essential Mathematics for Games and Interactive Applications. Of all the books I’ve used for my personal research, this one stands out as the most complete and comprehensive. For example, many books skip the View transformation, even the OpenGL red book, but this one goes into the nitty-gritty details of everything.

Model Operations

The \(Model \rightarrow World\) matrix is the resulting matrix of an ordered multiplication of transformations (scale first, then rotate, then translate).

\(M_{model \rightarrow world} = T_{ranslate} \cdot R_{otate} \cdot S_{cale}\)

Translation Matrix

\( \begin{aligned} T_{(x,y,z)} = \begin{bmatrix} 1 & 0 & 0 & x\\\ 0 & 1 & 0 & y\\\ 0 & 0 & 1 & z\\\ 0 & 0 & 0 & 1\\\ \end{bmatrix} \end{aligned} \)

Rotation Matrices

\( \begin{aligned} R_x = \begin{bmatrix} 1 & 0 & 0 & 0 \\\ 0 & \cos\theta & -\sin\theta & 0 \\\ 0 & \sin\theta & \cos\theta & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} , R_y = \begin{bmatrix} \cos\theta & 0 & \sin\theta & 0 \\\ 0 & 1 & 0 & 0 \\\ -\sin\theta & 0 & \cos\theta & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} , R_z = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\\ \sin\theta & \cos\theta & 0 & 0 \\\ 0 & 0 & 1 & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Multiply all the matrices to apply the general rotation, shown below. \(\alpha\) = x rotation, \(\beta\) = y rotation and \(\gamma\) = z rotation. Note that the rotations are applied in the following order: x first, then y, then z.

\( \begin{aligned} R_z \cdot R_y \cdot R_x = \begin{bmatrix} \cos\beta\cos\gamma & \cos\gamma\sin\alpha\sin\beta - \cos\alpha\sin\gamma & \cos\alpha\cos\gamma\sin\beta + \sin\alpha\sin\gamma & 0 \\\ \cos\beta\sin\gamma & \cos\alpha\cos\gamma + \sin\alpha\sin\beta\sin\gamma & -\cos\gamma\sin\alpha + \cos\alpha\sin\beta\sin\gamma & 0 \\\ -\sin\beta & \cos\beta\sin\alpha & \cos\alpha\cos\beta & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Scale Matrix

\( \begin{aligned} S_{(x,y,z)} = \begin{bmatrix} x & 0 & 0 & 0 \\\ 0 & y & 0 & 0 \\\ 0 & 0 & z & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Shear Matrix

The following combines all shearing operations, the notation \(Sr_{xy}\) denotes a shear along \(x\) by \(y\). It is rarely used in practice.

\( \begin{aligned} Sr_{(xy,xz,yx,yz,zx,zy)} = \begin{bmatrix} 1 & Sr_{yx} & Sr_{zx} & 0 \\\ Sr_{xy} & 1 & Sr_{zy} & 0 \\\ Sr_{xz} & Sr_{yz} & 1 & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

View Matrices

There is more than one technique to compute the \(World \rightarrow View\) matrix. Covered here are the Inverse model calculations and the Look At calculations.

Matrix Inverse

You can treat the camera as a normal model. Apply initial orientation, then rotation, then translation to obtain \(V\). The inverse of this matrix is your \(World \rightarrow View\) matrix. Calculating the inverse of a 4x4 matrix is costly, so special properties are used to simplify the inverse operation.

\(V_{view \rightarrow world} = T_{ranslate} \cdot R_{otate} \cdot V_{orientation}\)

\(V_{world \rightarrow view} = V_{view \rightarrow world}^{-1}\)

First, we define a starting view orientation. We will use the standard right-handed OpenGL version: \(\vec{Vside} = (1,0,0), \vec{Vup} = (0,1,0), -\vec{Vforward} = -(0,0,1)\). This defines a camera facing \(-z\), with \(y\) going up and \(x\) going right.

\( \begin{aligned} V_{orientation} = \begin{bmatrix} Vside_x & Vup_x & -Vforward_x & 0 \\\ Vside_y & Vup_y & -Vforward_y & 0 \\\ Vside_z & Vup_z & -Vforward_z & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\\ 0 & 1 & 0 & 0 \\\ 0 & 0 & -1 & 0 \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Next, we compute our translation and rotation using previous matrices. Multiply the starting orientation with rotation, then translation to obtain \(V\).

\(V_{view \rightarrow world} = T_{ranslate} \cdot R_{otate} \cdot V_{orientation}\)

To invert \(V\), we can use special affine properties of our transformations and invert it with the following calculation.

\( \begin{aligned} V^{-1}_{world \rightarrow view} = \begin{bmatrix} R^{-1} & -(R^{-1} \cdot V_{pos}) \\\ 0^T & 1 \\\ \end{bmatrix} = \begin{bmatrix} R^T & -(R^T \cdot V_{pos}) \\\ 0^T & 1 \\\ \end{bmatrix} \end{aligned} \)

For example: \( \begin{aligned} V_{view \rightarrow world} = \begin{bmatrix} R_{00} & R_{01} & R_{02} & Vpos_x \\\ R_{10} & R_{11} & R_{12} & Vpos_y \\\ R_{20} & R_{21} & R_{22} & Vpos_z \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} , V^{-1}_{world \rightarrow view} = \begin{bmatrix} R_{00} & R_{10} & R_{20} & -R_{00}Vpos_x - R_{10}Vpos_y - R_{20}Vpos_z \\\ R_{01} & R_{11} & R_{21} & -R_{01}Vpos_x - R_{11}Vpos_y - R_{21}Vpos_z \\\ R_{02} & R_{12} & R_{22} & -R_{02}Vpos_x - R_{12}Vpos_y - R_{22}Vpos_z \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Look At Operation

Another solution to compute our \(World \rightarrow View\) matrix is using the Look At formula and operations. We define \(eye\) as our camera position, \(target\) as our look-at target point, \(up\) as the upward direction \((0,1,0)\).

First, we compute the rotational portion of our matrix.

\( \hat{Vforward} = \frac{\vec{target} - \vec{eye}}{|\vec{target} - \vec{eye}|} \)

\( \hat{Vside} = \frac{\vec{Vforward} \times \vec{up}}{|\vec{Vforward} \times \vec{up}|} \)

\( \hat{Vup} = \frac{\vec{Vforward} \times \vec{Vside}}{|\vec{Vforward} \times \vec{Vside}|} \)

\( \begin{aligned} V_{rot} = \begin{bmatrix} Vside_x & Vup_x & -Vforward_x \\\ Vside_y & Vup_y & -Vforward_y \\\ Vside_z & Vup_z & -Vforward_z \\\ \end{bmatrix} \end{aligned} \)

Next, using the inverse of our camera translation \(-eye\), we compute the translation vector \(Vpos\).

\(V_{pos} = V_{rot} \cdot -\vec{eye}\)

\( \begin{aligned} V_{pos} = \begin{bmatrix} Vside_x & Vup_x & -Vforward_x \\\ Vside_y & Vup_y & -Vforward_y \\\ Vside_z & Vup_z & -Vforward_z \\\ \end{bmatrix} \cdot \begin{bmatrix} -eye_x \\\ -eye_y \\\ -eye_z \\\ \end{bmatrix} \end{aligned} \)

Finally, we compose our view matrix using \(Vrot\) and \(Vpos.\)

\( \begin{aligned} V^{-1}_{world \rightarrow view} = \begin{bmatrix} Vside_x & Vup_x & -Vforward_x & Vpos_x \\\ Vside_y & Vup_y & -Vforward_y & Vpos_y \\\ Vside_z & Vup_z & -Vforward_z & Vpos_z \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Projection Matrices

Perspective Projection

This matrix assumes a symmetric projection volume. It is equivalent to the gluPerspective() call. We will need \(\theta_{fov}\) which is your field of view (something close to 90), \(ar\) the aspect ratio of your projection, \(n\) the near clipping plane, \(f\) the far clipping plane.

\( d = \cot(\frac{\theta_{fov}}{2}) \)

\( ar = \frac{width}{height} \)

\( \begin{aligned} P_{persp} = \begin{bmatrix} \frac{d}{ar} & 0 & 0 & 0 \\\ 0 & d & 0 & 0 \\\ 0 & 0 & \frac{n+f}{n-f} & \frac{2nf}{n-f} \\\ 0 & 0 & -1 & 0 \\\ \end{bmatrix} \end{aligned} \)

Oblique Projection

This projection works for non-symmetric projections and is a generalized version of the perspective projection. It is equivalent to the glFrustum() call. We will need \(n\) the near clipping plane, \(f\) the far clipping plane, \([l, r]\) the left and right interval along \(x\) on the near clipping plane, \([t, b]\) the top and bottom interval along \(y\) on the near clipping plane.

\( \begin{aligned} P_{obl} = \begin{bmatrix} \frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\\ 0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\\ 0 & 0 & \frac{n+f}{n-f} & \frac{2nf}{n-f} \\\ 0 & 0 & -1 & 0 \\\ \end{bmatrix} \end{aligned} \)

Orthographic Projection

This projection does not resize further objects, it is useful for 2D games and special applications. It is equivalent to the glOrtho() call. We will need \(n\) the near clipping plane, \(f\) the far clipping plane, \([l, r]\) the left and right interval along \(x\) on the near clipping plane, \([t, b]\) the top and bottom interval along \(y\) on the near clipping plane.

\( \begin{aligned} P_{ortho} = \begin{bmatrix} \frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\\ 0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\\ 0 & 0 & -\frac{2}{f-n} & -\frac{f+n}{f-n} \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Oblique Orthographic Projection

A special case of the orthographic projection, with a shear along the \(z\) axis.

\( \begin{aligned} P_{obl ortho} = \begin{bmatrix} \frac{2}{r-l} & 0 & \frac{1}{r-l} & -\frac{r+l-n}{r-l} \\\ 0 & \frac{2}{t-b} & \frac{1}{t-b} & -\frac{t+b-n}{t-b} \\\ 0 & 0 & -\frac{2}{f-n} & -\frac{f+n}{f-n} \\\ 0 & 0 & 0 & 1 \\\ \end{bmatrix} \end{aligned} \)

Final Notes

That should cover most your needs. If there is a useful transform missing, or mathematical errors, please leave a comment.

Cover image : Snail by iQ.