Texture Mapping
Images on polygons
Texture mapping wraps 2D images onto 3D surfaces, adding visual detail to polygonal models without the geometric complexity that would otherwise be required.
Overview
Polygons alone look flat and lifeless. Texture mapping wraps 2D images — textures — onto 3D surfaces, giving them detail that geometry couldn't practically provide. A brick wall doesn't need thousands of polygons; it needs one rectangle with a brick image. The technique became fundamental to real-time 3D graphics from Doom (1993) onward and remains the foundation of modern GPU pipelines.
Fast facts
- Purpose: Add surface detail without geometric complexity.
- Method: UV coordinate mapping — each vertex carries a (U, V) coordinate into a texture image.
- Benefit: A 1000-pixel-wide brick texture costs the same vertices as a 1-pixel-wide flat colour.
- Hardware: Modern GPUs have dedicated texture units; 1990s consoles had wildly varying capabilities.
UV coordinates
Each vertex of a polygon carries a 2D coordinate (U, V) that points into the texture image. The renderer interpolates these UVs across the polygon's interior, sampling the texture at each pixel:
| Concept | Function |
|---|---|
| U axis | Horizontal texture position (0.0 = left edge, 1.0 = right edge) |
| V axis | Vertical texture position (0.0 = top, 1.0 = bottom) — convention varies by API |
| Mapping | Texture-coordinate-to-vertex association set by the artist |
| Interpolation | Renderer fills polygon interior, computing UV at each pixel |
| Sampling | Look up the texture pixel at the interpolated UV |
Mapping methods
How artists assign UVs:
| Type | Application |
|---|---|
| Planar | Flat surfaces — project from one axis (floors, walls, billboards) |
| Cylindrical | Rounded objects — wrap the texture around an axis |
| Spherical | Globes, balls, eyeballs |
| Cube map | Six textures, one per cube face — used for skyboxes |
| UV unwrap | Complex shapes — manually flatten the model into a 2D layout |
Modern 3D modelling software (Blender, Maya, 3ds Max) provides interactive UV unwrapping tools that flatten meshes into 2D atlases.
Perspective correction
The hardest part of 1990s real-time texture mapping. The naive approach (linearly interpolate U and V across screen space) is affine texture mapping; it produces visible warping when polygons are at sharp angles to the camera. The correct approach is perspective-correct — interpolate U/Z and V/Z linearly, then divide by 1/Z per pixel.
| Issue | Solution |
|---|---|
| Affine distortion | Visible warp on tilted polygons (PSX is the famous example) |
| Per-pixel division | Compute U/Z, V/Z, and 1/Z; divide at every pixel; perspective-correct |
| Hardware support | GPU divides per pixel for free; software renderers ration this expensive operation |
| Software fallback | Subdivide polygons into smaller pieces so affine looks better; slow |
The per-pixel division was prohibitively expensive on 1990s consoles without dedicated hardware. Doom uses affine mapping for walls (always vertical, mostly camera-facing), and avoids tilted floors. Quake (1996) introduced subdivision-based perspective correction in software, then later versions used 3dfx Voodoo's hardware perspective correction.
Platform approaches
The 1990s console wars produced three distinct compromises on texture mapping:
| Platform | Method | Visible artefacts |
|---|---|---|
| PlayStation 1 | Affine, no perspective correction; integer-only UV units | Heavy warping on tilted polygons; texture "swimming" as polygons move |
| Sega Saturn | Quadrilateral rasterisation with forward differencing | Different artefacts — texture distortion at quad edges; less visible warp |
| Nintendo 64 | Perspective-correct, with bilinear filtering and 4 KB texture cache | Smaller texture sizes (most N64 games use 32×32 or 64×64 textures); blur from filtering |
| 3dfx Voodoo (PC) | Full perspective-correct, mipmapped, bilinear/trilinear | The gold standard of mid-90s texture mapping |
| PowerVR (PC, Dreamcast) | Tile-based deferred rendering with perspective correction | Different rendering architecture; very efficient at the time |
The PlayStation's affine mapping became its visual signature — every PSX game has the characteristic floor-warping look. Subsequent re-releases sometimes patch this; modern emulators (Duckstation) offer perspective-correct interpolation as an optional enhancement.
Filtering methods
How to sample a texture when the screen pixel doesn't align with a texture pixel exactly:
| Filter | Quality | Cost |
|---|---|---|
| Nearest neighbour | Pixelated; preserves the original art | One sample per pixel |
| Bilinear | Smooth, slightly blurry | Four samples + linear blend |
| Trilinear | Smooth across mipmap levels | Eight samples (two mipmap levels × four each) |
| Anisotropic | Sharp at oblique angles | Variable — up to 16 samples per pixel |
The choice between nearest-neighbour and bilinear is partly aesthetic. Modern pixel-art-style 3D games often use nearest-neighbour deliberately to preserve crisp pixel boundaries; AAA games use trilinear or anisotropic for smoothness.
Mipmapping
A texture rendered at distance — covering only a few pixels of the screen — should be sampled from a smaller version of itself, both for performance and to avoid aliasing. Mipmaps are pre-computed half-size, quarter-size, eighth-size... copies of the texture, all stored together:
| Purpose | Implementation |
|---|---|
| Distance scaling | Pre-scaled versions of the texture (each level half-resolution of the previous) |
| Performance | Smaller textures fit in cache; faster to sample |
| Aliasing reduction | Reduces the "shimmer" caused by sub-sampling high-frequency textures |
| Memory cost | Mipmap chain adds 1/3 to texture storage |
Standard chain: a 256×256 texture has mipmaps at 128×128, 64×64, 32×32, 16×16, 8×8, 4×4, 2×2, 1×1.
Memory considerations
Texture memory was the dominant constraint of 1990s 3D:
| Factor | Impact |
|---|---|
| Resolution | Doubling resolution = 4× VRAM |
| Colour depth | 32-bit RGBA vs 8-bit indexed = 4× memory |
| Compression | DXT / S3TC / BC formats: 4-8× saving |
| Palettes | Indexed colour with shared palettes was the PSX / N64 / Saturn norm |
| Texture atlas | Pack many small textures into one big one to reduce binding overhead |
The N64's tiny 4 KB texture cache forced its games into 32×32 or 64×64 textures — visibly blurry compared to PSX's larger texture allowance. The PSX's 1 MB VRAM limit meant aggressive palette use; full-colour textures were rare.
Modern texture features
Beyond simple colour mapping, modern texture pipelines support:
| Feature | Purpose |
|---|---|
| Normal maps | Per-pixel surface direction → fake high-detail lighting on low-poly models |
| Specular maps | Per-pixel shininess |
| Roughness maps | Per-pixel surface roughness for PBR |
| Albedo / colour maps | The "main" colour texture |
| Ambient occlusion maps | Pre-baked subtle shadowing |
| Displacement / parallax | Per-pixel surface displacement |
| Cube maps | Skyboxes, reflections |
| 3D textures | Volumetric data, fog, smoke |
Modern PBR (physically-based rendering) pipelines bind 4-8 textures per material. Compare to Quake (1996) using 1 texture per surface plus 1 lightmap.
See also
- BSP Trees
- Quake
- PlayStation
- Nintendo 64
- Mode 7