Why SSAO's Dark Corners Were Never Real
Sean Barrett's 2012 photographic takedown of screen-space ambient occlusion predicted a decade of rendering research.
A fourteen-year-old blog post is back on the Hacker News front page, and it deserves the second lap. In December 2012, Sean Barrett — the graphics programmer behind the ubiquitous stb single-file libraries — pointed a Canon 5D at the corners of his apartment and published the pixel-brightness plots. His conclusion: the dark creases that screen-space ambient occlusion paints into every game are, mostly, not there in real life. The essay reads today less like a period piece and more like a prediction of the next decade of rendering research, which is exactly why it keeps resurfacing.
The hack that ate real-time lighting
SSAO comes from Martin Mittring's CryEngine 2 course notes at SIGGRAPH 2007. The trick was irresistible: sample the depth buffer around each pixel, estimate how "enclosed" that point is, and darken accordingly. No scene queries, no precomputation, one full-screen pass. Crysis shipped it, and within a couple of years some variant was in essentially every engine, because it was the cheapest way to make flat ambient lighting look like it had depth.
Buried in Mittring's own notes is a detail the industry promptly forgot: Crytek tried applying the occlusion term to ambient, diffuse, and specular lighting, and found it only worked applied to ambient. Most implementations that followed multiplied it over everything, including direct light — which is physically nonsense, since a surface lit by a visible light source is not occluded from that source by definition.
Four errors, stacked
Barrett's essay is a systematic teardown of that pipeline of approximations. SSAO approximates ambient occlusion, which itself approximates real indirect lighting, which engines then misapply to all lighting, and which artists then crank because subtle effects don't justify their milliseconds. Each stage compounds the last, and the sum is the "dirt in the creases" look that reads instantly as video game.
The photographic evidence is the fun part. Shooting his apartment at multiple exposures and graphing brightness across wall junctions, Barrett found that corner darkening happens only some of the time, and when it does, the cause is usually something SSAO doesn't model: soft shadows from an area light, the Lambertian cosine falloff as a surface turns away from the light, or Mach bands — a perceptual illusion in which your visual system exaggerates edges at brightness discontinuities. That last one is quietly devastating. If the darkening at corners is partly manufactured by the viewer's own retina, a renderer that bakes it into the framebuffer is double-counting an effect the eye will add anyway.
The sharpest observation is about what AO structurally cannot represent. Ambient occlusion is a darkening-only model of indirect light. But real global illumination at a concave corner does two things at once: the geometry blocks some sky light, and the two nearby surfaces bounce light into each other. Those terms partially cancel — interreflection is precisely why real corners are brighter than AO says they should be. Model only the occlusion half and you've built a renderer that's wrong by construction, no matter how many samples you throw at it.
The industry quietly conceded the point
You don't have to take a blog post's word for it, because the field's own trajectory validated it. Activision's 2016 GTAO paper — Jimenez et al., "Practical Realtime Strategies for Accurate Indirect Occlusion" — reformulated ambient occlusion to match a ray-traced ground-truth reference in about half a millisecond of console GPU time. And critically, the same paper bolts on a near-field bounce term, because, in the authors' framing, that lighting is lost when you use ambient occlusion alone. That is Barrett's interreflection argument, restated four years later as an Activision technical memo and shipped in production. Intel's open-source XeGTAO implementation has since made GTAO the de facto reference for anyone building a modern occlusion pass.
The larger arc points the same way. Unreal Engine 5's Lumen and the various ray-traced GI systems compute actual indirect lighting, sky visibility and bounce together, which is the correct fix: when your ambient term is no longer a flat constant, you don't need a screen-space fudge to hide that it's a flat constant. AO survives in those pipelines as a small-radius contact term, not a look.
What this means for your renderer
If you're shipping on hardware where real GI isn't affordable — mobile, Switch-class consoles, the long tail of PC — SSAO isn't going anywhere, so the practical takeaways matter:
- Apply occlusion to the ambient and indirect terms only. If your AO texture is multiplied into direct lighting, that's a bug with a one-line fix and a large visual payoff.
- Keep the sample radius physically small. AO is a contact-shadow approximation; a two-meter radius turns it into a bad GI imitation with halos.
- If you're writing a new pass in 2026, start from XeGTAO rather than a Crysis-lineage or HBAO-style kernel. Same budget, and it's fit to ground truth instead of to taste.
- When dynamic GI is on, ratchet AO intensity down rather than layering both at full strength — otherwise you're occluding light your GI already occluded.
The transferable lesson
The reason this essay keeps trending isn't really SSAO. It's the epistemics. A generation of developers tuned their occlusion to look like other games' occlusion, and the reference drifted so far from reality that brighter, physically-plausible corners started to look wrong. Barrett's response — go outside, photograph the actual world, plot the data — is the part worth stealing, and it applies just as well to tone mapping, bloom, and whatever screen-space trick ships next. Fourteen years on, the verdict is clean: he was right, the industry's best practitioners eventually agreed in the literature, and the games still shipping crushed black creases in 2026 no longer have the excuse of not knowing better.
Sources & further reading
- Corners Don't Look Like That: Regarding Screenspace Ambient Occlusion — nothings.org
- Finding Next Gen: CryEngine 2 — advances.realtimerendering.com
- Practical Realtime Strategies for Accurate Indirect Occlusion (GTAO) — research.activision.com
- XeGTAO: Ground Truth Ambient Occlusion implementation — github.com
Rachel has been embedded in the developer tooling ecosystem for nearly eight years, covering everything from IDE wars and package-manager drama to the quiet rise of AI-assisted coding. She has a soft spot for open-source maintainers and an unhealthy number of terminal emulators installed on a single laptop.
Discussion 2
the thing that bugs me though—barrett's comparison assumes SSAO was ever trying to replicate real-world lighting rather than serve as a cheap visual cue for geometry. game engines aren't photography. feels like he's measuring the wrong thing unless there were actual test cases somewhere defining what SSAO was supposed to achieve
yeah, when we migrated our deferred renderer away from ssao last year, we actually measured the perf cost against visual fidelity and... the gap was wild. turned out we were burning cycles on something players literally couldn't perceive in motion, and sean's photographic proof is exactly why i stopped being religious about it.