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:

_images/mult_anim.gif

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:

  1. Matrix multiplication is generally not commutative—the order of multiplicands in multiplication matters.

  2. 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.”

    _images/Z-Up.jpg

  • 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.”

    _images/Y-Up.jpg

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.

_images/cross_ambi.gif

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

_images/CrossLH.png

_images/CrossRH.png

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

_images/Y-Up-LH.png

_images/Y-Up-RH.png

Z-Up

_images/Z-Up-LH.png

_images/Z-Up-RH.png

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

_images/PlaneDown.png

_images/PlaneUp.png

_images/OrderCW.png

_images/OrderCCW.png

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

_images/cross_LH.gif

_images/cross_RH.gif

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}}

_images/order_XYZ.gif

_images/order_XZY.gif

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

_images/OrderXY.png

_images/OrderYX.png

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

_images/rot_ccw.gif

_images/rot_cw.gif

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

image

image

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

_images/YUp_LH_XYZ.png

_images/YUp_LH_XZY.png

_images/YUp_RH_XYZ.png

_images/YUp_RH_XZY.png

Z-Up

_images/ZUp_LH_XYZ.png

_images/ZUp_LH_XZY.png

_images/ZUp_RH_XYZ.png

_images/ZUp_RH_XZY.png

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

_images/Rot_YUp_LH_XYZ.gif

_images/Rot_YUp_RH_XYZ.gif

Z-Up

_images/Rot_ZUp_LH_XYZ.gif

_images/Rot_ZUp_RH_XYZ.gif

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
ZBrush
Cinema 4D
Unity

Autodesk Maya
Modo
Houdini 3D Animation Tool
Substance Painter
Marmoset Toolbag
Godot
OGRE

Z-Up

Unreal Engine*
Open 3D Engine

Autodesk 3ds Max
Blender
SketchUp
Autodesk AutoCAD
CRYENGINE
UNIGINE

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

image

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.

_images/memory.png

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

_images/OrderRow.png

_images/OrderColumn.png

Example 4\times4 Matrix

_images/memory_row_anim.gif

_images/memory_col_anim.gif

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
C++
Python (NumPy)
Java
Rust
HLSL (by default or with row_major modifier)
GLSL (with layout(row_major) qualifier)

Column Layout Order

Fortran
Matlab
Julia
HLSL (with column_major modifier)
GLSL (by default or with layout(column_major) qualifier)

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

_images/PRE_MAT.png

_images/POST_MAT.png

Two multiplication orders and two storage conventions yield four ways to store a translation matrix:

Pre-Multiplication
Row-Major

Pre-Multiplication
Column-Major

Post-Multiplication
Row-Major

Post-Multiplication
Column-Major

_images/PRE_ROW.png

_images/PRE_COL.png

_images/POST_ROW.png

_images/POST_COL.png

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:

[Row][Column] Order

[Column][Row] Order

foo[X][Y] is interpreted as \mathbf{X \times Y} matrix

foo[X][Y] is interpreted as \mathbf{Y \times X} matrix

To access matrix elements,
specify the desired row using the first square bracket
and the desired column using the second square bracket

To access matrix elements,
specify the desired column using the first square bracket
and the desired row using the second square bracket

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

image

image

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:

  1. OpenGL®: Right-hand coordinate system, and a post matrix multiplication convention.

  2. 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:

  1. 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]

  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:

  1. 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).

  2. 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:

  1. 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)?

  2. 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)?

  3. 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)?

  4. 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