Skip to content

Latest commit

 

History

History
602 lines (440 loc) · 12.3 KB

File metadata and controls

602 lines (440 loc) · 12.3 KB

Appendix C: Math Reference

Quick-reference cheat sheet for the math used throughout this guide. Keep this open while coding.


1. Vector Operations

Basics

Vector:           v = (x, y, z)
Addition:         a + b = (a.x + b.x, a.y + b.y, a.z + b.z)
Subtraction:      a - b = (a.x - b.x, a.y - b.y, a.z - b.z)
Scalar multiply:  s * v = (s*v.x, s*v.y, s*v.z)
Negation:         -v = (-v.x, -v.y, -v.z)

Magnitude (Length)

|v| = sqrt(v.x^2 + v.y^2 + v.z^2)

// 2D
|v| = sqrt(v.x^2 + v.y^2)

Normalize (Unit Vector)

v_hat = v / |v|

// Result has magnitude 1. Direction is preserved.
// WARNING: check |v| > 0 before dividing!

Dot Product

a . b = a.x*b.x + a.y*b.y + a.z*b.z

// Geometric form:
a . b = |a| * |b| * cos(theta)

// Properties:
a . b = b . a                    (commutative)
a . b = 0  when a perp to b     (orthogonality test)
a . a = |a|^2                    (length squared)

Common uses:

  • Lighting: diffuse = max(0, N . L) where N is surface normal, L is light direction
  • Projection: length of a along b = (a . b) / |b|
  • Angle between vectors: cos(theta) = (a . b) / (|a| * |b|)
  • Front/back test: N . V > 0 means surface faces camera

Cross Product (3D only)

a x b = (a.y*b.z - a.z*b.y,
         a.z*b.x - a.x*b.z,
         a.x*b.y - a.y*b.x)

// Geometric form:
|a x b| = |a| * |b| * sin(theta)

// Properties:
a x b = -(b x a)                (anti-commutative!)
a x b is perpendicular to both a and b
|a x b| = area of parallelogram formed by a and b

Common uses:

  • Surface normal: N = normalize((v1 - v0) x (v2 - v0))
  • Winding order: sign of cross product z-component (2D)
  • Tangent frame construction

Distance

dist(a, b) = |b - a| = sqrt((b.x-a.x)^2 + (b.y-a.y)^2 + (b.z-a.z)^2)

// Often compare squared distances to avoid sqrt:
distSq(a, b) = (b.x-a.x)^2 + (b.y-a.y)^2 + (b.z-a.z)^2

Reflection

// Reflect vector I around normal N (N must be unit length)
R = I - 2 * (I . N) * N

// For light reflection: if L points TOWARD the surface,
// the reflected direction is:
R = 2 * (N . L) * N - L

