Skip to content
Techniques & Technology

BSP Trees

3D rendering optimisation

Binary Space Partitioning trees enabled Doom's impossible 3D by pre-calculating visibility, determining which surfaces could see which others to eliminate overdraw.

ibm-pcsony-playstationsega-saturn 3drenderingoptimisation 1993–present

Overview

Doom looked 3D but ran on hardware that couldn't do real 3D — 386 PCs in 1993 had no GPU, no floating-point unit by default, no depth buffer. Binary Space Partitioning (BSP) trees made it possible by pre-computing the level's spatial structure during compilation. The algorithm recursively divided space into convex regions, building a tree the engine could walk at runtime to render every wall in correct front-to-back order, with no z-fighting and no wasted overdraw.

BSP became the foundation of Doom, Quake, Duke Nukem 3D, and the entire 1990s first-person genre. Modern GPU pipelines have replaced it for raw rendering, but BSP-derived ideas — visibility pre-computation, spatial indexing, level compilation — persist in modern engines as PVS, occlusion culling, and BVH structures.

Fast facts

  • Computer-science origin: Schumacker, Brand, Gilliland, Sharp (1969); Fuchs/Kedem/Naylor (1980) refined the algorithm.
  • Gaming use: Doom (1993, John Carmack at id Software).
  • Purpose: Visibility determination and back-to-front render ordering without a depth buffer.
  • Compile time: Pre-calculated when the level is built; runtime is a tree walk.
  • Tree size: Doom maps typically 200-2000 BSP nodes.

How BSP works

The algorithm recursively divides space using planes (lines in 2D). Each split produces two half-spaces — one in front of the splitting plane, one behind:

StepProcess
1Choose a splitting plane (typically a wall in the level)
2Divide level geometry into "in front of" and "behind" subsets
3Recurse on each subset
4Stop when a subset is convex (or fits a leaf-size criterion)
5The recursion structure becomes a binary tree

The result is a tree where each internal node is a splitting plane and each leaf is a convex region of empty space.

Front-to-back traversal

The magic: from any camera position, you can walk the tree to render geometry in correct front-to-back (or back-to-front) order:

function render_bsp(node, camera_pos):
    if node is a leaf:
        render_polygons_in_leaf(node)
        return
    
    if camera_pos is in front of node.plane:
        # Far side first (back), then this plane, then near side (front)
        render_bsp(node.back_child, camera_pos)
        render_polygons_on_plane(node)
        render_bsp(node.front_child, camera_pos)
    else:
        render_bsp(node.front_child, camera_pos)
        render_polygons_on_plane(node)
        render_bsp(node.back_child, camera_pos)

This produces a correct sort order from any viewpoint in O(n) per frame, where n is tree size — much faster than sorting all polygons every frame.

2D vs 3D BSP

Doom's BSP is 2D — it partitions the level's floor plan with vertical splitting planes. Doom worlds are "2.5D" (no rooms over rooms, no looking up/down beyond pitch limit), so 2D BSP suffices. Quake (1996) generalised to 3D BSP with arbitrary splitting planes — true volumetric partitioning.

Doom's BSP implementation

UseBenefit
Wall renderingFront-to-back order via tree walk; no z-buffer needed
Collision detectionWalk the tree to find what convex region the player is in
Sound propagationTrace through the BSP tree to find paths between rooms
LightingPer-sector lighting computed via BSP traversal
AI line-of-sightTrace ray through BSP to check visibility

The BSP file (*.WAD LUMP types NODES, SUBSECTORS, SEGS, SSECTORS) contains pre-built trees compiled by tools like DoomBuilder / Slade.

Compile-time calculation

AdvantageTrade-off
No runtime costEach level must be compiled before play
Optimal orderingStatic geometry only — moving walls don't fit cleanly
Consistent speedPredictable frame budget
Map changes need rebuildEditor must recompile each save

Doom level editors take seconds-to-minutes to rebuild the BSP after edits. Quake's BSP compilation is famously slow — large maps could compile for hours.

Visibility benefits

Problem solvedMethod
OverdrawFront-to-back rendering with implicit occlusion — closer walls hide farther ones
SortingAutomatic ordering during tree walk
OcclusionWalls behind solid walls aren't drawn
No z-bufferTree provides ordering; no per-pixel depth test needed

Quake evolution: PVS

Quake added the Potentially Visible Set (PVS) — for each leaf in the BSP, pre-compute which other leaves can be visible from it. At runtime, the camera's leaf's PVS gives an instant list of all leaves to even consider rendering. Most of the level is rejected without any geometry test.

EnhancementPurpose
PVS (Potentially Visible Set)Leaf-to-leaf visibility table; renders only visible leaves
PortalsRoom-connectivity hints used during PVS computation
LightmapsPre-calculated radiosity-style lighting per surface
Surface cachingCache rendered surfaces to skip re-rendering

PVS extended Doom's BSP idea from "draw in correct order" to "skip drawing entirely what isn't visible". The combination is what made Quake's 3D feasible on 1996 hardware.

Legacy

UseApplication
Collision detectionSpatial queries — walk the tree to find what region a point/ray hits
AI navigationPathfinding through BSP regions (Doom's reject-table optimisation)
CSG operationsConstructive Solid Geometry — adding/removing geometry from a BSP-organised world
Modern enginesSource engine (Half-Life 2) and successors still use BSP for static world geometry

Modern alternatives

BSP isn't the universal answer anymore — modern hardware and dynamic worlds favour different structures:

TechniqueUse case
OctreesDynamic scenes, voxel worlds, 3D-axis-aligned subdivision
BVH (Bounding Volume Hierarchy)Ray tracing, dynamic object collision
GPU cullingHardware occlusion queries; modern engines do per-frame culling on GPU
K-D treesSpatial indexing for nearest-neighbour queries
Cluster / portal systemsLarge open-world games partition by sectors

For static worlds with predictable geometry, BSP still wins on simplicity and predictability — many modern engines compile static level geometry into BSP-or-similar structures even when the runtime uses GPU rasterisation.

Famous engines using BSP

EngineEraBSP use
Doom engine19932D BSP for level rendering
Quake engine19963D BSP + PVS
Quake II engine1997Refined BSP + PVS, hardware acceleration
GoldSrc (Half-Life 1)1998Quake-derived BSP
Source (Half-Life 2)2004Full 3D BSP, modern compilation
id Tech 4 (Doom 3)2004Portal-based culling layered on BSP
id Tech 5+2011+Gradually replaced BSP with mega-texture approaches

See also