Matrix Math for Game Devs

What is a Matrix?

A matrix is just a 2-dimensional array of variables or values. Each matrix has a size, determined by its number of rows and columns. For example, a 2×3 matrix (2 rows, 3 columns) can look like this:

[abcdef]or[1.5240134.22]\begin{bmatrix} a & b & c \\ d & e & f \end{bmatrix} \quad \quad or \quad \quad \begin{bmatrix} 1.5 & 2 & 40 \\ 13 & 4.2 & 2\end{bmatrix}

Matrices can be used for a variety of things, from solving equations to applying transformations to vectors, such as translation, rotation, and scale.

Matrix Addition

Adding two matrices together results in a matrix which, when multiplied by a vector matrix, results in a sort of averaging effect of the matrices on the resulting vector:

(Ma+Mb)v=(Mav)+(Mbv)(M_a+M_b) * \vec v = (M_a*\vec v) + (M_b * \vec v)

Essentially it’s the same as applying each matrix to the vector individually, and then adding the resulting vectors together to produce the final result vector.

To add two matrices together, we need the matrices to be the same dimensions, and then we just add each element of the two matrices:

[abcd]+[efgh]=[a+eb+fc+gd+h]\begin{bmatrix} a & b \\ c & d \end{bmatrix} + \begin{bmatrix} e & f \\ g & h\end{bmatrix} = \begin{bmatrix} a+e & b+f \\ c+g & d+h\end{bmatrix}

Matrix addition has very niche uses in game development, but is used for linear blend skinning, which is a technique for calculating vertex positions based on bone weights and transforms, which would look something like this:

skinned vertex position(v)=[(wb1Mb1)+(wb2Mb2)+...+(wbnMbn)]vskinned\ vertex \ position(\vec v) = [(w_{b1} * M_{b1}) + (w_{b2} * M_{b2}) + … + (w_{bn}*M_{bn})] *\vec v

Matrix Multiplication

Matrix multiplication is often used to manipulate points, or vectors in games. We can take a transformation matrix, and when we multiply it by our vector, the result will be our vector transformed in 2D/3D space.

Matrix multiplication can also be used to create composite transformations which apply multiple transformations at once. This is done by multiplying the two transformation matrices.

To multiply two matrices, A and B, matrix B must have the same number of rows as the matrix A has columns. The result of the operation is a Arows×BcolumnsA_{rows} \times B_{columns} matrix.

In the following example, note that the matrix A is a 2×1 matrix, with 2 rows and 1 column. Matrix B must therefore have 1 row, but can have any number of columns. The result is a 2×2 matrix (Arows×BcolumnsA_{rows} \times B_{columns}).

[ab][cd]=[acadbcbd]\begin{bmatrix} a \\ b \end{bmatrix} \cdot \begin{bmatrix} c & d \end{bmatrix} = \begin{bmatrix} ac & ad \\ bc & bd\end{bmatrix}

To calculate each value in the result, we take these steps:

  1. Start out at matrix A, row 0 (the top row), and matrix B, column 0 (the left column).
  2. Multiply each value in the current row of A by the corresponding value of B current column, and sum the result of all of those multiplications.
  3. The resulting value is placed in the same row as the current matrix A row, and the same column as the current column of matrix B.
  4. If there is another column in matrix B, move on to that column and repeat the process again for the current row of matrix A. If not, reset to use column 0 of matrix B.
  5. Once all columns have been multiplied and summed, and while we have additional rows, move on to the next row of matrix A, and repeat the process until all values have been computed.

Here is an example of multiplying a 2×3 matrix with a 3×1 matrix…the result is a 2×1 matrix:

[abcdef][ghi]=[ag+bh+cidg+eh+fi]\begin{bmatrix} a & b & c \\ d & e & f \end{bmatrix} \cdot\begin{bmatrix} g \\ h \\ i \end{bmatrix} = \begin{bmatrix} ag + bh + ci \\ dg + eh + fi \end{bmatrix}

And we get a 3×4 matrix when we multiply a 3×2 matrix with a 2×4 matrix:

[abcdef][ghijklmn]=[ag+bkah+blai+bmaj+bncg+dkch+dlci+dmcj+dneg+fkeh+flei+fmej+fn]\begin{bmatrix} a & b \\ c & d \\ e & f \end{bmatrix} \cdot\begin{bmatrix} g & h & i & j \\ k & l & m & n\end{bmatrix} = \begin{bmatrix} ag + bk & ah + bl & ai + bm & aj+bn \\ cg+dk & ch+dl & ci+dm & cj+dn \\ eg+fk & eh+fl & ei+fm & ej+fn \end{bmatrix}

