Part 11: 3D Graphics: Parallel & Perspective Projections & Hidden Surface Removal
3D Rendering Pipeline & Visibility Determination
The leap from 2D plane drawings to three-dimensional visual worlds requires solving two fundamental mathematical problems: Projection (mapping 3D spatial points $(x, y, z)$ onto a 2D planar projection screen) and Hidden Surface Removal (determining which surfaces are occluded from the viewer's eye). In this capstone installment, we analyze Parallel vs Perspective Projections, derive perspective foreshortening and vanishing points, and dissect the industry-standard depth occlusion algorithms: Back-Face Culling and the Hardware Z-Buffer (Depth-Buffer) Algorithm.
1. Geometric Projections: Parallel vs. Perspective
Parallel (Orthographic) Projection
Projection lines (projectors) are parallel to one another. The Center of Projection (COP) is effectively at infinity.
- Preserves relative object dimensions and parallel lines.
- Zero perspective foreshortening.
- Essential for architectural blueprints, engineering CAD drawings, and isometric tactical video games.
Perspective Projection
All projectors converge at a single finite point: the Center of Projection (COP), representing the human eye or camera lens.
- Foreshortening: Distant objects appear progressively smaller in inverse proportion to distance $z$: $x' = \frac{d \cdot x}{z}, y' = \frac{d \cdot y}{z}$.
- Parallel 3D lines converge toward Vanishing Points on the horizon.
- Models biological human depth perception and photographic realism.
2. Hidden Surface Removal (Visible Surface Determination)
In complex 3D scenes, millions of polygons overlap in screen space. Drawing opaque objects in arbitrary order produces chaotic visual artifacts where background geometry overwrites foreground objects. Visibility determination algorithms resolve occlusion using two primary paradigms:
- Object-Space Algorithms: Compare 3D geometric surfaces against each other in world space (e.g. Back-Face Culling, BSP Trees). Complexity scales as $\mathcal{O}(N^2)$ with polygon count $N$.
- Image-Space Algorithms: Resolve visibility point-by-point at each pixel on the projection screen (e.g. Z-Buffer). Complexity scales as $\mathcal{O}(N \cdot \text{Pixels})$.
3. Back-Face Culling
In closed polyhedral meshes (such as 3D characters, vehicles, or terrain), the polygons facing away from the camera are physically occluded by the front surfaces of the same mesh.
Each triangular polygon possesses an outward-facing surface normal vector $\vec{N} = (A, B, C)$ computed via the cross product of its edges: $\vec{N} = (V_1 - V_0) \times (V_2 - V_0)$. Let $\vec{V}_{view}$ be the viewing direction vector pointing from the camera to the surface:
Back-face culling is executed early in the geometry shader stage of GPU pipelines, instantly pruning approximately 50% of the scene's triangles before rasterization.
4. The Z-Buffer (Depth-Buffer) Algorithm
First formulated by Edwin Catmull in 1974, the Z-Buffer Algorithm is the universal hardware standard implemented in 100% of modern GPUs (DirectX, Vulkan, Metal, WebGPU).
The GPU maintains two concurrent 2D memory arrays matched to screen resolution ($W \times H$):
- Color Buffer (Framebuffer): Stores the color $(R, G, B, A)$ of the currently visible pixel.
- Depth Buffer (Z-Buffer): Stores the floating-point depth $z \in [0.0, 1.0]$ of the closest surface currently rendered at $(x, y)$.
Z-Buffer Algorithmic Workflow
// 1. Frame Initialization
for each pixel (x, y) in Screen:
DepthBuffer[x][y] = 1.0; // Farthest possible depth (normalized)
ColorBuffer[x][y] = BackgroundColor;
// 2. Rasterization of all Polygons
for each polygon P in Scene:
for each fragment (x, y) generated during scan conversion:
Calculate fragment depth: z_frag;
if (z_frag < DepthBuffer[x][y]) {
DepthBuffer[x][y] = z_frag; // Update to new closer depth
ColorBuffer[x][y] = ShadeFragment(P); // Overwrite pixel color
}
// else: fragment is occluded by existing closer object -> DISCARDAdvantages & The Z-Fighting Phenomenon
- Order Independence: Polygons can be submitted to the GPU in arbitrary order; the depth buffer guarantees correct occlusion without sorting meshes.
- Z-Fighting: When two surfaces share nearly identical depth coordinates ($z_1 \approx z_2$), floating-point precision limitations cause the depth test to alternate erratically between surfaces across adjacent pixels, resulting in flickering visual noise. Modern engines mitigate this using reversed-Z floating point buffers ($[1.0 \to 0.0]$ mapping).
Related reading
How to Give Google Antigravity Direct SSH Access to Remote Servers (Proxmox & Linux)
Connect Google Antigravity directly to Proxmox and Linux servers using the Model Context Protocol (MCP), dedicated Ed25519 keys, and automated updates.
By Super Admin
Part 9: Load Balancing: Layer 4 vs Layer 7, Algorithms, Reverse Proxies & High Availability
Master high-availability load balancing, comparing Layer 4 transport vs Layer 7 application routing, consistent hashing rings, and health checks.
By Super Admin
Part 10: Distributed Databases: Partitioning, Sharding, 2PC & CAP Theorem
Master distributed database architecture: horizontal sharding, consistent hashing, two-phase commit (2PC), CAP theorem trade-offs, and NewSQL engines.
By Super Admin
Part 11: Linux 64-Bit System Calls, NASM Macros & Dynamic Memory Allocation
Learn low-level systems programming: Linux 64-bit SYSCALL table, multi-argument NASM %macro directives, file I/O operations, and sys_mmap memory allocation.
By Super Admin