The Matrix
Matrix Dimensions:
M × N matrix framework.1# 1. A 4x1 Column Vector (4 rows, 1 column)2col_vec = [[x], [y], [z], [1.0]]34# 2. A 1x4 Row Vector (1 row, 4 columns)5row_vec = [[x, y, z, 1.0]]67# 3. A 4x4 Transformation Matrix (4 rows, 4 columns)8matrix_4x4 = [9 [1.0, 0.0, 0.0, tx],10 [0.0, 1.0, 0.0, ty],11 [0.0, 0.0, 1.0, tz],12 [0.0, 0.0, 0.0, 1.0],13]
More generally, an array of numbers can extend to m × n × t (m rows, n columns, t depth slices, known as a 3D tensor), though 3D graphics relies primarily on 4 × 4 matrices and 4 × 1 column vectors.
ToHomogeneousColumn():
w = 1.0).1class Point3d:2 def ToHomogeneousColumn(self) -> list[list[float]]:3 """Converts a 3D point into a 4x1 homogeneous column vector (w = 1.0)."""4 return [5 [self.X],6 [self.Y],7 [self.Z],8 [1.0] # w = 1.0 for Points (0.0 for Vectors)9 ]
[x, y, z, 1] looks just like defining a point or vector—the magic happens as soon as we multiply it by a 4×4 matrix.The Structure of a 4×4 Matrix
A general 3D transformation matrix contains several different operations encoded into exactly 16 numbers. It is divided into logical blocks that each serve a distinct purpose:
| Section | Purpose |
|---|---|
| Upper-Left 3×3 | Rotation and Scaling |
| Right Column | Translation (Position) |
| Bottom Row | Homogeneous Coordinate Mechanics |
Matrix4x4 Structure:
[0, 0, 0]. Adding a 4th homogeneous row and column unlocks translation inside a unified 4×4 structure.1class Matrix4x4:2 def __init__(self):3 # 4x4 Homogeneous Identity Matrix4 self.M = [5 [1.0, 0.0, 0.0, 0.0],6 [0.0, 1.0, 0.0, 0.0],7 [0.0, 0.0, 1.0, 0.0],8 [0.0, 0.0, 0.0, 1.0],9 ]
[0, 0, 0]. Switching to 4×4 mode allows translation to occur because the homogeneous coordinate w = 1 activates the 4th translation column!Identity:
1 in ordinary arithmetic.1# The Identity Matrix (I):2# Columns 1, 2, 3 are the X, Y, Z basis vectors; Column 4 is translation3# Col 1(X) Col 2(Y) Col 3(Z) Col 4(T)4I = [5 [ 1.0, 0.0, 0.0, 0.0 ],6 [ 0.0, 1.0, 0.0, 0.0 ],7 [ 0.0, 0.0, 1.0, 0.0 ],8 [ 0.0, 0.0, 0.0, 1.0 ]9]
Translation(Vector3d):
t_x, t_y, and t_z.1# Translation Matrix:2T = [3 [1.0, 0.0, 0.0, tx ],4 [0.0, 1.0, 0.0, ty ],5 [0.0, 0.0, 1.0, tz ],6 [0.0, 0.0, 0.0, 1.0]7]8# Applied to v = [x, y, z, 1]^T:9# v_new = [x + tx, y + ty, z + tz, 1]^T
w = 1 ensures the translation factor is added. If applied to a pure directional vector (where w = 0), the translation is ignored because directions are position-independent!Scale(Vector3d):
s_x, s_y, and s_z.1# Scaling Matrix:2S = [3 [sx , 0.0, 0.0, 0.0],4 [0.0, sy , 0.0, 0.0],5 [0.0, 0.0, sz , 0.0],6 [0.0, 0.0, 0.0, 1.0]7]8# Applied to v = [x, y, z, 1]^T:9# v_new = [sx*x, sy*y, sz*z, 1]^T
•
s > 1: stretches the object along that axis.•
0 < s < 1: squashes the object.•
s < 0: mirrors/flips the object across that plane! Try dragging any slider below zero.Determinant & Mirroring:
1def GetDeterminant(self) -> float:2 """Calculates the determinant of the 4x4 matrix using Laplace expansion."""3 m00, m01, m02, m03 = self.m00, self.m01, self.m02, self.m034 m10, m11, m12, m13 = self.m10, self.m11, self.m12, self.m135 m20, m21, m22, m23 = self.m20, self.m21, self.m22, self.m236 m30, m31, m32, m33 = self.m30, self.m31, self.m32, self.m3378 d2_01_01 = m20 * m31 - m21 * m309 d2_01_02 = m20 * m32 - m22 * m3010 d2_01_03 = m20 * m33 - m23 * m3011 d2_01_12 = m21 * m32 - m22 * m3112 d2_01_13 = m21 * m33 - m23 * m3113 d2_01_23 = m22 * m33 - m23 * m321415 minor0 = m11 * d2_01_23 - m12 * d2_01_13 + m13 * d2_01_1216 minor1 = m10 * d2_01_23 - m12 * d2_01_03 + m13 * d2_01_0217 minor2 = m10 * d2_01_13 - m11 * d2_01_03 + m13 * d2_01_0118 minor3 = m10 * d2_01_12 - m11 * d2_01_02 + m12 * d2_01_011920 return m00 * minor0 - m01 * minor1 + m02 * minor2 - m03 * minor3
Mirroring geometry is just scaling by -1 across an axis. However, this flips the matrix determinant to negative, which means mesh faces will render inside-out (normals are inverted).
Rotation(Vector3d, float):
1import math23def RotationX(a: float) -> list[list[float]]:4 c, s = math.cos(a), math.sin(a)5 return [6 [1.0, 0.0, 0.0, 0.0],7 [0.0, c, -s, 0.0],8 [0.0, s, c, 0.0],9 [0.0, 0.0, 0.0, 1.0],10 ]1112def RotationY(a: float) -> list[list[float]]:13 c, s = math.cos(a), math.sin(a)14 return [15 [ c, 0.0, s, 0.0],16 [0.0, 1.0, 0.0, 0.0],17 [ -s, 0.0, c, 0.0],18 [0.0, 0.0, 0.0, 1.0],19 ]2021def RotationZ(a: float) -> list[list[float]]:22 c, s = math.cos(a), math.sin(a)23 return [24 [ c, -s, 0.0, 0.0],25 [ s, c, 0.0, 0.0],26 [0.0, 0.0, 1.0, 0.0],27 [0.0, 0.0, 0.0, 1.0],28 ]
| Axis | Behavior |
|---|---|
| X-Axis | X coordinate remains fixed; Y and Z rotate. |
| Y-Axis | Y coordinate remains fixed; X and Z rotate. |
| Z-Axis | Z coordinate remains fixed; X and Y rotate. |
LookAt(Eye, Target, Up):
1def LookAt(eye: 'Point3d', target: 'Point3d', up: 'Vector3d') -> 'Matrix4x4':2 # 1. Forward Vector (Z)3 forward = (target - eye).Unitize()45 # 2. Right Vector (X) = Cross Product of Up and Forward6 right = Vector3d.CrossProduct(up, forward).Unitize()78 # 3. True Up Vector (Y) = Cross Product of Forward and Right9 true_up = Vector3d.CrossProduct(forward, right)1011 # Build rotation matrix from these 3 basis vectors12 return Matrix4x4([13 right.X, true_up.X, forward.X, eye.X,14 right.Y, true_up.Y, forward.Y, eye.Y,15 right.Z, true_up.Z, forward.Z, eye.Z,16 0, 0, 0, 117 ])
Multiply(Matrix4x4, Matrix4x4):
1def Multiply(A: list[list[float]], B: list[list[float]]) -> list[list[float]]:2 """Multiplies two 4x4 matrices (Row i of A dot Column j of B)."""3 return [4 [sum(A[i][k] * B[k][j] for k in range(4)) for j in range(4)]5 for i in range(4)6 ]78# Composition order goes RIGHT to LEFT: M = Multiply(T, Multiply(R, S))
AB ≠ BA). The order of operations matters:• Rotate then Translate (0): Spins the box in place, then pushes it out. The box stays close.
• Translate then Rotate (1): Pushes the box out, then rotates around the origin, causing it to orbit along a large arc!
Decompose(Matrix4x4):
1def Decompose(self) -> tuple['Vector3d', 'Quaternion', 'Vector3d']:2 """Extracts Translation, Rotation, and Scale from a 4x4 matrix."""34 # 1. Translation is easily extracted from the 4th column5 translation = Vector3d(self.m03, self.m13, self.m23)67 # 2. Scale is the magnitude of the 3 basis vectors (columns)8 scaleX = Vector3d(self.m00, self.m10, self.m20).Length9 scaleY = Vector3d(self.m01, self.m11, self.m21).Length10 scaleZ = Vector3d(self.m02, self.m12, self.m22).Length11 scale = Vector3d(scaleX, scaleY, scaleZ)1213 # 3. Rotation is what's left after removing scale from the 3x314 # Usually returned as a Quaternion or Euler angles15 rotation = ExtractRotation(self)1617 return translation, rotation, scale
Invert(Matrix4x4):
M⁻¹ reverses every operation of M such that M × M⁻¹ = I (the Identity Matrix). If the Model matrix transforms a vertex from Local Space → World Space, its inverse Model⁻¹ maps coordinates back from World Space → Local Space:- Local Space: Object's own origin (model-relative, recovered via
Model⁻¹). - World Space: Scene-wide origin (
v_world = Model × v_local). - View Space: Relative to camera lens (
View = CameraWorld⁻¹). - Screen Space: Flattened 2D screen coordinates.
1def InvertRigid(M: list[list[float]]) -> list[list[float]]:2 """Inverts a rigid 4x4 affine matrix (R^T and -R^T * T)."""3 # Transpose the upper-left 3x3 orthonormal rotation block (R^T)4 rt = [[M[j][i] for j in range(3)] for i in range(3)]5 tx, ty, tz = M[0][3], M[1][3], M[2][3]67 # Inverse translation is -R^T * T8 itx = -(rt[0][0] * tx + rt[0][1] * ty + rt[0][2] * tz)9 ity = -(rt[1][0] * tx + rt[1][1] * ty + rt[1][2] * tz)10 itz = -(rt[2][0] * tx + rt[2][1] * ty + rt[2][2] * tz)1112 return [13 [rt[0][0], rt[0][1], rt[0][2], itx],14 [rt[1][0], rt[1][1], rt[1][2], ity],15 [rt[2][0], rt[2][1], rt[2][2], itz],16 [0.0, 0.0, 0.0, 1.0],17 ]
Space Transformation (Instancing):
1def BuildInstancedTransforms(grid_pts: list['Point3d'], space_matrix: list[list[float]]) -> list['Point3d']:2 """Applies a single 4x4 space transformation matrix across an entire instanced grid."""3 return [pt.Transform(space_matrix) for pt in grid_pts]