Identity Matrices

An identity matrix essentially preserves the values of a matrix when that matrix is multiplied by the identity matrix. It’s pretty much like multiplying by 1. If you have a matrix which represents an object’s 3D transform (position, rotation, and scale), then multiplying by an identity matrix will result in no change to the object’s transform.

An identity matrix consists of 0s, except for the diagonal from top left to bottom right, which is composed of 1s. The following are all examples of identity matrices:

[1]\begin{bmatrix} 1 \end{bmatrix}
[1001]\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}
[100010001]\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}
[1000010000100001]\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

In the example below, you can see how multiplying a matrix by the identity matrix results in the original matrix:

[abcdef][100010001]=[1a+0b+0c0a+1b+0c0a+0b+1c1d+0e+0f0d+1e+0f0d+0e+1f]=[abcdef]\begin{bmatrix} a & b & c \\ d & e & f\end{bmatrix} \cdot\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{bmatrix} = \begin{bmatrix} 1a+0b+0c & 0a+1b+0c & 0a+0b+1c \\ 1d+0e+0f & 0d+1e+0f & 0d+0e+1f \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f\end{bmatrix}

Transpose of a Matrix

The transpose of a matrix flips a matrix around its diagonal, changing the rows values into column values and vice versa.

For example, given a matrix, M:

M=[abcdef]M = \begin{bmatrix} a & b & c \\ d & e & f \end{bmatrix}

If we take the transpose of the matrix (denoted with a superscript T), we swap around the diagonal, a…e…etc.:

MT=[adbecf]M^T = \begin{bmatrix} a & d \\ b & e \\ c & f\end{bmatrix}

Determinants

A determinant can only be found for square matrices. Determinants of matrices are useful for a number of things:

  1. Finding out whether a matrix has an inverse matrix or not.
  2. Determining whether a formula has a single solution in a linear system (Ax=bAx = b).
  3. Calculating the signed area of a parallelogram or a triangle between two 2D vectors.
  4. Calculating the signed volume of a parallelepiped formed by three 3D vectors.
  5. Measuring how much a matrix will scale area (in 2D) or volume (in 3D).
  6. Determining whether a matrix will preserve a vector’s orientation or reverse it.

To find the determinant of a matrix, we take the sums of the multiples of all diagonals, from top left to bottom right, and subtract the sum of the multiples of all diagonals which go from top right to bottom left.

As an example, here is how the determinant is calculated for a 3×3 matrix:

M=[abcdefghi][𝐚bcd𝐞fgh𝐢]+[a𝐛cde𝐟𝐠hi]+[ab𝐜𝐝efg𝐡i][ab𝐜d𝐞f𝐠hi][a𝐛c𝐝efgh𝐢][𝐚bcde𝐟g𝐡i]M =\begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i\end{bmatrix} \rightarrow \begin{bmatrix} \color{green}\bold a & b & c \\ d & \color{green}\bold e & f \\ g & h & \color{green}\bold i \end{bmatrix} + \begin{bmatrix} a & \color{green} \bold b & c \\ d & e & \color{green}\bold f \\ \color{green}\bold g & h & i \end{bmatrix} + \begin{bmatrix} a & b & \color{green}\bold c \\ \color{green}\bold d & e & f \\ g & \color{green}\bold h & i\end{bmatrix} – \begin{bmatrix} a & b & \color{red}\bold c \\ d & \color{red}\bold e & f \\ \color{red}\bold g & h & i \end{bmatrix} – \begin{bmatrix} a & \color{red}\bold b & c \\ \color{red}\bold d & e & f \\ g & h & \color{red}\bold i \end{bmatrix} – \begin{bmatrix} \color{red}\bold a & b & c \\ d & e & \color{red}\bold f \\ g & \color{red}\bold h & i \end{bmatrix}
determinant(M)=(aei+bfg+cdh)(ceg+bdi+afh)determinant(M) = \color{green}(aei+bfg+cdh) \color{white} – \color{red}(ceg + bdi + afh)

And for a 2×2 matrix:

M=[abcd][𝐚bc𝐝][a𝐛𝐜d]M = \begin{bmatrix} a & b \\ c & d \end{bmatrix} \rightarrow \begin{bmatrix} \color{green}\bold a & b \\ c & \color{green}\bold d \end{bmatrix} – \begin{bmatrix} a & \color{red}\bold b \\ \color{red}\bold c & d \end{bmatrix}
determinant(M)=adbcdeterminant(M) = \color{green}ad \color{white}- \color{red}bc

