Linear_Algebra_3D_Engine_Curriculum
Introduction
Learning linear algebra from a textbook can feel dry and abstract. By building a 3D graphics engine from scratch using Python and Pygame, you transform abstract matrices and vectors into tangible, visual objects. This curriculum is designed to drip-feed linear algebra concepts exactly when you need them to make your 3D engine work.
The Curriculum
Part 1: Hello, Vector (The Setup)
The Goal: Get a window open and understand how to represent 3D space in code.
- The Math: What is a vector? Coordinate systems (Left-handed vs. Right-handed). Using column vectors.
- The Code: Write the basic Pygame loop. Define the 8 vertices of a simple 3D cube as an array of vectors using
numpy.
Part 2: Flatland (Orthographic Projection)
The Goal: Draw your 3D cube on a 2D screen.
- The Math: Matrices and the Identity Matrix. Orthographic Projection (dropping the Z-axis).
- The Code: Write a function to multiply vertex vectors by the projection matrix and draw lines connecting the 2D coordinates.
Part 3: The Spin (Rotation Matrices)
The Goal: Make the cube spin so it actually looks 3D.
- The Math: Sine and Cosine (unit circle). Rotation Matrices for X, Y, and Z axes. Matrix multiplication order.
- The Code: Apply a rotation matrix to your vertices before projecting them. Increment the angle every frame.
Part 4: The 4th Dimension Hack (Homogeneous Coordinates)
The Goal: Move (translate) and scale the cube.
- The Math: The limitation of matrices (no translation). Homogeneous Coordinates (adding ). Upgrading to matrices to combine Translation, Rotation, and Scaling (TRS).
- The Code: Refactor vectors to 4D. Build a translation matrix to move the spinning cube.
Part 5: True Perspective (The Illusion of Depth)
The Goal: Make objects further away look smaller.
- The Math: The View Frustum. The Perspective Divide (dividing and by ). The Perspective Projection Matrix (FOV, Aspect Ratio, Near/Far planes).
- The Code: Replace the flat orthographic matrix with the perspective matrix.
Part 6: The Camera (The Final Boss)
The Goal: Move a “camera” around a stationary scene.
- The Math: Vector Operations (Dot products, Cross products). The “LookAt” Matrix (building a dynamic coordinate system).
- The Code: Implement WASD keyboard controls in Pygame to navigate your camera vector through the 3D space.
Recommended Source Material
1. Intuition & Visualization (The “Must Watch”)
- 3Blue1Brown: “Essence of Linear Algebra” (YouTube): Before you read a single textbook page, watch this series. It focuses entirely on the geometric intuition behind vectors, matrices, determinants, and transformations. It is the absolute perfect companion to this project.
2. Graphics Math & Implementation
- “3D Math Primer for Graphics and Game Development” by Fletcher Dunn & Ian Parberry: This is arguably the best book for this specific project. It bridges the gap between pure math and code, explaining exactly how vectors and matrices apply to 3D engines without getting bogged down in too much pure theory.
- Scratchapixel 2.0 (Online Tutorial): An incredible, free online resource that teaches computer graphics from the ground up. Their lessons on the Perspective Projection Matrix and general matrix operations are top-tier.
- TinyRenderer by Dmitry V. Sokolov (GitHub/Wiki): A famous tutorial series that walks through building a 3D renderer from scratch without OpenGL. While the tutorial uses C++, the underlying math concepts and the step-by-step progression are extremely similar to what you will be doing in Python.
3. Pure Math Textbooks (For deeper dives)
- “Linear Algebra and Its Applications” by Gilbert Strang: The gold standard for university-level linear algebra. It’s dense, but excellent if you want to understand the rigorous proofs behind the math you are using. Strang’s MIT OpenCourseWare video lectures are also available for free online.
- “Mathematics for 3D Game Programming and Computer Graphics” by Eric Lengyel: A solid alternative to Fletcher Dunn’s book, offering a slightly more rigorous mathematical approach while staying focused on graphics programming.
import pygame
import numpy as np
import math
SCALE = 100 # define some scale factor for our cube so it's not microscopic
WIDTH = 800
HEIGHT = 600
# The 8 corners of a cube
# We use column vectors (3 rows, 1 column) because that's the standard for linear algebra transformations.
vertices = [
np.array([[-1], [-1], [-1]]), # 0: Bottom-left-front
np.array([[ 1], [-1], [-1]]), # 1: Bottom-right-front
np.array([[ 1], [ 1], [-1]]), # 2: Top-right-front
np.array([[-1], [ 1], [-1]]), # 3: Top-left-front
np.array([[-1], [ -1], [1]]), # 4: Bottom-left-back
np.array([[ 1], [-1], [1]]), # 5: Bottom-right-back
np.array([[ 1], [ 1], [1]]), # 6: Top-right-back
np.array([[-1], [ 1], [1]]) # 7: Top-left-back
]
# Define the connections between vertices (Index pairs)
edges = [
# Front Face
(0, 1), (1, 2), (2, 3), (3, 0),
# Back Face
(4, 5), (5, 6), (6, 7), (7, 4),
# Connecting the faces (Front to Back)
(0, 4), (1, 5), (2, 6), (3, 7)
]
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("matricies are cool")
clock = pygame.time.Clock()
angle_z = 0
angle_x = 0
angle_y = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((20, 20, 30)) # fill bg with drak gray
angle_z += 0.01
angle_x += 0.02
angle_y += 0.015
# The Z-Rotation Matrix
rotation_z = np.array([
[math.cos(angle_z), -math.sin(angle_z), 0],
[math.sin(angle_z), math.cos(angle_z), 0],
[0, 0, 1]
])
rotation_x = np.array([
[1, 0, 0],
[0, math.cos(angle_x), -math.sin(angle_x)],
[0, math.sin(angle_x), math.cos(angle_x)]
])
rotation_y = np.array([
[ math.cos(angle_y), 0, math.sin(angle_y)],
[ 0, 1, 0],
[-math.sin(angle_y), 0, math.cos(angle_y)]
])
projected_points = [] # buffer for the coordinates of the cube during a given frame
# --- Do a little math ----
for v in vertices:
# multiply rotation matrix by original vector
# First rotate around Y, then X, then Z
rotated_v = np.dot(rotation_y, v)
rotated_v = np.dot(rotation_x, rotated_v)
rotated_v = np.dot(rotation_z, rotated_v)
# implement Great Value Orthographic projection (Dropping the Z axis)
x = rotated_v[0][0]
y = rotated_v[1][0]
# apply scale factor
x *= SCALE
y *= SCALE
# translate to center of screen, by cheating, instead of using matrix operations
x += WIDTH / 2
y += HEIGHT / 2
projected_points.append([int(x), int(y)])
# draw the point on the canvas
pygame.draw.circle(screen, (255, 255, 255), (int(x), int(y)), 5)
# --- Draw a little cube ---
for edge in edges:
# using our stored coords from the math phase
point_a = projected_points[edge[0]]
point_b = projected_points[edge[1]]
# draw the line
pygame.draw.line(screen, (0, 255, 150), point_a, point_b, 2)
pygame.display.flip()
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
main()