Matrix Details

Back to documentation index.

Matrix Details

A matrix is a rectangular array that can describe a transformation from one coordinate system to another. Transformations that matrices can describe include translation (shifting), scaling, and rotation. Functions dealing with matrices begin with “mat”. A 3x3 or 4x4 matrix has 9 or 16 elements, respectively.

This section contains detailed information on matrices.

Contents

Arrangement

In mathematical publications, matrices are often notated in column-major order, in which each element of the matrix is placed in columns as opposed to rows, as in the following example:

matrix[0] matrix[4] matrix[8] matrix[12] matrix[1] matrix[5] matrix[9] matrix[13] matrix[2] matrix[6] matrix[10] matrix[14] matrix[3] matrix[7] matrix[11] matrix[15]

The numbers in brackets in the matrix above are the zero-based indices into the matrix arrays passed to MathUtil’s matrix methods.

For 3x3 matrices, the elements are arranged in the following order:

matrix[0] matrix[3] matrix[6] matrix[1] matrix[4] matrix[7] matrix[2] matrix[5] matrix[8]

A Matrix Transforms Between Coordinate Systems

A transformed 3D coordinate system is made up of an X, Y, and Z axis, and a center of the coordinate system. These are four 3-element vectors that describe how the three axes and the center map to the new coordinate system in relation to the old coordinate system.

The following depiction of a 4x4 matrix illustrates the meaning of each of its elements. To keep things simple, this matrix’s transformation is one that keeps straight lines straight and parallel lines parallel.

[0] X-axis X [4] Y-axis X [8] Z-axis X [12] Center X [1] X-axis Y [5] Y-axis Y [9] Z-axis Y [13] Center Y [2] X-axis Z [6] Y-axis Z [10] Z-axis Z [14] Center Z [3] 0 [7] 0 [11] 0 [15] 1

The following is an example of a transformation matrix.