Checking if a matrix has an inverse

Determinants are useful for quickly determining if a matrix has in inverse. If the determinant is non-zero, and the matrix is a square matrix, it has an inverse.

Inverse of a Matrix

An inverse matrix is a matrix which, when multiplied by some other matrix, results in an identity matrix. These matrices can be used to perform the inverse operation of another matrix. For example, if you use a matrix to rotate a vector, then multiplying by the inverse of that matrix will undo the rotation.

For a matrix to have an inverse, it needs to be a square matrix (meaning it has the same number of rows and columns), and it must have a non-zero determinant.

To calculate the inverse matrix (denoted by a superscript -1), we divide a matrix’s adjugate matrix by the determinant of the matrix:

M1=adjugate(M)determinant(M)M^{-1} = \frac{adjugate(M)}{determinant(M)}

Calculating the Adjugate Matrix

An adjucate matrix can be found by taking the transpose of a cofactor matrix. A cofactor matrix is formed by taking the minor of each element in a matrix, and then multiplying by a power of -1, where the power depends on the position of the element in the matrix.

For each element of a matrix, we take the minor by eliminating all values of the row and column that element is in, then taking the determinant of the remaining values.

Consider the matrix below, where each value is attributed with a row and column, for example, the top right is in row 1, column 3.

[v1,1v1,2v1,3v2,1v2,2v2,3v3,1v3,2v3,3]\begin{bmatrix} \color{red} v_{1,1} & \color{red} v_{1,2} & \color{orange} v_{1,3} \\ \color{yellow} v_{2,1} & \color{yellow} v_{2, 2} & \color{red} v_{2,3} \\ \color{yellow} v_{3,1} & \color{yellow} v_{3,2} & \color{red} v_{3,3} \end{bmatrix}

In order to find the minor for v1,3v_{1,3} we remove all values in row 1, and all values in column 3, which gives us a matrix like this:

minor(v1,3)=determinant[v2,1v2,2v3,1v3,2]=(v2,1v3,2)(v2,2v3,1)minor(v_{1,3}) = determinant\begin{bmatrix} \color{yellow}v_{2,1} & \color{yellow}v_{2,2} \\ \color{yellow}v_{3,1} & \color{yellow}v_{3,2} \end{bmatrix} = (v_{2,1}*v_{3,2}) – (v_{2,2}*v_{3,1})

Then we take the cofactor and multiply it by the minor. The cofactor is (1)row+column(-1)^{row + column}. So in this case:

cofactor(v1,3)=(1)1+3=(1)4=1cofactor(v_{1,3}) = (-1)^{1+3} = (-1)^4 = 1

It’s more intuitive to look at a matrix to understand what the cofactor value for any given element would be. As you can see below, the value alternates between 1 and -1 based on whether the row and column for any element are even and odd. This scales up and down for all matrix sizes:

[1111111111111111111111111]\begin{bmatrix} \color{green}1 & \color{yellow}-1 & \color{green}1 & \color{yellow}-1 & \color{green}1\\\color{yellow}-1 & \color{green}1 & \color{yellow}-1 & \color{green}1 & \color{yellow}-1\\ \color{green}1 & \color{yellow}-1 & \color{green}1& \color{yellow}-1 & \color{green}1\\ \color{yellow}-1 & \color{green}1 & \color{yellow}-1 & \color{green}1 & \color{yellow}-1 \\\color{green}1 & \color{yellow}-1 & \color{green}1 & \color{yellow}-1 & \color{green}1\end{bmatrix}

So, to get our cofactor matrix, we do this process and compute the minor value for each element in the original matrix, then multiply that by the appropriate 1/-1 cofactor, and then place the result in a matrix in the same row and column as the element/value being evaluated.

We then take the transpose of this cofactor matrix, and that gives us the adjugate matrix.

Dividing the adjugate by the original matrix’s determinant gives us our inverse matrix:

Inverse(M)=M1=adj(M)det(M)Inverse(M) = M^{-1} = \frac{adj(M)}{det(M)}

Translation

2D Translation

To translate a vector by some amount in 2D space, we multiply a translation matrix by a matrix representing our 2D vector. First we build the translation matrix using the 3×3 identity matrix, and we set the right-most column’s values to the X and Y offsets we want to apply to our vector.

Translation Matrix (T)=[10offsetx01offsety001]Translation\ Matrix \ (T)= \begin{bmatrix} 1 & 0 & offset_x \\ 0 & 1 & offset_y \\ 0 & 0 & 1 \end{bmatrix}

