Matrix Compendium
Introduction
The primary goal of this article is to consolidate knowledge on transformations in Computer Graphics into a single, comprehensive resource.
Although these concepts are widely available online, they are often presented in disconnected fragments, lacking cohesive explanations of their subtleties. This article seeks to address those gaps and provide a thorough understanding of the topic.
Many real-world problems initially appear simple, but their underlying complexity becomes evident upon closer examination. To explore these challenges comprehensively, we will employ the well-known divide and conquer technique. By breaking this intricate problem into smaller sub-problems and addressing each individually, we can create a structured reference guide.
This approach will bring clarity and organization to the challenges often encountered in transformations within computer graphics.
These sub-problems can be categorized from both a mathematical (theoretical) and an implementation (practical) perspective as follows:
-
The order of matrix multiplication: Ensuring operations follow consistent sequencing rules.
-
The handedness of three-dimensional Euclidean space: Determining orientation conventions, such as the right-hand rule.
-
The order of basis vectors in a Cartesian coordinate system: Establishing the sequence for defining axes.
-
The storage of matrices in memory as multidimensional arrays: Optimizing memory representation for efficient computations.
Matrix 101
In this article, we will explore matrices and their properties in depth. Before diving into the details, let’s briefly cover a few foundational concepts that are essential for understanding their nuances.
What is a matrix?
In mathematics, a matrix is essentially a rectangular array of numbers organized into rows and columns.
For example, the matrix below has 3 rows and 5 columns and can be referred to as a \mathbf{3 \times 5} matrix:
\begin{array}{c} \begin{bmatrix} m_{11} & m_{12} & m_{13} & m_{14} & m_{15}\\ m_{21} & m_{22} & m_{23} & m_{24} & m_{25}\\ m_{31} & m_{32} & m_{33} & m_{34} & m_{35}\\ \end{bmatrix} \end{array}
Why Are Matrices Important for Computer Graphics?
The answer can be summarized by the following points:
-
They serve as an excellent algebraic tool for representing geometry in a structured way.
-
Transformations such as Affine and Projective in three-dimensional Euclidean space can be represented using \mathbf{4 \times 4} matrices.
-
Concatenation of transformations, or combining two transformations, can be expressed as matrix multiplication.
-
Matrices with one row or column (in other words, row or column vectors) can store mesh vertices.
-
Transformation of mesh vertices from one space to another is, once again, a usage of matrix multiplication.
-
Matrices themselves and operations on them can be efficiently implemented in computer graphics applications.
How Do We Compute Matrix Multiplication?
The algorithm for matrix multiplication is not as straightforward as matrix addition, where corresponding elements from both matrices are added component-wise. Instead, it is slightly more complex and involves combining rows and columns through a series of operations.
To illustrate this concept, consider the following visualization:
The value of each element in matrix C
is calculated by multiplying each corresponding entry in the iᵗʰ row of matrix A
with the jᵗʰ column of matrix B
and summing these products. In essence, this operation is equivalent to performing a dot product between the iᵗʰ row of matrix A
and the jᵗʰ column of matrix B
.
It is important to note:
-
Matrix multiplication is generally not commutative—the order of multiplicands in multiplication matters.
-
For matrix multiplication to be valid, the number of columns in the first matrix (on the left side of the operator) must match the number of rows in the second matrix.
\begin{array}{c} \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \\ a_{31} & a_{32} \\ a_{41} & a_{42} \\ \end{bmatrix} * \begin{bmatrix} b_{11} & b_{12} & b_{13}\\ b_{21} & b_{22} & b_{23}\\ \end{bmatrix} = \begin{bmatrix} c_{11} & c_{12} & c_{13}\\ c_{21} & c_{22} & c_{23}\\ c_{31} & c_{32} & c_{33}\\ c_{41} & c_{42} & c_{43}\\ \end{bmatrix} \end{array}
Divide and Conquer
Once we have completed the preliminary explanations, we can shift our attention to the core of this article.
The Order of Matrix Multiplication
Now that we understand matrix multiplication, we can discuss its order. One of the key—yet often overlooked—aspects of transformations in computer graphics is that matrix multiplication is generally non-commutative.
We are accustomed to the fact that multiplication of real numbers is commutative. Therefore, when we learn about matrices, it is surprising—and often leads to errors in practice—that matrix multiplication is non-commutative:
\begin{array}{c} \mathbf{A} * \mathbf{B} \neq \mathbf{B} * \mathbf{A} \end{array}
where A and B are matrices.
To minimize challenges in practice, two distinct approaches—or conventions—have emerged, each offering a method for ensuring consistent and correct application of matrix multiplication.
In computer graphics, these conventions are referred to as:
-
Pre-multiplication Transformation
-
Post-multiplication Transformation
It is worth noting that the terminology of pre- and post-multiplication is used by some authors but not universally.
For example, in linear algebra, vectors are typically treated as column vectors when solving systems of equations, leading to a preference for post-multiplication order in textbooks.
Matrix Multiplication Order and Transformations in Three-Dimensional Euclidean Space
In computer graphics applications, transformations such as translation, rotation, scaling, and shearing are combined to create realistic virtual worlds. These transformations are represented using 4 \times 4 matrices, and combining them is equivalent to multiplying matrices.
It is crucial to choose and apply the correct order consistently to achieve the desired result.
While matrix multiplication is non-commutative, it remains associative:
(\mathbf{A} * \mathbf{B}) * \mathbf{C} = \mathbf{A} * (\mathbf{B} * \mathbf{C})
In the examples below, parentheses are used solely to indicate the order in which transformations are combined.
Let’s describe the problem of matrix multiplication order for transformation concatenation using the following example:
\begin{array}{c} \mathbf{Z} = \mathbf{A} * \mathbf{B} * \mathbf{C} * \mathbf{D} \end{array}
where A, B, C, D, Z are matrices.
Parentheses are helpful to clearly indicate the sequence (order) of operations. In mathematics, multiplication is typically assumed to proceed from left to right.
The differences between the two conventions are summarized in the table below:
Post-multiplication |
Pre-multiplication |
|
---|---|---|
Order of operations |
\mathbf{Z} = \mathbf{A} * \mathbf{B} * \mathbf{C} * \mathbf{D} = \mathbf{A} * (\mathbf{B} * (\mathbf{C} * \mathbf{D})) | \mathbf{Z} = \mathbf{A} * \mathbf{B} * \mathbf{C} * \mathbf{D} = ((\mathbf{A} * \mathbf{B}) * \mathbf{C}) * \mathbf{D} |
Transforming a vector |
\vec{V_w} = \mathbf{M} * \vec{V_c} | \vec{V_w} = \vec{V_c} * \mathbf{M} |
Vector representation |
\begin{bmatrix}x \ y \z \w\end{bmatrix} = \begin{bmatrix}m_{11} & m_{12} & m_{13} & m_{14}\m_{21} & m_{22} & m_{23} & m_{24}\m_{31} & m_{32} & m_{33} & m_{34}\m_{41} & m_{42} & m_{43} & m_{44}\end{bmatrix}*\begin{bmatrix}v_1 \ v_2 \v_3 \v_4\end{bmatrix} | \begin{bmatrix}x & y & z & w\end{bmatrix} =\begin{bmatrix}v_1 & v_2 & v_3 & v_4\end{bmatrix}*\begin{bmatrix}m_{11} & m_{21} & m_{31} & m_{41}\m_{12} & m_{22} & m_{32} & m_{42}\m_{13} & m_{23} & m_{33} & m_{43}\m_{14} & m_{24} & m_{34} & m_{44}\end{bmatrix} |
Final Transformation Matrix |
\mathbf{M_f} = \color{magenta}\mathbf{M_p}\color{default} * \color{cyan}\mathbf{M_v}\color{default} * \color{yellow}\mathbf{M_m}\color{default} | \mathbf{M_f} = \color{yellow}\mathbf{M_m}\color{default} * \color{cyan}\mathbf{M_v}\color{default} * \color{magenta}\mathbf{M_p}\color{default} |
World Matrix Composition |
\mathbf{M_w} = \color{Violet}\mathbf{M_a}\color{default} * \color{SpringGreen}\mathbf{M_l}\color{default} | \mathbf{M_w} = \color{SpringGreen}\mathbf{M_l}\color{default} * \color{Violet}\mathbf{M_a}\color{default} |
Local Transformation Matrix |
\color{SpringGreen}\mathbf{M_l}\color{default} = \color{red}\mathbf{M_t}\color{default} * \color{green}\mathbf{M_r}\color{default} * \color{RoyalBlue}\mathbf{M_s}\color{default} | \color{SpringGreen}\mathbf{M_l}\color{default} = \color{RoyalBlue}\mathbf{M_s}\color{default} * \color{green}\mathbf{M_r}\color{default} * \color{red}\mathbf{M_t}\color{default} |
Local Transformation Matrix |
\color{SpringGreen}\mathbf{M_l}\color{default} = \color{red}\mathbf{M_t}\color{default} * \color{green}\mathbf{M_r}\color{default} * \color{grey}\mathbf{M_h}\color{default} * \color{RoyalBlue}\mathbf{M_s}\color{default} | \color{SpringGreen}\mathbf{M_l}\color{default} = \color{RoyalBlue}\mathbf{M_s}\color{default} * \color{grey}\mathbf{M_h}\color{default} * \color{green}\mathbf{M_r}\color{default} * \color{red}\mathbf{M_t}\color{default} |
Impact of convention |
Transformations composed right-to-left |
Transformations composed left-to-right |
Impact of convention |
Preferred matrix storage in column-major layout |
Preferred matrix storage in row-major layout |
Impact of convention |
Mesh vertices treated as column vectors |
Mesh vertices treated as row vectors |
Where:
\vec{V_w} = Transformed vector in another space.
\vec{V_c} = Transformed vector in original space.
\mathbf{M} = Transform matrix.
\mathbf{M_f} = Final transformation matrix.
\color{yellow}\mathbf{M_m}\color{default} = Model transformation matrix (transforms from local model space to global world space).
\color{cyan}\mathbf{M_v}\color{default} = View matrix (transforms from world space to camera space).
\color{magenta}\mathbf{M_p}\color{default} = Projection matrix (transforms from camera space to clipping space).
\mathbf{M_w} = World transformation matrix.
\color{SpringGreen}\mathbf{M_l}\color{default} = Current local transformation matrix.
\color{Violet}\mathbf{M_a}\color{default} = Transformation matrix of the parent node.
\color{RoyalBlue}\mathbf{M_s}\color{default} = Scaling matrix.
\color{green}\mathbf{M_r}\color{default} = Rotation matrix.
\color{red}\mathbf{M_t}\color{default} = Translation matrix.
\color{grey}\mathbf{M_h}\color{default} = Shear matrix.
The Handedness of Three-Dimensional Euclidean Space
Before discussing the topic of the orientation of three-dimensional Euclidean space, one more issue should be mentioned and described: the “Up Direction” axial convention.
Three-Dimensional Cartesian Coordinate System: “Up Direction”
Mathematically, no axis is inherently designated as the Up Direction in three-dimensional Euclidean space.
However, in 3D applications, two conventions have emerged over the years:
-
Z-Axis as “Up Direction”
This convention is particularly useful in fields such as architecture. When the working space is a plane representing, for example, the Earth’s surface, the two main working directions are the X-Axis and Y-Axis, while the Z-Axis represents elevation, or the “Up Direction.”
-
Y-Axis as “Up Direction”
In this convention, the working plane corresponds to the monitor screen. The X-Axis and Y-Axis represent the width and height of the screen, respectively, while the Z-Axis represents depth. This setup aligns with our everyday perception of the world, such as in first-person shooter (FPS) games, where the Y-Axis represents the “Up Direction.”
Cross Product in Three-Dimensional Euclidean Space
In computer graphics, particularly in three-dimensional Euclidean space, the cross product is a highly useful and common operation. The cross product of two non-collinear vectors results in another vector that is perpendicular to both original vectors.
This operation introduces ambiguity because the direction of the resulting vector does not have a unique solution—it could point in two equally valid directions. To resolve this ambiguity, we select an orientation (or handedness) for the three-dimensional Euclidean space. By making this choice, we designate one cross-product direction as the preferred solution.
To get a unique and consistent result, we use either the right-hand rule or the left-hand rule.
It is worth mentioning that in mathematics and physics, the right-hand orientation is the most commonly used. This orientation is applied to determine directions in magnetic fields, electromagnetic fields, and enantiomers in chemistry.
Left-Hand Rule |
Right-Hand Rule |
---|---|
The right-hand rule and left-hand rule can be described as follows:
Let the straightened index finger of the chosen hand (right or left) point in the direction of the first vector (\vec{a}), and let the bent middle finger point in the direction of the second vector (\vec{b}). Finally, the extended thumb will indicate the direction of the cross-product vector \vec{a} \times \vec{b}.
To determine the directions of the X, Y, and Z axes in three-dimensional Euclidean space, replace the first vector with the direction of the X-Axis and the second vector with the direction of the Y-Axis. The thumb will then indicate the direction of the Z-Axis.
Using this method, we can designate a three-dimensional Euclidean space with the desired handedness (orientation). The orientations, right-hand or left-hand, can be summarized in the table below (if applicable).
Handedness |
Left-Hand Rule |
Right-Hand Rule |
---|---|---|
Y-Up |
||
Z-Up |
Importance of Choosing the Handedness
Choosing the handedness is crucial as it determines not only the direction of the cross-product vector but also:
-
The orientation of surface normal vectors.
-
The direction in which points rotate around an axis.
-
The order of vertices in triangle meshes.
Left-Hand Rule |
Right-Hand Rule |
---|---|
Anticommutativity of Cross Product
Another key property of the cross product of two non-collinear vectors is anticommutativity:
\begin{array}{c} \vec{a} \times \vec{b} = - \vec{b} \times \vec{a} \end{array}
If you need to fix a rendering error by reversing the result of a cross product, keep in mind that you are not simply adding a *negative sign* to the vector. Instead, you are altering the handedness of your three-dimensional Euclidean space.
Left-Hand Rule |
Right-Hand Rule |
---|---|
The Order of Basis Vectors in a Cartesian Coordinate System
This topic should be introduced prior to the concept of handedness in Euclidean space, as the two are closely related. It is usually covered earlier in linear algebra classes. However, describing it now will make it easier to understand.
The concept of basis vectors often remains hidden in the shadows of linear algebra textbooks, and its significance is frequently overlooked, despite being fundamental to linear algebra and vector spaces.
For example, in three-dimensional Euclidean space, the following basis vectors:
\vec{e_0} = (1,0,0) \\ \vec{e_1} = (0,1,0) \\ \vec{e_2} = (0,0,1) \\
form the building blocks of the Euclidean space. They allow any vector in this space to be described in terms of these basis vectors.
However, defining the basis vectors is only part of the process—it is also necessary to determine their order, specifying which vector is designated as the first, second, and third. For example:
{\vec{e_0},\vec{e_1},\vec{e_2}} | {\vec{e_0},\vec{e_2},\vec{e_1}} |
---|---|
The order of the basis vectors is important because, while coordinate systems defined by different orders may appear similar, they are not the same and differ in subtle ways:
\{\vec{e_0},\vec{e_1},\vec{e_2}\} \neq \{\vec{e_0},\vec{e_2},\vec{e_1}\}
When the basis vectors and their order are determined, we establish the Cartesian coordinate system. This allows us to uniquely determine the position of points in space and define the Positive Direction—the direction of rotation when increasing the angle of rotation.
From a mathematical and computational perspective, there is no universally better or worse ordering of basis vectors. However, physicists consistently use the right-hand rule, a convention based on the same principle as the cross-product vector direction. This rule eliminates ambiguities, facilitates collaboration, and ensures coherence in calculations across different contributors.
Unfortunately, the absence of a standardized framework in computer graphics increases the likelihood of errors. This article aims to highlight the importance of addressing this issue.
Positive Direction in Two-Dimensional Euclidean Space
The concept of Positive Direction refers to the direction of rotation established by starting from the first chosen basis vector (or axis of the coordinate system) and moving in the direction of the second basis vector.
Visually, this can be easily explained in two-dimensional Euclidean space, as shown in the table below:
Basis Vector Order {\vec{e_0},\vec{e_1}} or X \rightarrow Y |
Basis Vector Order {\vec{e_1},\vec{e_0}} or Y \rightarrow X |
---|---|
Counterclockwise rotation |
Clockwise rotation |
From the X-axis towards the Y-axis |
From the Y-axis towards the X-axis |
This demonstrates why the order of basis vectors matters.
Positive Direction and Rotation Matrices
To describe a rotation in two- or three-dimensional Euclidean space, a rotation matrix is commonly used. This matrix is then applied to rotate a vector (or a point treated as a vector) by a specified angle.
The rotation is achieved by multiplying the vector by the matrix. Matrix multiplication is not commutative, as we’ve seen, so two conventions exist for rotation: pre-multiplication and post-multiplication.
The order of basis vectors plays a nuanced role in defining the rotation. For example, the same rotation matrix:
R = \begin{bmatrix} \cos(\phi) & -\sin(\phi)\\ \sin(\phi) & \cos(\phi)\\ \end{bmatrix}
depending on whether it is used in pre- or post-multiplication, determines the Positive Direction of rotation for different orders of basis vectors.
In the two-dimensional case, this relates to {\vec{e_0},\vec{e_1}} or {\vec{e_1},\vec{e_0}}.
Post-multiplication convention v’ = R * v |
Pre-multiplication convention v’ = v * R |
---|---|
Counterclockwise rotation |
Clockwise rotation |
Basis Vector Order {\vec{e_0},\vec{e_1}} or X \rightarrow Y |
Basis Vector Order {\vec{e_1},\vec{e_0}} or Y \rightarrow X |
Ensuring Coherence in Basis Vector Order and Rotation
Maintaining coherence in the concept of the order of basis vectors is critical when determining the position of a point in space and the unique Positive Direction of rotation.
To ensure coherence, the specific form of the rotation matrix must correspond to a particular order of basis vectors, along with the chosen matrix multiplication convention.
All of these options can be summarized in the table below:
Basis Vector Order {\vec{e_0},\vec{e_1}} or X \rightarrow Y |
Basis Vector Order {\vec{e_1},\vec{e_0}} or Y \rightarrow X |
|
---|---|---|
Post-multiplication convention |
\begin{bmatrix}\cos(\phi) & \color{red}-\color{default}\sin(\phi)\sin(\phi) & \cos(\phi)\end{bmatrix} | \begin{bmatrix}\cos(\phi) & \sin(\phi)\color{red}-\color{default}\sin(\phi) & \cos(\phi)\end{bmatrix} |
Pre-multiplication convention |
\begin{bmatrix}\cos(\phi) & \sin(\phi)\color{red}-\color{default}\sin(\phi) & \cos(\phi)\end{bmatrix} | \begin{bmatrix}\cos(\phi) & \color{red}-\color{default}\sin(\phi)\sin(\phi) & \cos(\phi)\end{bmatrix} |
Positive Direction in Three-Dimensional Euclidean Space
To determine the Positive Rotation, in three-Dimensional Euclidean Space we can use a hand rule similar to the one used for the cross-product.
The right-hand rule and left-hand rule in this context can be described as follows:
Bent fingers point in the direction of the Positive Direction, and your thumb indicates the axis of rotation
or
Point your thumb in the direction of the axis of rotation, and your bent fingers will indicate the Positive Direction.
Let us illustrate these rules using the following table:
Left-Hand Axis of Rotation |
Right-Hand Axis of Rotation |
---|---|
|
|
In three-dimensional Euclidean space, we encounter several possible permutations of basis vector orders. Fortunately, these permutations can be grouped into two groups of basis vector orders:
- (X, Y, Z) = {\vec{e_0},\vec{e_1},\vec{e_2}}, {\vec{e_1},\vec{e_2},\vec{e_0}}, {\vec{e_2},\vec{e_0},\vec{e_1}}
- (X, Z, Y) = {\vec{e_0},\vec{e_2},\vec{e_1}}, {\vec{e_2},\vec{e_1},\vec{e_0}}, {\vec{e_1},\vec{e_0},\vec{e_2}}
In the table below, I aim to demonstrate why the topic of the order of basis vectors—and, consequently, the Positive Direction of rotation—can appear complex and challenging to comprehend.
Left-Hand Rule |
Right-Hand Rule |
|||
---|---|---|---|---|
(X, Y, Z) | (X, Z, Y) | (X, Y, Z) | (X, Z, Y) | |
Y-Up |
||||
Z-Up |
The variety of possible use cases increases the likelihood of making mistakes. How can we summarize the table above? After analyzing each case, the hand used to determine the order of the base vectors (and thus the Positive Direction of rotation), as well as the direction of the vector product, varies. In some cases, different hands are required, while in others, the same hand is used. The table below provides a summary of these cases:
Basis Vector Order Group: (X, Y, Z) |
Basis Vector Order Group: (X, Z, Y) |
|
---|---|---|
Right-Hand Rule for Cross-Product |
The SAME hand rule is used for both cross-product direction and Positive Direction |
The OPPOSITE hand rule is used for cross-product direction and Positive Direction |
Left-Hand Rule for Cross-Product |
The SAME hand rule is used for both cross-product direction and Positive Direction |
The OPPOSITE hand rule is used for cross-product direction and Positive Direction |
Maintaining coherence in basis vector order is essential for determining point positions in space and the Positive Direction of rotations. Only the basis vector order from the group (X, Y, Z) satisfies these criteria.
Finally, the Positive Direction in three-dimensional Euclidean space are illustrated in the table below:
Left-Hand Rule |
Right-Hand Rule |
|
---|---|---|
Y-Up |
||
Z-Up |
Basis Vector Order and Rotation Systems
Consider this: If you create a model in a Y-Up left-handed coordinate system but render it in a Z-Up left-handed coordinate system, swapping the Y and Z coordinates may seem like a simple fix.
However, this adjustment implicitly alters elements like plane normal vectors, triangle mesh winding orders, and associated animation data due to changes in Positive Direction and basis vector order.
Positive Direction and Quaternions
The order of basis vectors also affects quaternions. When W. R. Hamilton invented quaternions to describe rotations in three-dimensional Euclidean space, he defined them as Q = a + bi + cj + dk and established dependencies between the imaginary components: ij = k, jk = i, ki = j, and ijk = -1. When quaternions are defined using these rules, the Positive Direction of rotation is determined by the right-hand rule. In computer graphics, however, there are applications that use the left-hand rule to determine the Positive Direction. This raises the question: how should quaternions defined in this manner be handled?
The convention proposed by M.D. Shuster does not change the fundamental definition of the quaternion itself but modifies its dependencies on the imaginary components. This approach differs slightly from the quaternion definitions typically found in mathematics textbooks. Notably, Shuster’s definition is employed by NASA’s Jet Propulsion Laboratory and, in computer graphics, within the Microsoft DirectX Math Library.
The differences between Hamilton’s and Shuster’s quaternion definitions are summarized in the following table:
Hamilton’s Definition |
Shuster’s Definition |
|
---|---|---|
Imaginary Components Dependencies |
ij = k\ jk = i\ ki = j\ ijk = -1 | ij = -k\ jk = -i\ ki = -j\ ijk = 1 |
Positive Direction |
Determined by right-hand rule |
Determined by left-hand rule |
Matrix Multiplication Order |
Post-multiplication |
Pre-multiplication |
“Sandwich” Product Order |
v’ = Q v Q^{-1} | v’ = Q^{-1} v Q |
Rotation Matrix |
R = \begin{bmatrix}1 - 2(Q_y^2 + Q_z^2) & 2(Q_xQ_y \color{red}-\color{default} Q_zQ_w) & 2(Q_xQ_z + Q_yQ_w)\ 2(Q_xQ_y + Q_zQ_w) & 1 - 2(Q_x^2 + Q_z^2) & 2(Q_yQ_z \color{red}-\color{default} Q_xQ_w)\ 2(Q_xQ_z \color{red}-\color{default} Q_yQ_w) & 2(Q_yQ_z + Q_xQ_w) & 1 - 2(Q_x^2 + Q_y^2)\end{bmatrix} | R = \begin{bmatrix}1 - 2(Q_y^2 + Q_z^2) & 2(Q_xQ_y + Q_zQ_w) & 2(Q_xQ_z \color{red}-\color{default} Q_yQ_w)\ 2(Q_xQ_y \color{red}-\color{default} Q_zQ_w) & 1 - 2(Q_x^2 + Q_z^2) & 2(Q_yQ_z + Q_xQ_w)\ 2(Q_xQ_z + Q_yQ_w) & 2(Q_yQ_z \color{red}-\color{default} Q_xQ_w) & 1 - 2(Q_x^2 + Q_y^2)\end{bmatrix} |
Handedness in Computer Graphics Applications
To summarize Positive Direction concepts, here’s a table of applications (game engines or software tools for animations or models) that utilize different three-dimensional space orientations and specify the Up axis:
Orientation |
Left-Hand Rule |
Right-Hand Rule |
---|---|---|
Y-Up |
LightWave |
Autodesk Maya |
Z-Up |
Unreal Engine* |
Autodesk 3ds Max |
Special Case: Unreal Engine
Unreal Engine uses both hand rules depending on the axis of rotation. For example:
-
X and Y-Axis: Right-hand rule
-
Z-Axis: Left-hand rule
The Storage of Matrices in Memory as Multidimensional Arrays
We now turn to a strictly implementation-related topic.
From the matrix definition, a matrix is a two-dimensional mathematical object (it has row and column dimensions), but computer memory operates linearly as a one-dimensional array.
Representing Matrices in Computer Memory
How can we represent this matrix in computer memory?
A common solution is to use multidimensional arrays. These arrays can be mapped to computer memory in different ways, each offering distinct advantages and disadvantages.
The simplest and most widely used methods are the row-major and column-major conventions, where matrices are stored row by row or column by column, respectively.
Row-Major |
Column-Major |
|
---|---|---|
Example 3\times5 Matrix |
||
Example 4\times4 Matrix |
We have two conventions for storing matrices in memory, and each programming language must decide which one to adopt for its multidimensional array representation.
The following table summarizes how some popular programming languages implement these conventions:
Storage Order |
Programming Languages |
---|---|
Row Layout Order |
C |
Column Layout Order |
Fortran |
The majority of widely used languages favor row-major order, while those focused on numerical or scientific computations often prioritize column-major order.
The Storage of Matrices as Transformations
Data storage conventions are inevitably linked to transformations.
For example, in a translation matrix, the translation vector is stored in either the third row or the third column, depending on the multiplication order.
Pre-Multiplication |
Post-Multiplication |
---|---|
Two multiplication orders and two storage conventions yield four ways to store a translation matrix:
Pre-Multiplication |
Pre-Multiplication |
Post-Multiplication |
Post-Multiplication |
---|---|---|---|
For performance reasons, it is better to read or write memory elements that are adjacent.
As a result, the translation vector should be stored using the convention that allows for this. This may explain why applications that store matrices in row-major order often prefer the pre-multiplication convention, while those storing matrices in column-major order tend to favor the post-multiplication convention.
But what can we do if, for some reason, we need to use the post-multiplication convention, and we don’t want to store matrices in column-major order?
There is a way—though somewhat hacky—but it is possible.
We can reinterpret the multidimensional array indices in a different order.
By doing this, in C/C++, we can pretend that the matrix is stored in column-major order rather than row-major order.
Both approaches to storing multidimensional arrays are summarized in the following table:
|
|
---|---|
|
|
To access matrix elements, |
To access matrix elements, |
The rows of a matrix correspond directly to the rows in a multidimensional array |
The columns of a matrix correspond to the rows in a multidimensional array |
More intuitive for mathematicians, as it aligns closely with the matrix definition |
By doing this, we can pretend that matrices are being stored in column-major order |
|
|
The Storage of Matrices in HLSL and GLSL
HLSL and GLSL shader languages are independent from the perspective of matrix multiplication and matrix storage. Similarly, hardware and Graphics APIs, such as DirectX® and Vulkan®, are also independent of these conventions and support various possibilities. However, their default behavior is defined by older API versions.
This independence is evident in how vectors and matrices interact. When a vector is multiplied by a matrix, \vec{v_p} = \vec{v} * \mathbf{m}, the vector \vec{v} is treated as a row vector. Conversely, when the matrix is multiplied by the vector, \vec{v_p} = \mathbf{m} * \vec{v}, the vector \vec{v} is treated as a column vector.
Graphics APIs and implicit coordinate system handedness
When 3D Graphics APIs were first introduced, they adhered to specific coordinate system conventions:
-
OpenGL®: Right-hand coordinate system, and a post matrix multiplication convention.
-
DirectX®: Left-hand coordinate system, and a pre matrix multiplication convention.
With the evolution of GPU hardware and graphics APIs, these conventions have become less rigid. Today, most APIs are agnostic to the coordinate system and multiplicative order, with two notable exceptions. The API must ultimately commit to (and cannot be overridden by the user) the handedness of the two and three-dimensional Euclidean space in the following contexts:
-
Clipping Space (normalized device coordinate (NDC) space or homogeneous screen space).
API |
Clipping Space |
---|---|
DirectX® |
X-Axis points to the right, Y-Axis points up, Z-Axis points into the screen, Z in the range [0,1] |
Vulkan® |
X-Axis points to the right, Y-Axis points down, Z-Axis points into the screen, Z in the range [0,1] |
OpenGL® |
X-Axis points to the right, Y-Axis points up, Z-Axis points into the screen, Z in the range [-1,1] |
Metal |
X-Axis points to the right, Y-Axis points up, Z-Axis points into the screen, Z in the range [0,1] |
WebGPU |
X-Axis points to the right, Y-Axis points up, Z-Axis points into the screen, Z in the range [-1,1] |
WebGL™ |
X-Axis points to the right, Y-Axis points up, Z-Axis points into the screen, Z in the range [0,1] |
-
Texture space
API |
Texture space |
---|---|
DirectX® |
origin Top-Left corner, X-Axis points to the right, Y-Axis points down |
Vulkan® |
origin Top-Left corner, X-Axis points to the right, Y-Axis points down |
OpenGL® |
origin Bottom-Left corner, X-Axis points to the right, Y-Axis points up (by default) |
Metal |
origin Top-Left corner, X-Axis points to the right, Y-Axis points down |
WebGPU |
origin Top-Left corner, X-Axis points to the right, Y-Axis points down |
WebGL™ |
origin Bottom-Left corner, X-Axis points to the right, Y-Axis points up |
Transformation matrices
The above considerations—namely, the non-commutative nature of matrix multiplication and the anticommutativity of a vector’s cross product—have important consequences for the four alternative methods of constructing a 3D transformation as a 4×4 matrix. Each of these alternatives is described in detail in the sections below.
Tips on the Above Conventions:
-
Transposing the entire 4×4 matrix will only change the pre- and post-multiplication order (without altering the coordinate system’s “handedness” or the order of the basis vectors).
-
Transposing just the upper-left 3×3 matrix will only change the order of the basis vectors (without affecting the pre- and post-multiplication order).
How to Detect LH/RH Row/Column-Major Pre/Post Multiplication Conventions
The specific convention used in an application may not be immediately apparent. Answering the following questions can help identify it:
-
Translation Matrix Construction: Examine how the components of the translation vector (X, Y, Z) are stored in memory. Are they stored adjacent to each other (1a) or separated (1b)?
-
Order of Transformations: Determine the sequence in which transformations (Scale, Rotation, and Translation) are applied. Is the composition executed as T \cdot R \cdot S (2a) or S \cdot R \cdot T (2b)? Similarly, is Matrix-Vector multiplication computed as M \cdot V (2a) or V \cdot M (2b)?
-
Rotation Storage: Check how rotation around the X-Axis or Z-Axis is represented in memory. Does the first stored element correspond to -\sin (3a) or \sin (3b)? For rotation around the Y-Axis, is the first stored element \sin (3a) or -\sin (3b)?
-
Handedness of Coordinate System: Use the “hand rule” to determine the coordinate system. For the left hand, the straightened index finger points along the X-Axis, the bent middle finger along the Y-Axis, and the extended thumb along the Z-Axis (4a). For the right hand, the corresponding directions are (4b).
With answers to these questions, the conventions in use can be identified using the table below.
Question 1 |
Question 2 |
Question 3 |
Question 4 |
\rightarrow |
Convention |
|||
---|---|---|---|---|---|---|---|---|
1a |
2a |
3a |
4a |
\rightarrow |
Post-multiplication |
Column-Major |
{X,Z,Y} |
Left-handed |
1b |
2a |
3a |
4a |
\rightarrow |
Post-multiplication |
Row-Major |
{X,Y,Z} |
Left-handed |
1a |
2b |
3a |
4a |
\rightarrow |
Pre-multiplication |
Row-Major |
{X,Y,Z} |
Left-handed |
1b |
2b |
3a |
4a |
\rightarrow |
Pre-multiplication |
Column-Major |
{X,Z,Y} |
Left-handed |
1a |
2a |
3b |
4a |
\rightarrow |
Post-multiplication |
Column-Major |
{X,Y,Z} |
Left-handed |
1b |
2a |
3b |
4a |
\rightarrow |
Post-multiplication |
Row-Major |
{X,Z,Y} |
Left-handed |
1a |
2b |
3b |
4a |
\rightarrow |
Pre-multiplication |
Row-Major |
{X,Z,Y} |
Left-handed |
1b |
2b |
3b |
4a |
\rightarrow |
Pre-multiplication |
Column-Major |
{X,Y,Z} |
Left-handed |
1a |
2a |
3a |
4b |
\rightarrow |
Post-multiplication |
Column-Major |
{X,Z,Y} |
Right-handed |
1b |
2a |
3a |
4b |
\rightarrow |
Post-multiplication |
Row-Major |
{X,Y,Z} |
Right-handed |
1a |
2b |
3a |
4b |
\rightarrow |
Pre-multiplication |
Row-Major |
{X,Y,Z} |
Right-handed |
1b |
2b |
3a |
4b |
\rightarrow |
Pre-multiplication |
Column-Major |
{X,Z,Y} |
Right-handed |
1a |
2a |
3b |
4b |
\rightarrow |
Post-multiplication |
Column-Major |
{X,Y,Z} |
Right-handed |
1b |
2a |
3b |
4b |
\rightarrow |
Post-multiplication |
Row-Major |
{X,Z,Y} |
Right-handed |
1a |
2b |
3b |
4b |
\rightarrow |
Pre-multiplication |
Row-Major |
{X,Z,Y} |
Right-handed |
1b |
2b |
3b |
4b |
\rightarrow |
Pre-multiplication |
Column-Major |
{X,Y,Z} |
Right-handed |