AnyLearn
All lessons

Sort or Search: Why Both Pipelines Survive

The two renderer architectures are not competing implementations of the same idea. One sorts fragments for a single viewpoint, the other searches for a hit along an arbitrary ray. Reading them that way explains which effects are cheap in each, why production renderers combine them, and what a hybrid actually buys.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
Progress1 / 9

One question, two data structures

Both pipelines answer the same question: along a given ray, which surface is nearest? The difference is what they build to answer it.

A rasteriser builds a depth buffer. It is a per-pixel record of the nearest thing found so far, filled by streaming geometry past it in any order. That is a distributed sort by depth, done incrementally, with one comparison per fragment and no global bookkeeping.

A ray tracer builds a spatial hierarchy. It is a record of where geometry is in space, and it is queried rather than filled. That is a search structure.

Sort versus search is the whole distinction. Everything downstream follows from it: which effects are cheap, how each degrades, what each needs to know before it can start, and what happens when the scene changes.

Full lesson text

All 9 steps on one page, for reading, reference, and search.

Show

1. One question, two data structures

Both pipelines answer the same question: along a given ray, which surface is nearest? The difference is what they build to answer it.

A rasteriser builds a depth buffer. It is a per-pixel record of the nearest thing found so far, filled by streaming geometry past it in any order. That is a distributed sort by depth, done incrementally, with one comparison per fragment and no global bookkeeping.

A ray tracer builds a spatial hierarchy. It is a record of where geometry is in space, and it is queried rather than filled. That is a search structure.

Sort versus search is the whole distinction. Everything downstream follows from it: which effects are cheap, how each degrades, what each needs to know before it can start, and what happens when the scene changes.

2. The consequence: how many origins you get

A depth buffer is indexed by pixel, so it holds the answer for rays sharing one origin. Change the origin and it is worthless: nothing in it says what is visible from a light, or from a point on a mirror.

A hierarchy is indexed by position in space, so it answers for any origin and direction at the same cost.

That single asymmetry explains the effect list. Camera visibility needs one origin, so rasterisation handles it. A shadow needs visibility from the light, a reflection from a surface point in a direction that varies per pixel, ambient occlusion from every shading point in many directions, and global illumination from every point in every direction.

Each step down that list adds origins, and each is correspondingly harder to fake in a rasteriser and more natural in a tracer.

3. Where the approximations come from

Read this as a diagnosis rather than a menu. Every rasteriser technique with a reputation for artefacts sits on this diagram at the point where it ran out of origins.

Shadow mapping works because a light is one fixed extra origin, so a second depth buffer covers it. Its familiar problems, acne, peter-panning and resolution aliasing, are all sampling artefacts of that one buffer.

Screen-space reflections fail on anything not currently visible on screen, and that is not an implementation weakness: the only depth information available is for the camera's origin, so reflections of off-screen geometry were never in the data.

The artefacts are not bugs. They are the shape of the missing information.

flowchart TD
A["Effect needs visibility from a new origin"] --> B["One extra origin: the light"]
A --> C["One origin per pixel"]
A --> D["Many origins per pixel"]
B --> E["Render a depth map from there: shadow mapping"]
C --> F["No rasteriser equivalent: fake with probes or screen space"]
D --> G["Trace rays, or precompute offline"]

4. Deferred shading: sorting first, shading later

Before reaching for rays, rasterisers extract more from the sort they already do. The previous lesson noted that overdraw wastes shading on fragments later covered. Deferred shading removes the waste by splitting the pass in two.

The first pass rasterises geometry but computes no lighting. It writes surface parameters, position or depth, normal, albedo, roughness, into a set of buffers, letting the depth test resolve visibility. The second pass runs once per pixel over those buffers and computes lighting for the one surface that survived.

Shading cost becomes independent of scene depth complexity, and lighting cost becomes proportional to pixels times lights rather than fragments times lights.

The price is a fixed vocabulary of surface parameters, since anything the buffers cannot express cannot be shaded later, plus bandwidth, plus the standing transparency problem: a translucent surface has no single set of parameters per pixel.

5. The hybrid, and why the split falls where it does

Production real-time renderers now run both. The division of labour follows directly from the two cost models.

Primary visibility goes to the rasteriser. Those rays share one origin, are perfectly coherent, and the depth buffer resolves them with one comparison per fragment. Tracing them instead means paying tree traversal for a query the sort already answers.

Secondary visibility goes to the tracer. Shadows, reflections and indirect light all need origins the depth buffer cannot supply, and those are exactly the rays worth paying traversal for.

The join is the buffer written by the first pass. A deferred pass leaves position and normal per pixel, which is precisely the origin and surface frame a secondary ray needs. That is why deferred shading and hybrid ray tracing fit together so neatly: the buffer built to defer lighting is also a buffer of ray origins.

6. Choosing a pipeline, as a decision

Reduced to the questions that actually decide it:

How many ray origins does the image need?
  one, the camera            -> rasterise
  one plus a few fixed       -> rasterise, one depth map per extra origin
  one per pixel, varying     -> trace secondary rays, keep primary rasterised
  many per pixel             -> trace, and budget for noise and denoising

Does the geometry deform every frame?
  yes, heavily               -> rasterisation is cheaper; BVH refit costs
  no, mostly rigid instances -> a two-level BVH handles motion well

Is the frame budget fixed?
  yes, real time             -> rasterise what you can, trace what you must
  no, offline                -> trace everything; the sort buys nothing