We then multiply the translation matrix by our vector matrix:

Offset Vector=[10offsetx01offsety001][vxvy1]=[vx+offsetxvy+offsety1]=v+offsetOffset\ Vector= \begin{bmatrix} 1 & 0 & offset_x \\ 0 & 1 & offset_y \\ 0 & 0 & 1\end{bmatrix} \cdot\begin{bmatrix} \vec v_x \\ \vec v_y \\ 1 \end{bmatrix} = \begin{bmatrix} \vec v_x+offset_x \\ \vec v_y + offset_y \\ 1\end{bmatrix} = \vec v + offset

3D Translation

To translate a vector by some offset in 3D, we essentially do the same thing as in 2D, but we use a 4×4 identity matrix for our translation matrix, including the Z value for our offset, and we use a 4×1 array for our vector’s matrix:

Offset Vector=[100offsetx010offsety001offsetz0001][vxvyvz1]=[vx+offsetxvy+offsetyvz+offsetz1]=point+offsetOffset\ Vector= \begin{bmatrix} 1 & 0 & 0 & offset_x \\ 0 & 1 & 0 & offset_y \\ 0 & 0 & 1 & offset_z \\ 0 & 0 & 0 & 1\end{bmatrix} \cdot\begin{bmatrix} \vec v_x \\ \vec v_y \\ \vec v_z \\ 1\end{bmatrix} = \begin{bmatrix} \vec v_x + offset_x \\ \vec v_y + offset_y \\\vec v_z + offset_z \\ 1 \end{bmatrix} = point + offset

Rotation

2D Rotation

For 2D rotations, the following 2×2 matrix can be used, where θ\theta is the angle of rotation:

rotation2d(v)=[cosθsinθsinθcosθ][vxvy]rotation_{2d}(\vec v) = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta\end{bmatrix} \cdot \begin{bmatrix} \vec v_x \\ \vec v_y \end{bmatrix}

Or if using in combination with other transformations such as translation and scale (created by combining with a 1×1 larger identity matrix):