1 0 0 2 0 0.5 -0.866025</mtd> 3 </mtr> 0 0.866025</mtd> 0.5</mtd> 4 </mtr> 0 0 0 1 </mtable> </mfenced> </math> Here, the first column shows an X-axis vector at (1, 0, 0), the second column shows a Y-axis vector at (0, 0.5, 0.866025), the third column shows a Z-axis vector at (0, -0.866025, 0.5), and the fourth column centers the coordinate system at (2, 3, 4). Provided the matrix can be [**inverted**](#Matrix_Inversions), the three axis vectors are _basis vectors_ of the coordinate system. ### Why a 4x4 Matrix? A matrix can describe _linear transformations_ from one vector in space to another. These transformations, which include [**scaling**](#Scaling), [**rotation**](#Rotation), and shearing, can change where a vector points _at_, but not where it points _from_. It's enough to use a 3x3 matrix to describe linear transformations in 3D space. But certain other transformations, such as [**translation**](#Translation) and [**perspective**](#Projective_Transformations), are common in 3D computer graphics. To describe translation and perspective in 3D, the 3x3 matrix must be augmented by an additional row and column, turning it into a 4x4 matrix. A 4x4 matrix can describe linear transformations in 4D space and transform 4-element vectors. A 4-element vector has four components: X, Y, Z, and W. If a 4-element vector represents a 3D point, these components are the point's _homogeneous coordinates_ (unless the vector's W is 0). To convert these coordinates back to 3D, divide X, Y, and Z by W. This is usually only required, however, if the matrix describes a perspective projection (see [**"Projective Transformations"**](#Projective_Transformations)). A similar situation applies in 2D between 2x2 and 3x3 matrices as it does in 3D between 3x3 and 4x4 matrices. ## Transforming Points The transformation formula multiplies a matrix by a 3D point to change that point's position: * **a′**_x_ = matrix[0] ⋅ **a**_x_ + matrix[4] ⋅ **a**_y_ + matrix[8] ⋅ **a**_z_ + matrix[12] ⋅ **a**_w_ * **a′**_y_ = matrix[1] ⋅ **a**_x_ + matrix[5] ⋅ **a**_y_ + matrix[9] ⋅ **a**_z_ + matrix[13] ⋅ **a**_w_ * **a′**_z_ = matrix[2] ⋅ **a**_x_ + matrix[6] ⋅ **a**_y_ + matrix[10] ⋅ **a**_z_ + matrix[14] ⋅ **a**_w_ * **a′**_w_ = matrix[3] ⋅ **a**_x_ + matrix[7] ⋅ **a**_y_ + matrix[11] ⋅ **a**_z_ + matrix[15] ⋅ **a**_w_ For more on why **a′**_w_ appears here, see [**"Why a 4x4 Matrix?"**](#Why_a_4x4_Matrix). In each formula that follows, **a**_w_ is assumed to be 1 (indicating a conventional 3D point). The following sections describe different kinds of matrix transformations in more detail. Related functions: * MathUtil.mat4transform() - Transforms a 4-element vector with a 4x4 matrix * MathUtil.mat3transform() - Transforms a 3-element vector with a 3x3 matrix * MathUtil.mat4projectVec3 - Does a perspective-correct transformation of a 3D point with a 4x4 matrix ### Scaling Scaling changes an object's size. Scaling uses the 1st, 6th, and 11th elements of the matrix as seen here: sx 0 0 0 0 sy 0 0 0 0 sz 0 0 0 0 1 where the X coordinate is multiplied by `sx`, the Y coordinate is multiplied by `sy`, and the Z coordinate is multiplied by `sz`. The scaling formula would look like: * **a′**_x_ = sx ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 0 * **a′**_y_ = 0 ⋅ **a**_x_ + sy ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 0 * **a′**_z_ = 0 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + sz ⋅ **a**_z_ + 0 * **a′**_w_ = 0 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 1 = 1 For example, we multiply the input x by `sx` to get the output x. If `sx` is 1, x remains unchanged. Likewise for y (`sy`) and z (`sz`). If `sx`, `sy`, or `sz` is -1, that coordinate is _reflected_ along the corresponding axis. If `sx`, `sy`, and `sz` are all 1, we have an _identity matrix_, where the input vector is equal to the output vector. When the transformed X, Y, or Z axis has a length other than 1, the coordinate system will be scaled up or down along that axis. The scalings given here will scale the lengths of the corresponding axes. For example, if `sx` is 2, the X axis will be (2, 0, 0) and thus have a length of 2. Related functions: * MathUtil.mat4scaled() - Returns a scaling matrix * MathUtil.mat4scale() - Multiplies a matrix by a scaling. * MathUtil.mat4scaleInPlace() - Multiplies a matrix in place by a scaling. * MathUtil.mat4identity() - Returns a 4x4 identity matrix * MathUtil.mat3identity() - Returns a 3x3 identity matrix ### Translation A translation is a shifting of an object's position. In a transformation matrix, this shifting effectively happens after all other transformations such as scaling and rotation. It uses the 13th, 14th, and 15th elements of the matrix as seen here: 1 0 0 tx 0 1 0 ty 0 0 1 tz 0 0 0 1 where `tx` is added to the X coordinate, `ty` is added to the Y coordinate, and `tz` is added to the Z coordinate. The transformation formulas would look like: * **a′**_x_ = 1 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + tx * **a′**_y_ = 0 ⋅ **a**_x_ + 1 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + ty * **a′**_z_ = 0 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 1 ⋅ **a**_z_ + tz * **a′**_w_ = 0 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 1 = 1 For example, we add the input x and `tx` to get the output x. If `tx` is 0, x remains unchanged. Likewise for y (`ty`) and z (`tz`). Related functions: * MathUtil.mat4translated() - Returns a translation matrix * MathUtil.mat4translate() - Multiplies a matrix by a translation. ### Rotation Rotation changes an object's orientation. Given an angle of rotation, θ, the transformation matrix for rotating 3D points is as follows. (For a list of common sines and cosines, see the end of this section.)
1 0 0 0 0 cosθ -sinθ 0 0 sinθ cosθ 0 0 0 0 1
Rotation about the X axis.
cosθ 0 sinθ 0 0 1 0 0 -sinθ 0 cosθ 0 0 0 0 1
Rotation about the Y axis.
cosθ -sinθ 0 0 sinθ cosθ 0 0 0 0 1 0 0 0 0 1
Rotation about the Z axis.
Note that: * When we rotate a point about the X axis, the X coordinate is unchanged and the Y and Z coordinates are adjusted in the rotation. For rotations about the Y axis or the Z axis, the Y or Z coordinate, respectively, is likewise unchanged. * If the axis of rotation points backward from the "eye", positive rotations mean counterclockwise rotation in glmath. For example, 60 degrees about the axis means 60 degrees counterclockwise, and negative 60 degrees means 60 degrees clockwise. * Rotating a point around an arbitrary axis of rotation is more complicated to describe. When describing an axis of rotation, [1, 0, 0] is the X axis, [0, 1, 0] is the Y axis, and [0, 0, 1] is the Z axis. See [**"Rotation example"**](#Rotation_Example) for an illustration of a rotation transformation. Related functions: * MathUtil.mat4rotated() - Returns a rotation matrix * MathUtil.mat4rotate() - Multiplies a matrix by a translation. A list of common sines and cosines follows. Values shown with three decimal places are approximate. |   | 0°| 22.5°| 30°| 45°| 60°| 67.5°| 90°| 112.5°| 120°| 135°| 150°| 157.5°| 180°| -------|---|------|----|----|----|------|----|------|-----|-----|-----|-------|-----| | sin | 0 | 0.383 | 0.5 | 0.707 | 0.866 | 0.924 | 1 | 0.924 | 0.866 | 0.707 | 0.5 | 0.383 | 0 | | cos | 1 | 0.924 | 0.866 | 0.707 | 0.5 | 0.383 | 0 | -0.383 | -0.5 | -0.707 | -0.866 | -0.924 | -1 | |   | 180°| 202.5°| 210°| 225°| 240°| 247.5°| 270°| 292.5°| 300°| 315°| 330°| 337.5°| 360°| -------|---|------|----|----|----|------|----|------|-----|-----|-----|-------|-----| | sin | 0 | -0.383 | -0.5 | -0.707 | -0.866 | -0.924 | -1 | -0.924 | -0.866 | -0.707 | -0.5 | -0.383 | 0 | | cos | -1 | -0.924 | -0.866 | -0.707 | -0.5 | -0.383 | 0 | 0.383 | 0.5 | 0.707 | 0.866 | 0.924 | 1 | ### Matrix Multiplication When two matrices are multiplied, the combined matrix will be such that the transformations they describe happen in reverse order. For example, if the first matrix (input matrix) describes a translation and the second matrix describes a scaling, the multiplied matrix will describe the effect of scaling then translation. Matrix multiplication is not commutative; the order of multiplying matrices is important. To get an insight of how matrix multiplication works, treat the second matrix as a group of column vectors (with the same number of rows as the number of columns in the first matrix). Multiplying the two matrices transforms these vectors to new ones in the same way as if the column vectors were transformed individually. (This also explains why there can be one or more column vectors in the second matrix and not just four in the case of a 4x4 matrix, and also why transforming a 4-element column vector is the same as multiplying a 4x4 matrix by a matrix with one column and four rows.*) This insight reveals a practical use of matrix multiplication: transforming four 4-element vectors at once using a single matrix operation involving two 4x4 matrices. After the matrix multiplication, each of the transformed vectors will be contained in one of the four columns of the output matrix. The methods `mat4multiply`, `mat4scale`, `mat4scaleInPlace`, `mat4translate`, and mat4rotate involve multiplying 4x4 matrices. Related functions: * MathUtil.mat4multiply() - Multiplies two matrices \* Reading the [**tutorial by Dmitry Sokolov**](https://github.com/ssloy/tinyrenderer/wiki/Lesson-4:-Perspective-projection) led me to this highly useful insight. ### Projective Transformations In all the transformations described above, the last row in the transformation matrix is (0, 0, 0, 1). (Such transformations are called _affine transformations_, those that keep straight lines straight and parallel lines parallel.) However, this is not the case for some transformations in the `MathUtil` class. Transformations that don't necessarily preserve parallelism of lines are called _projective transformations_. An NxN matrix can describe certain projective transformations if it has one more row and one more column than the number of dimensions. For example, a 4x4 matrix can describe 3D projective transformations in the form of linear transformations on homogeneous coordinates (see [**"Why a 4x4 Matrix?"**](#Why_a_4x4_Matrix)). For a 3D projective transformation, the last row in the matrix is not necessarily (0, 0, 0, 1). One example of a projective transformation is found in a _perspective projection_ matrix, as returned by MathUtil.mat4perspective or MathUtil.mat4frustum. When a 4-element vector is transformed with this matrix, its W component is generated by setting it to the negative Z coordinate in _eye space_, or more specifically, as follows: * **a′**_w_ = 0 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + -1 ⋅ **a**_z_ + 0 For more on perspective projections, see _The "Camera" and Geometric Transforms_. Related functions: * MathUtil.mat4frustum() - Returns a frustum matrix * MathUtil.mat4perspective() - Returns a field-of-view perspective matrix ### Matrix Inversions An inverted matrix describes a transformation that undoes another transformation. For example, if a scaling enlarges an object, the inverted matrix reduces the object to its original size. To invert a **translation**, reverse the sign of the translation elements (given above as `tx`, `ty`, and `tz`) and generate a new translation matrix with the new translation elements. For example, to invert the translation (5, 2, -3), use the translation (-5, -2, 3). To invert a **scaling**, use 1 divided by each of the scaling elements (given above as `sx`, `sy`, and `sz`) and generate a new scaling matrix with those elements. For example, to invert the scaling (2, 3, 4), use the scaling (1/2, 1/3, 1/4). To invert a **rotation** for a 4x4 matrix, swap the 2nd and 5th elements of the matrix, the 3rd and 9th elements, and the 7th and 10th elements of the matrix (zero-based elements 1, 4, 2, 8, 6, and 9 respectively). The effect is like reversing the angle of the rotation to reset an object to its previous orientation. In general, to invert an NxN rotation matrix, _transpose_ that matrix (so that its rows become its columns and vice versa). #### Inverting a General NxN Matrix Matrices that use some combination of translation, scaling, and rotation as well as other kinds of matrices are more complicated to invert. In fact, some matrices can't be inverted at all. Many 4x4 or 3x3 matrices can be inverted using the MathUtil.mat4invert() or MathUtil.mat3invert() methods, respectively. To describe how inverting a matrix works, we will need to define some terms: * A matrix cell's _row index_ and _column index_ tell where that cell appears in the matrix. For example, a cell on the first row has row index 0 and a cell on the second column has column index 1. * A matrix's _determinant_ is its overall scaling factor. Only an NxN matrix with a determinant other than 0 can be inverted. To find a matrix's determinant: 1. For each cell in the first row (or first column), find the matrix's _minor_ at that cell (that is, the determinant of a matrix generated by eliminating the row and column where that cell appears in the original matrix). 2. Label the minors (A, B, C, D, ...), in that order. 3. The matrix's determinant is (A - B + C - D + ...). A 1x1 matrix's determinant is simply the value of its only cell. To invert an NxN matrix: 1. Create a new NxN matrix. 2. For each cell in the original matrix, find its minor at that cell (that is, the determinant of a matrix generated by eliminating the row and column where that cell appears in the original matrix), and set the corresponding cell in the new matrix to that value. 3. In the new matrix, reverse the sign of each cell whose row index plus column index is odd. (These cells will alternate in a checkerboard pattern in the matrix.) 4. Transpose the new matrix (convert its rows to columns and vice versa). 5. Find the original matrix's determinant and divide each cell in the new matrix by that value. 6. The new matrix will be the inverted form of the original NxN matrix. ## Rotation Example As an example, say we rotate 60 degrees about the X axis (`mat4rotated(60, 1, 0, 0)`, θ = 60°). First, we find the rotation formula for the X axis: * **a′**_x_ = 1 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 0 * **a′**_y_ = 0 ⋅ **a**_x_ + (cos θ) ⋅ **a**_y_ + -(sin θ) ⋅ **a**_z_ + 0 * **a′**_z_ = 0 ⋅ **a**_x_ + (sin θ) ⋅ **a**_y_ + (cos θ) ⋅ **a**_z_ + 0 * **a′**_w_ = 0 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 1 = 1 We calculate cos θ as 0.5 and sin θ as about 0.866025. We plug those numbers into the rotation formula to get a formula for rotating a point 60 degrees about the X axis. * **a′**_x_ = 1 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 0 = **a**_x_ * **a′**_y_ ~= 0 ⋅ **a**_x_ + 0.5 ⋅ **a**_y_ + -0.866025 ⋅ **a**_z_ + 0 * **a′**_z_ ~= 0 ⋅ **a**_x_ + 0.866025 ⋅ **a**_y_ + 0.5 ⋅ **a**_z_ + 0 * **a′**_w_ = 0 ⋅ **a**_x_ + 0 ⋅ **a**_y_ + 0 ⋅ **a**_z_ + 1 = 1 If a point is located at (10, 20, 30), the rotated point would now be: * **a′**_x_ = 1 ⋅ 10 + 0 ⋅ 20 + 0 ⋅ 30 + 0 * = 1 ⋅ 10 * = 10 * **a′**_y_ ~= 0 ⋅ 10 + 0.5 ⋅ 20 + -0.866025 ⋅ 30 + 0 * ~= 0.5 ⋅ 20 + -0.866025 ⋅ 30 * ~= 10 + -25.98075 * ~= -15.98075 * **a′**_z_ ~= 0 ⋅ 10 + 0.866025 ⋅ 20 + 0.5 ⋅ 30 + 0 * ~= 0.866025 ⋅ 20 + 0.5 ⋅ 30 * ~= 17.3205 + 15 * ~= 32.3205 * **a′**_w_ = 0 ⋅ 10 + 0 ⋅ 20 + 0 ⋅ 30 + 1 * = 1 So the rotated point would be at about (10, -15.98075, 32.3205). [Back to documentation index.](index.html)