The first question does most of the work, and it is a question about the image rather than about the hardware. Notice the last line: offline renderers do not use a depth buffer, because when there is no frame budget the rasteriser's speed advantage on primary rays is not worth maintaining a second architecture.

7. Where the noise comes from, and what removes it

Once rays are traced for lighting rather than visibility, a new cost appears that has no analogue in rasterisation.

Indirect light at a point is an integral over all incoming directions, which the lesson Rendering as an Integral develops properly. Tracing rays estimates that integral by sampling it, and an estimate from a handful of samples is noisy. Halving the noise requires roughly four times the samples, because the error of a Monte Carlo estimate falls with the square root of the sample count.

At a few rays per pixel, which is the real-time budget, the raw result is unusable. What makes it shippable is denoising: reusing samples from neighbouring pixels and from previous frames, guided by the position and normal buffers to avoid mixing across surfaces.

So a real-time tracer is not a small offline tracer. It is a very sparse estimate plus a reconstruction step, and the reconstruction is doing much of the work.

8. The two architectures side by side

RasterisationRay tracing
Loop orderTriangles, then pixelsPixels, then geometry
Structure builtDepth buffer, per pixelSpatial hierarchy, per scene
NatureIncremental sortSearch
Ray originsOneAny
Scene scalingLinear in trianglesRoughly logarithmic
Moving geometryFreeCosts a rebuild or refit
CoherenceGuaranteedDegrades with bounce depth
Weak atAnything off-cameraPrimary visibility, deformation

The rows are not a scorecard. They are two coherent sets of trade-offs that happen to be complementary, which is why the field settled on using both rather than one replacing the other.

The sharpest way to hold it: rasterisation is cheap because it answers one question; ray tracing is expensive because it answers all of them.

9. What to carry out of the cursus

The arc was one question narrowing. Projection moved geometry into a space where visibility could be asked. Then visibility was answered twice, by sorting and by searching, and every property of the two pipelines followed from that choice rather than from implementation quality.

The transferable habit is to read a rendering feature by asking what visibility query it requires and from how many origins. That predicts, without knowing anything about the engine, whether the feature will be cheap, whether it will be approximated, and what its artefacts will look like when the approximation runs out.

It also cuts through the framing that treats one architecture as the successor to the other. They compute different things. A depth buffer is not a worse hierarchy, and a hierarchy is not a better depth buffer.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. What is the sharpest structural difference between the two architectures?
    • One runs on the GPU and the other on the CPU
    • One builds a per-pixel depth record, an incremental sort; the other builds a spatial hierarchy, a search structure
    • One handles triangles and the other handles arbitrary surfaces
    • One computes shading and the other computes only visibility
  2. Why do screen-space reflections fail on off-screen geometry?
    • The reflection shader runs before the depth buffer is complete
    • Reflected rays diverge too much to be coherent
    • The depth buffer holds answers only for the camera's origin, so off-screen surfaces were never in the data
    • Screen-space buffers store insufficient precision for reflection vectors
  3. Why do deferred shading and hybrid ray tracing fit together so naturally?
    • Deferred shading removes the need for an acceleration structure
    • Both require the scene to be static
    • Deferred shading sorts triangles, which speeds up BVH construction
    • The buffer written to defer lighting holds per-pixel position and normal, which is exactly the origin and frame a secondary ray needs
  4. Why does an offline renderer typically not bother rasterising primary visibility?
    • Because with no frame budget the speed advantage on primary rays does not justify maintaining a second architecture
    • Because depth buffers cannot store enough precision for film resolution
    • Because rasterisation cannot handle triangles with textures
    • Because BVH traversal is faster than a depth test per fragment
  5. Why is a real-time ray tracer not simply a scaled-down offline one?
    • It uses a different acceleration structure entirely
    • It traces only shadow rays and never reflections
    • At a few samples per pixel the Monte Carlo estimate is unusably noisy, so denoising and sample reuse do much of the work
    • It computes lighting analytically rather than by sampling

Related lessons

Programming
advanced

Refs: A Branch Is a File Containing a Hash

Hashes are unusable by hand, so Git names them. A branch is not a copy of anything, not a directory, and not a container for commits: it is a forty-character file. Once that is clear, checkout, reset, detached HEAD and the reflog stop being separate concepts and become one operation on one kind of file.

8 steps·~12 min
Programming
intermediate

The Object Database: Git Is a Content-Addressed Store

Git is usually taught as a set of commands. Underneath it is a key-value store where the key is a hash of the content, and four object types built on that one idea. This lesson derives the hash by hand, shows what a commit actually contains, and explains why the design makes history tamper-evident.

8 steps·~12 min
Programming
advanced

The Design Philosophy: What Go Left Out

Go is unusual for what it refuses to include. Errors are values, not exceptions. Interfaces are satisfied without declaring it. Formatting is not a choice. This lesson works through the omissions, the reasoning behind each, and the real costs, because every one of them trades expressiveness for something else.

8 steps·~12 min
Programming
advanced

Channels: Share Memory by Communicating

Cheap concurrency without a coordination primitive is a bug generator. Go's answer is a typed conduit that carries values between goroutines, from a 1978 idea: rather than protecting shared state with locks, pass ownership through a channel so there is no sharing to protect. This lesson builds channels, select, and where the model does not fit.

8 steps·~12 min