rotation2d(v)=[cosθsinθ0sinθcosθ0001][vxvy1]rotation_{2d}(\vec v) = \begin{bmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1\end{bmatrix} \cdot \begin{bmatrix} \vec v_x \\ \vec v_y \\ 1\end{bmatrix}

3D Rotation

Rotations can be performed on a 3D vector on a per-axis basis, where θ\theta is the rotation angle around that axis, the following matrices can be used:

And for 3D:

Xaxis=[1000cosθsinθ0sinθcosθ]Yaxis=[cosθ0sinθ010sinθ0cosθ]Zaxis=[cosθsinθ0sinθcosθ0001]X_{axis} = \begin{bmatrix} 1 & 0 & 0 \\ 0 & cos\theta & -sin\theta \\ 0 & sin\theta & cos\theta\end{bmatrix} \quad \quad Y_{axis} = \begin{bmatrix} cos\theta & 0 & sin\theta \\ 0 & 1 & 0 \\ -sin\theta & 0 & cos\theta\end{bmatrix} \quad \quad Z_{axis} = \begin{bmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}

And when using rotation matrices in combination with other transformations, such as translation and scale, the following matrices can be created by combining the rotation matrices with a 1×1 larger identity matrix:

Xaxis=[10000cosθsinθ00sinθcosθ00001]Yaxis=[cosθ0sinθ00100sinθ0cosθ00001]Zaxis=[cosθsinθ00sinθcosθ0000100001]X_{axis} = \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1\end{bmatrix} \quad \quad Y_{axis} = \begin{bmatrix} cos\theta & 0 & sin\theta & 0 \\ 0 & 1 & 0 & 0\\ -sin\theta & 0 & cos\theta & 0 \\ 0 & 0 & 0 & 1\end{bmatrix} \quad \quad Z_{axis} = \begin{bmatrix} cos\theta & -sin\theta & 0 & 0 \\ sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1\end{bmatrix}

Additionally, for rotations around an arbitrary axis (in any direction), we can define a unit vector, a\vec a, as the axis we want to rotate around, and use the following matrix for the transformation:

Rotation Matrix(a)=[ax2(1cosθ)+cosθaxay(1cosθ)azsinθaxaz(1cosθ)+aysinθaxay(1cosθ)+azsinθay2(1cosθ)+cosθayaz(1cosθ)axsinθaxaz(1cosθ)aysinθayaz(1cosθ)+axsinθaz2(1cosθ)+cosθ]Rotation\ Matrix(\vec a) = \begin{bmatrix} \vec a_x^2 (1-cos\theta) + cos\theta & \vec a_x\vec a_y(1-cos\theta) – \vec a_zsin\theta & \vec a_x\vec a_z(1-cos\theta)+\vec a_ysin\theta \\ \vec a_x\vec a_y(1-cos\theta) + \vec a_zsin\theta & \vec a_y^2(1-cos\theta)+cos\theta & \vec a_y\vec a_z(1-cos\theta)-\vec a_xsin\theta \\ \vec a_x \vec a_z(1-cos\theta)-\vec a_ysin\theta & \vec a_y \vec a_z(1-cos\theta) + \vec a_xsin\theta & \vec a_z^2(1-cos\theta) + cos\theta\end{bmatrix}

Scale

To scale a vector by some amount, we simply replace the values in an identity matrix with our scale values for each dimension, and then multiply the matrix by our vector, v\vec v, to get our scaled result, s\vec s. In 2D:

[scalex000scaley0001][vxvy1]=[sxsy1]\begin{bmatrix} scale_x & 0 & 0\\ 0 & scale_y & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot\begin{bmatrix} \vec v_x \\ \vec v_y \\ 1 \end{bmatrix} = \begin{bmatrix} \vec s_x \\ \vec s_y \\ 1\end{bmatrix}

And in 3D:

[scalex0000scaley0000scalez00001][vxvyvz1]=[sxsysz1]\begin{bmatrix} scale_x & 0 & 0 & 0\\ 0 & scale_y & 0 & 0 \\ 0 & 0 & scale_z & 0\\ 0 & 0 & 0 & 1\end{bmatrix} \cdot\begin{bmatrix} \vec v_x \\ \vec v_y \\ \vec v_z \\ 1\end{bmatrix} = \begin{bmatrix} \vec s_x \\ \vec s_y \\ \vec s_z \\ 1\end{bmatrix}

The Transformation Matrix (Translation, Rotation, and Scale – Together)

One of the nice features of matrices is that, using matrix multiplication, we can combine them into a composite matrix. This is often utilized in game engines to improve efficiency and performance, since we can combine our rotation, translation, and scale into a single matrix, reducing the number of calculations by two thirds.

But we have to make sure we do these operations in the right order, since the result is order-dependent. Usually, game engines compute the composite transformation matrix such that the scale is applied first, then the rotation, and lastly, the translation: TRST \cdot R \cdot S. That looks like this:

[100offsetx010offsety001offsetz0001][10000cosθsinθ00sinθcosθ00001][scalex0000scaley0000scalez00001]=TRS Matrix\begin{bmatrix} 1 & 0 & 0 & offset_x \\ 0 & 1 & 0 & offset_y \\ 0 & 0 & 1 & offset_z \\ 0 & 0 & 0 & 1\end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1\end{bmatrix} \cdot \begin{bmatrix} scale_x & 0 & 0 & 0\\ 0 & scale_y & 0 & 0 \\ 0 & 0 & scale_z & 0\\ 0 & 0 & 0 & 1\end{bmatrix} = TRS\ Matrix

It’s important to keep in mind that matrices are multiplied with the right-most pair first, which gives us the combined rotation and scale:

[10000cosθsinθ00sinθcosθ00001][scalex0000scaley0000scalez00001]=Rotation and Scale Matrix\begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1\end{bmatrix} \cdot \begin{bmatrix} scale_x & 0 & 0 & 0\\ 0 & scale_y & 0 & 0 \\ 0 & 0 & scale_z & 0\\ 0 & 0 & 0 & 1\end{bmatrix} = Rotation \ and\ Scale\ Matrix

Then, we multiply the RS matrix and the translation matrix:

[100offsetx010offsety001offsetz0001][RS11RS12RS13RS14RS21RS22RS23RS24RS31RS32RS33RS34RS41RS42RS43RS44]=TRS Matrix\begin{bmatrix} 1 & 0 & 0 & offset_x \\ 0 & 1 & 0 & offset_y \\ 0 & 0 & 1 & offset_z \\ 0 & 0 & 0 & 1\end{bmatrix} \cdot \begin{bmatrix} RS_{11} & RS_{12} & RS_{13} & RS_{14} \\ RS_{21} & RS_{22} & RS_{23} & RS_{24} \\ RS_{31} & RS_{32} & RS_{33} & RS_{34} \\ RS_{41} & RS_{42} & RS_{43} & RS_{44}\end{bmatrix} = TRS\ Matrix