Refraction (Snell's Law)

n1 * sin(theta1) = n2 * sin(theta2)

// Refracted direction (I and N are unit vectors, eta = n1/n2):
k = 1 - eta^2 * (1 - (N . I)^2)
if k < 0: total internal reflection (no refraction)
T = eta * I - (eta * (N . I) + sqrt(k)) * N

2. Linear Interpolation

lerp(a, b, t) = a + t * (b - a) = (1 - t) * a + t * b

// t = 0 → a
// t = 1 → b
// t = 0.5 → midpoint

Smoothstep

smoothstep(edge0, edge1, x):
    t = clamp((x - edge0) / (edge1 - edge0), 0, 1)
    return t * t * (3 - 2 * t)

// S-shaped curve: smooth transition from 0 to 1
// Derivative is 0 at both endpoints

3. Transformation Matrices

All matrices are 4x4 for homogeneous coordinates. Points use w=1, directions use w=0.

Translation

T(tx, ty, tz) = | 1  0  0  tx |
                | 0  1  0  ty |
                | 0  0  1  tz |
                | 0  0  0  1  |

Scale

S(sx, sy, sz) = | sx 0  0  0 |
                | 0  sy 0  0 |
                | 0  0  sz 0 |
                | 0  0  0  1 |

Rotation (2D, around origin)

R(theta) = | cos(theta)  -sin(theta) |
           | sin(theta)   cos(theta) |

Rotation Around X Axis

Rx(theta) = | 1    0          0       0 |
            | 0    cos(theta) -sin(theta) 0 |
            | 0    sin(theta)  cos(theta) 0 |
            | 0    0          0       1 |

Rotation Around Y Axis

Ry(theta) = |  cos(theta) 0  sin(theta) 0 |
            |  0          1  0          0 |
            | -sin(theta) 0  cos(theta) 0 |
            |  0          0  0          1 |

Rotation Around Z Axis

Rz(theta) = | cos(theta) -sin(theta) 0  0 |
            | sin(theta)  cos(theta) 0  0 |
            | 0           0          1  0 |
            | 0           0          0  1 |

Rotation Around Arbitrary Axis (Rodrigues)

// Rotate by angle theta around unit axis k = (kx, ky, kz):
R = cos(theta) * I + (1 - cos(theta)) * (k * k^T) + sin(theta) * K

where K = |  0  -kz  ky |     (skew-symmetric matrix of k)
          |  kz  0  -kx |
          | -ky  kx  0  |

Look-At Matrix (View Transform)

// Camera at position eye, looking at target, with up vector
forward = normalize(target - eye)
right   = normalize(forward x up)
newUp   = right x forward

View = | right.x    right.y    right.z    -(right . eye)   |
       | newUp.x    newUp.y    newUp.z    -(newUp . eye)   |
       | -forward.x -forward.y -forward.z  (forward . eye) |
       | 0          0          0           1                |

4. Projection Matrices

Perspective Projection (OpenGL Convention)

// fov = vertical field of view in radians
// aspect = width / height
// n = near plane, f = far plane

f = 1 / tan(fov / 2)

P = | f/aspect  0       0              0             |
    | 0         f       0              0             |
    | 0         0   (f+n)/(n-f)    2*f*n/(n-f)      |
    | 0         0      -1              0             |

// After multiplying by P and dividing by w:
// x_ndc in [-1, 1]
// y_ndc in [-1, 1]
// z_ndc in [-1, 1]

Orthographic Projection

// l, r = left, right
// b, t = bottom, top
// n, f = near, far

P = | 2/(r-l)    0        0       -(r+l)/(r-l) |
    | 0        2/(t-b)    0       -(t+b)/(t-b) |
    | 0          0      -2/(f-n)  -(f+n)/(f-n) |
    | 0          0        0        1            |

The Full Transform Chain (MVP)

clip_pos = Projection * View * Model * local_pos

// Then perspective divide:
ndc = clip_pos.xyz / clip_pos.w

// Then viewport transform:
screen_x = (ndc.x + 1) / 2 * width
screen_y = (ndc.y + 1) / 2 * height

5. Barycentric Coordinates

For a point P inside triangle (A, B, C):

P = u*A + v*B + w*C
where u + v + w = 1 and u,v,w >= 0

// Compute from areas:
u = area(P, B, C) / area(A, B, C)
v = area(A, P, C) / area(A, B, C)
w = 1 - u - v

// Using cross products (2D version):
area(A, B, C) = 0.5 * |(B-A) x (C-A)|

// Edge function method (efficient for rasterization):
edge01(P) = (P.x - v0.x) * (v1.y - v0.y) - (P.y - v0.y) * (v1.x - v0.x)

Uses: interpolating vertex attributes (color, normals, UVs, depth) across a triangle during rasterization.


6. Ray-Geometry Intersections

Ray Definition

P(t) = O + t * D    where t >= 0
O = ray origin
D = ray direction (usually normalized)

Ray-Sphere Intersection

Sphere: center C, radius r
Substituting into |P - C|^2 = r^2:

a = D . D               (= 1 if D is normalized)
b = 2 * D . (O - C)
c = (O - C) . (O - C) - r^2

discriminant = b^2 - 4*a*c

if discriminant < 0: no intersection
if discriminant = 0: one intersection (tangent)
if discriminant > 0: two intersections

t = (-b +/- sqrt(discriminant)) / (2*a)
Use the smaller positive t for the nearest hit.
Hit point: P = O + t * D
Normal at hit: N = normalize(P - C)

Ray-Plane Intersection

Plane: N . P + d = 0  (or equivalently, N . P = d)
N = plane normal, d = distance from origin

t = -(N . O + d) / (N . D)

if N . D = 0: ray is parallel to plane (no intersection)
if t < 0: intersection is behind the ray

Ray-Triangle Intersection (Moller-Trumbore)

Triangle vertices: v0, v1, v2
Edge vectors: e1 = v1 - v0, e2 = v2 - v0

h = D x e2
a = e1 . h
if |a| < epsilon: ray parallel to triangle

f = 1 / a
s = O - v0
u = f * (s . h)
if u < 0 or u > 1: miss

q = s x e1
v = f * (D . q)
if v < 0 or u + v > 1: miss

t = f * (e2 . q)
if t < 0: miss (behind ray)

// Hit! Barycentric coordinates are (1-u-v, u, v)

Ray-AABB Intersection (Slab Method)

For each axis i (x, y, z):
    t_near_i = (box.min[i] - O[i]) / D[i]
    t_far_i  = (box.max[i] - O[i]) / D[i]
    if D[i] < 0: swap(t_near_i, t_far_i)

t_enter = max(t_near_x, t_near_y, t_near_z)
t_exit  = min(t_far_x,  t_far_y,  t_far_z)

if t_exit < t_enter: no intersection
if t_exit < 0: box is behind ray

7. GLSL Built-in Functions

Quick reference for the functions you'll use most in shaders.

Math

abs(x)              // absolute value
sign(x)             // -1, 0, or 1
floor(x)            // round down
ceil(x)             // round up
fract(x)            // x - floor(x), fractional part
mod(x, y)           // x - y * floor(x/y)
min(a, b)           // minimum
max(a, b)           // maximum
clamp(x, lo, hi)    // min(max(x, lo), hi)
mix(a, b, t)        // a*(1-t) + b*t  (same as lerp)
step(edge, x)       // 0 if x < edge, else 1
smoothstep(e0,e1,x) // smooth Hermite interpolation
pow(x, y)           // x^y
exp(x)              // e^x
log(x)              // ln(x)
sqrt(x)             // square root
inversesqrt(x)      // 1 / sqrt(x)

Vector

length(v)           // magnitude: sqrt(v.x^2 + v.y^2 + v.z^2)
distance(a, b)      // length(b - a)
dot(a, b)           // dot product
cross(a, b)         // cross product (vec3 only)
normalize(v)        // v / length(v)
reflect(I, N)       // I - 2*dot(N,I)*N
refract(I, N, eta)  // Snell's law refraction
faceforward(N,I,Nref) // N if dot(Nref,I)<0, else -N

Trigonometric

sin(x), cos(x), tan(x)
asin(x), acos(x), atan(y, x)
radians(degrees)    // degrees to radians
degrees(radians)    // radians to degrees

Texture

texture(sampler, uv)          // sample 2D texture
textureLod(sampler, uv, lod)  // sample at specific mip level
textureGrad(sampler, uv, ddx, ddy)  // sample with explicit gradients
texelFetch(sampler, ivec2, lod)      // fetch exact texel (no filtering)
textureSize(sampler, lod)     // get texture dimensions

8. Trigonometric Identities

Fundamental

sin^2(x) + cos^2(x) = 1
1 + tan^2(x) = sec^2(x)

Double Angle

sin(2x) = 2 * sin(x) * cos(x)
cos(2x) = cos^2(x) - sin^2(x)
        = 2*cos^2(x) - 1
        = 1 - 2*sin^2(x)

Half Angle

sin^2(x/2) = (1 - cos(x)) / 2
cos^2(x/2) = (1 + cos(x)) / 2

Sum/Difference

sin(a +/- b) = sin(a)*cos(b) +/- cos(a)*sin(b)
cos(a +/- b) = cos(a)*cos(b) -/+ sin(a)*sin(b)

Small Angle Approximations (for |x| << 1)

sin(x) ≈ x
cos(x) ≈ 1 - x^2/2
tan(x) ≈ x

Taylor Series

sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ...
cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + ...
e^x    = 1 + x + x^2/2! + x^3/3! + ...

9. Color Conversions

Linear to sRGB

if linear <= 0.0031308:
    sRGB = 12.92 * linear
else:
    sRGB = 1.055 * linear^(1/2.4) - 0.055

// Quick approximation: sRGB ≈ linear^(1/2.2)

sRGB to Linear

if sRGB <= 0.04045:
    linear = sRGB / 12.92
else:
    linear = ((sRGB + 0.055) / 1.055)^2.4

// Quick approximation: linear ≈ sRGB^2.2

HSV to RGB (for procedural color)

H in [0, 360), S in [0, 1], V in [0, 1]
C = V * S
X = C * (1 - |((H/60) mod 2) - 1|)
m = V - C

(R', G', B') based on H sector:
  0-60:   (C, X, 0)
  60-120:  (X, C, 0)
  120-180: (0, C, X)
  180-240: (0, X, C)
  240-300: (X, 0, C)
  300-360: (C, 0, X)

(R, G, B) = (R'+m, G'+m, B'+m)

10. Useful Constants

pi        = 3.14159265358979...
2*pi      = 6.28318530717959...   (full circle in radians)
pi/2      = 1.57079632679490...   (90 degrees)
pi/4      = 0.78539816339745...   (45 degrees)
1/pi      = 0.31830988618379...
1/(2*pi)  = 0.15915494309190...
sqrt(2)   = 1.41421356237310...
1/sqrt(2) = 0.70710678118655...
e         = 2.71828182845905...
golden    = 1.61803398874989...   (golden ratio)

// Degrees ↔ Radians
degrees = radians * 180 / pi
radians = degrees * pi / 180

11. Common PBR Formulas

Schlick Fresnel Approximation

F(theta) = F0 + (1 - F0) * (1 - cos(theta))^5

// F0 = reflectance at normal incidence
// Common F0 values:
//   Water: 0.02
//   Glass: 0.04
//   Plastic: 0.04
//   Diamond: 0.17
//   Iron: (0.56, 0.57, 0.58)
//   Gold: (1.0, 0.71, 0.29)
//   Silver: (0.95, 0.93, 0.88)

GGX/Trowbridge-Reitz Normal Distribution

D(h) = alpha^2 / (pi * ((N.H)^2 * (alpha^2 - 1) + 1)^2)

// alpha = roughness^2
// N = surface normal
// H = half vector = normalize(L + V)

Smith-GGX Geometry Function

G(N,V,L) = G1(N,V) * G1(N,L)

G1(N,X) = 2*(N.X) / ((N.X) + sqrt(alpha^2 + (1-alpha^2)*(N.X)^2))

Cook-Torrance Specular BRDF

f_spec = D * F * G / (4 * (N.L) * (N.V))

Full PBR Shading

f = (1 - F) * (1 - metallic) * albedo/pi    // diffuse (Lambert)
  + D * F * G / (4 * (N.L) * (N.V))         // specular (Cook-Torrance)

L_out = f * L_in * max(0, N.L)