🎨 3D Transform Fundamentals
🎯 What are 3D Transforms?
3D transforms are operations that change the position, orientation, or scale of objects in three-dimensional space. They are fundamental to computer graphics, robotics, and computer vision.
Key concept: Transforms are represented using linear algebra, typically with 4×4 transformation matrices in homogeneous coordinates.
Deep Explanation
3D transforms are the foundation of computer graphics. They allow us to position objects in a scene, animate them, and project them onto a 2D screen using translation, rotation, scaling, and projection.
Why it matters: Understanding 3D transforms is essential for game development, 3D modeling, AR/VR, and computer vision.
# 3D transform basics
import numpy as np
# Define a point in 3D
point = np.array([1.0, 2.0, 3.0])
# Translation matrix (move by (2, 3, 4))
def translation_matrix(tx, ty, tz):
return np.array([
[1, 0, 0, tx],
[0, 1, 0, ty],
[0, 0, 1, tz],
[0, 0, 0, 1]
])
# Apply transform
point_h = np.array([1.0, 2.0, 3.0, 1.0]) # Homogeneous
T = translation_matrix(2, 3, 4)
transformed = T @ point_h
print(transformed[:3]) # [3.0, 5.0, 7.0]📐 Vectors & Points
🎯 3D Geometry
3D transforms operate on points (positions) and vectors (directions). Points and vectors behave differently under transforms.
Geometric Primitives
Points: Represent positions in space (P = [x, y, z]). Vectors: Represent directions and magnitudes (V = [dx, dy, dz]). Difference: Points are position-dependent; vectors are position-independent.
Operations: Vector addition, dot product, cross product, scalar multiplication.
# Vector operations
class Vector3D:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def dot(self, other):
return self.x * other.x + self.y * other.y + self.z * other.z
def cross(self, other):
return Vector3D(
self.y * other.z - self.z * other.y,
self.z * other.x - self.x * other.z,
self.x * other.y - self.y * other.x
)
def magnitude(self):
return math.sqrt(self.x**2 + self.y**2 + self.z**2)
def normalize(self):
mag = self.magnitude()
if mag == 0:
return Vector3D(0, 0, 0)
return Vector3D(self.x/mag, self.y/mag, self.z/mag)
def __add__(self, other):
return Vector3D(self.x + other.x, self.y + other.y, self.z + other.z)
def __mul__(self, scalar):
return Vector3D(self.x * scalar, self.y * scalar, self.z * scalar)
# Usage
v1 = Vector3D(1, 0, 0)
v2 = Vector3D(0, 1, 0)
v3 = v1.cross(v2) # (0, 0, 1)📊 Transformation Matrices
🎯 Matrix Representation
3D transforms are represented by 4×4 matrices in homogeneous coordinates. Matrix multiplication composes transforms.
Matrix Properties
Affine transforms: Preserve lines and parallelism (translation, rotation, scaling, shear). Matrix multiplication: Non-commutative (order matters). Inverse: Undoes a transform. Identity: Does nothing.
Composition: M_total = M_n * ... * M_2 * M_1 (applied right to left).
# Matrix operations
def identity_4x4():
return np.eye(4)
def matrix_multiply(A, B):
return A @ B
def compose_transforms(transforms):
# Compose multiple transforms (apply in order)
result = identity_4x4()
for transform in transforms:
result = transform @ result
return result
# Example: rotate then translate
def transform_point(point, matrix):
point_h = np.array([point[0], point[1], point[2], 1.0])
result = matrix @ point_h
return result[:3]🔄 Translation
🎯 Moving Objects
Translation moves an object by a vector (tx, ty, tz). It's the simplest 3D transform.
Translation Details
Matrix: [[1, 0, 0, tx], [0, 1, 0, ty], [0, 0, 1, tz], [0, 0, 0, 1]]. Inverse: Translate by (-tx, -ty, -tz). Properties: Commutative with scaling? No. With other translations? Yes.
Application: Positioning objects in a scene.
# Translation
def translation(tx, ty, tz):
return np.array([
[1, 0, 0, tx],
[0, 1, 0, ty],
[0, 0, 1, tz],
[0, 0, 0, 1]
])
# Translation inverse
def translation_inverse(T):
return translation(-T[0,3], -T[1,3], -T[2,3])
# Apply translation to multiple points
def translate_points(points, tx, ty, tz):
T = translation(tx, ty, tz)
transformed = []
for p in points:
p_h = np.array([p[0], p[1], p[2], 1.0])
result = T @ p_h
transformed.append(result[:3])
return transformed🔄 Rotation
🎯 Rotating Objects
Rotation rotates an object around an axis by an angle. It is defined by axis and angle.
Rotation Details
Axis-aligned: Rotation around X, Y, or Z axis. Arbitrary axis: Rotation around any unit vector. Matrix: Uses sine and cosine. Properties: Orthogonal (R^T = R^{-1}), determinant = 1.
Applications: Orienting objects, camera movement.
# Rotation matrices
def rotation_x(angle):
c = np.cos(angle)
s = np.sin(angle)
return np.array([
[1, 0, 0, 0],
[0, c, -s, 0],
[0, s, c, 0],
[0, 0, 0, 1]
])
def rotation_y(angle):
c = np.cos(angle)
s = np.sin(angle)
return np.array([
[c, 0, s, 0],
[0, 1, 0, 0],
[-s, 0, c, 0],
[0, 0, 0, 1]
])
def rotation_z(angle):
c = np.cos(angle)
s = np.sin(angle)
return np.array([
[c, -s, 0, 0],
[s, c, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
])
# Rotation around arbitrary axis (Rodrigues formula)
def rotation_axis(axis, angle):
axis = axis / np.linalg.norm(axis)
x, y, z = axis
c = np.cos(angle)
s = np.sin(angle)
t = 1 - c
return np.array([
[t*x*x + c, t*x*y - z*s, t*x*z + y*s, 0],
[t*x*y + z*s, t*y*y + c, t*y*z - x*s, 0],
[t*x*z - y*s, t*y*z + x*s, t*z*z + c, 0],
[0, 0, 0, 1]
])📏 Scaling
🎯 Changing Size
Scaling changes the size of an object. Uniform scaling preserves proportions; non-uniform scaling stretches or compresses.
Scaling Details
Uniform: sx = sy = sz. Non-uniform: Different factors per axis. Matrix: [[sx, 0, 0, 0], [0, sy, 0, 0], [0, 0, sz, 0], [0, 0, 0, 1]]. Inverse: Scale by 1/sx, 1/sy, 1/sz.
Properties: Does not commute with translation (scaling around origin).
# Scaling matrix
def scale(sx, sy, sz):
return np.array([
[sx, 0, 0, 0],
[0, sy, 0, 0],
[0, 0, sz, 0],
[0, 0, 0, 1]
])
# Scale around a point (not origin)
def scale_around_point(sx, sy, sz, center):
# Translate to origin, scale, translate back
T1 = translation(-center[0], -center[1], -center[2])
S = scale(sx, sy, sz)
T2 = translation(center[0], center[1], center[2])
return compose_transforms([T2, S, T1])
# Scale inverse
def scale_inverse(S):
return scale(1/S[0,0], 1/S[1,1], 1/S[2,2])🔀 Shear
🎯 Shearing Transforms
Shear distorts an object by sliding layers along an axis. It is used in oblique projections and deformations.
Shear Details
Matrix: [[1, sh_xy, sh_xz, 0], [sh_yx, 1, sh_yz, 0], [sh_zx, sh_zy, 1, 0], [0, 0, 0, 1]]. Properties: Preserves area/volume. Applications: Creating oblique views, distorting text.
Combination: Often combined with other transforms.
# Shear matrices
def shear_xy(sh_xy, sh_yx):
# Shear in XY plane
return np.array([
[1, sh_xy, 0, 0],
[sh_yx, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
])
def shear_xz(sh_xz, sh_zx):
return np.array([
[1, 0, sh_xz, 0],
[0, 1, 0, 0],
[sh_zx, 0, 1, 0],
[0, 0, 0, 1]
])
def shear_yz(sh_yz, sh_zy):
return np.array([
[1, 0, 0, 0],
[0, 1, sh_yz, 0],
[0, sh_zy, 1, 0],
[0, 0, 0, 1]
])
# General shear
def shear_3d(sh_xy, sh_xz, sh_yx, sh_yz, sh_zx, sh_zy):
return np.array([
[1, sh_xy, sh_xz, 0],
[sh_yx, 1, sh_yz, 0],
[sh_zx, sh_zy, 1, 0],
[0, 0, 0, 1]
])🧮 Homogeneous Coordinates
🎯 4D Representation
Homogeneous coordinates represent 3D points as 4D vectors (x, y, z, w). This enables affine transforms with a single matrix.
Homogeneous Details
Point: (x, y, z, 1). Vector: (x, y, z, 0). Perspective division: (x/w, y/w, z/w). Projective transforms: w can be anything.
Advantages: Unifies affine transforms, enables perspective projection.
# Homogeneous coordinate operations
def to_homogeneous(point_3d):
# Convert 3D point to homogeneous (w=1)
return np.array([point_3d[0], point_3d[1], point_3d[2], 1.0])
def to_homogeneous_vector(vector_3d):
# Convert 3D vector to homogeneous (w=0)
return np.array([vector_3d[0], vector_3d[1], vector_3d[2], 0.0])
def from_homogeneous(point_h):
# Convert homogeneous to 3D (perspective division)
w = point_h[3]
if w == 0:
return None # Point at infinity
return np.array([point_h[0]/w, point_h[1]/w, point_h[2]/w])
def transform_homogeneous(point_h, matrix):
# Apply transform in homogeneous coordinates
return matrix @ point_h
# Example: Transform a point
point = np.array([1, 2, 3])
point_h = to_homogeneous(point)
T = translation(2, 3, 4)
result_h = transform_homogeneous(point_h, T)
result = from_homogeneous(result_h) # (3, 5, 7)🌀 Quaternions
🎯 Quaternion Rotation
Quaternions are a mathematical system for representing rotations. They avoid gimbal lock and provide smooth interpolation.
Quaternion Details
Format: q = (w, x, y, z) where w is scalar and (x,y,z) is vector. Rotation: q = (cos(θ/2), sin(θ/2) * axis). Multiplication: Non-commutative. SLERP: Smooth interpolation between quaternions.
Advantages: No gimbal lock, compact (4 numbers vs 9 for matrix).
# Quaternion class
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
def normalize(self):
mag = math.sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)
return Quaternion(self.w/mag, self.x/mag, self.y/mag, self.z/mag)
def conjugate(self):
return Quaternion(self.w, -self.x, -self.y, -self.z)
def multiply(self, other):
return Quaternion(
self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x,
self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w
)
def rotate_point(self, point):
# Rotate a 3D point using quaternion
p = Quaternion(0, point[0], point[1], point[2])
q = self
q_inv = q.conjugate()
result = q.multiply(p).multiply(q_inv)
return np.array([result.x, result.y, result.z])
def to_rotation_matrix(self):
# Convert to 3x3 rotation matrix
w, x, y, z = self.w, self.x, self.y, self.z
return np.array([
[1 - 2*y*y - 2*z*z, 2*x*y - 2*w*z, 2*x*z + 2*w*y],
[2*x*y + 2*w*z, 1 - 2*x*x - 2*z*z, 2*y*z - 2*w*x],
[2*x*z - 2*w*y, 2*y*z + 2*w*x, 1 - 2*x*x - 2*y*y]
])
# Create rotation quaternion from axis-angle
def quaternion_from_axis_angle(axis, angle):
axis = axis / np.linalg.norm(axis)
half_angle = angle / 2
return Quaternion(
np.cos(half_angle),
axis[0] * np.sin(half_angle),
axis[1] * np.sin(half_angle),
axis[2] * np.sin(half_angle)
)📊 Euler Angles
🎯 Euler Angle Representation
Euler angles represent rotation as three successive rotations around the axes (e.g., roll, pitch, yaw).
Euler Details
Order: XYZ, ZYX, etc. Gimbal lock: Loss of one degree of freedom when two axes align. Advantages: Intuitive for humans. Disadvantages: Gimbal lock, non-unique representation.
Applications: Aircraft orientation, game cameras.
# Euler angles to rotation matrix (XYZ order)
def euler_to_matrix(roll, pitch, yaw):
# Roll (X), Pitch (Y), Yaw (Z)
Rz = rotation_z(yaw)
Ry = rotation_y(pitch)
Rx = rotation_x(roll)
return compose_transforms([Rz, Ry, Rx])
# Matrix to Euler angles (XYZ order)
def matrix_to_euler(R):
# Extract euler angles from 3x3 rotation matrix
# R is 3x3 or 4x4 matrix
if R.shape[0] == 4:
R = R[:3, :3]
# Handle gimbal lock
if abs(R[0, 2]) < 1 - 1e-8:
yaw = np.arctan2(-R[0, 1], R[0, 0])
pitch = np.arcsin(R[0, 2])
roll = np.arctan2(-R[1, 2], R[2, 2])
else:
# Gimbal lock
roll = 0
if R[0, 2] < 0:
pitch = np.pi / 2
yaw = np.arctan2(R[1, 0], R[1, 1])
else:
pitch = -np.pi / 2
yaw = np.arctan2(-R[1, 0], R[1, 1])
return roll, pitch, yaw
# Quaternion to Euler
def quaternion_to_euler(q):
# q is Quaternion object
# Convert to matrix first
R = q.to_rotation_matrix()
return matrix_to_euler(R)👁️ View Transform
🎯 Camera Position
The view transform positions the camera in the scene. It maps world coordinates to camera coordinates.
View Transform Details
Look-at matrix: Position camera at eye, looking at target. Components: Position, target, up vector. Inverse: Model transform (placing objects in world).
Applications: 3D rendering, AR/VR, navigation.
# Look-at matrix (camera view)
def look_at(eye, target, up):
# eye: camera position (3D vector)
# target: point camera is looking at (3D vector)
# up: up direction (3D vector)
z = (eye - target) / np.linalg.norm(eye - target)
x = np.cross(up, z) / np.linalg.norm(np.cross(up, z))
y = np.cross(z, x)
# Create rotation matrix
R = np.array([
[x[0], x[1], x[2], 0],
[y[0], y[1], y[2], 0],
[z[0], z[1], z[2], 0],
[0, 0, 0, 1]
])
# Translate by -eye
T = translation(-eye[0], -eye[1], -eye[2])
return R @ T
# Or use a simpler implementation
def look_at_simple(eye, target, up):
forward = (target - eye) / np.linalg.norm(target - eye)
right = np.cross(forward, up) / np.linalg.norm(np.cross(forward, up))
up_new = np.cross(right, forward)
# Build matrix
return np.array([
[right[0], right[1], right[2], -np.dot(right, eye)],
[up_new[0], up_new[1], up_new[2], -np.dot(up_new, eye)],
[-forward[0], -forward[1], -forward[2], np.dot(forward, eye)],
[0, 0, 0, 1]
])🎯 Projection Transform
🎯 3D to 2D Projection
The projection transform maps 3D points to 2D screen coordinates. Perspective and orthographic projections are the main types.
Projection Types
Perspective: Objects farther away appear smaller (realistic). Orthographic: No perspective distortion (technical drawings). Frustum: View volume for perspective projection.
Matrix: Projection matrix + perspective division.
# Perspective projection matrix
def perspective(fov_y, aspect, near, far):
# fov_y: vertical field of view in radians
# aspect: width / height
# near, far: clipping planes
f = 1.0 / np.tan(fov_y / 2.0)
return np.array([
[f / aspect, 0, 0, 0],
[0, f, 0, 0],
[0, 0, (far + near) / (near - far), (2 * far * near) / (near - far)],
[0, 0, -1, 0]
])
# Orthographic projection
def orthographic(left, right, bottom, top, near, far):
return np.array([
[2/(right-left), 0, 0, -(right+left)/(right-left)],
[0, 2/(top-bottom), 0, -(top+bottom)/(top-bottom)],
[0, 0, -2/(far-near), -(far+near)/(far-near)],
[0, 0, 0, 1]
])
# Apply projection and perspective division
def project_point(point, projection_matrix):
point_h = to_homogeneous(point)
projected = projection_matrix @ point_h
return from_homogeneous(projected)🔄 Compositing
🎯 Transform Composition
Compositing combines multiple transforms into a single matrix. The order of operations matters.
Composition Rules
Local vs World: Local transforms are applied in object space. Order: Scale → Rotate → Translate (for local). Parent hierarchy: Children inherit parent transforms.
Matrix stack: Push/pop for hierarchical animation.
# Transform stack
class TransformStack:
def __init__(self):
self.stack = [identity_4x4()]
def push(self):
self.stack.append(self.stack[-1].copy())
def pop(self):
if len(self.stack) > 1:
return self.stack.pop()
def multiply(self, matrix):
self.stack[-1] = self.stack[-1] @ matrix
def get_current(self):
return self.stack[-1]
# Object hierarchy transform
class TransformNode:
def __init__(self, name):
self.name = name
self.children = []
self.parent = None
self.local_transform = identity_4x4()
def add_child(self, child):
self.children.append(child)
child.parent = self
def get_world_transform(self):
if self.parent:
return self.parent.get_world_transform() @ self.local_transform
return self.local_transform
def set_transform(self, T):
self.local_transform = T💼 Applications
🎯 Where 3D Transforms are Used
3D transforms are essential in game development, 3D modeling, AR/VR, robotics, and computer vision.
Application Domains
Games: Object positioning, animation, camera control. 3D modeling: Manipulating vertices and objects. AR/VR: Tracking and rendering. Robotics: Kinematics and motion planning. Vision: Camera calibration and 3D reconstruction.
Medical imaging: Registration and visualization.
# Application: 3D object renderer
class Renderer:
def __init__(self):
self.objects = []
self.camera = Camera()
def render_frame(self):
# Get view and projection transforms
view_matrix = self.camera.get_view_matrix()
proj_matrix = self.camera.get_projection_matrix()
for obj in self.objects:
# Get object's transform
model_matrix = obj.get_world_transform()
# Model-View-Projection matrix
mvp = proj_matrix @ view_matrix @ model_matrix
# Transform vertices
transformed_vertices = []
for vertex in obj.vertices:
vertex_h = to_homogeneous(vertex)
transformed = mvp @ vertex_h
screen_pos = from_homogeneous(transformed)
transformed_vertices.append(screen_pos)
# Draw object
self.draw(transformed_vertices, obj.color)
def draw(self, vertices, color):
# Rasterization code
pass
# Application: Animation with keyframes
class Animation:
def __init__(self, target, keyframes):
self.target = target
self.keyframes = keyframes # (time, position, rotation, scale)
self.current_time = 0
def update(self, delta_time):
self.current_time += delta_time
# Interpolate between keyframes (SLERP for rotation)
# Apply transform to target
pass