'Path tracing' in graphics literature means Monte Carlo solution of the rendering equation, with random hemisphere sampling at every diffuse surface and indirect light transport. card.cc has none of that — the floor branch in S() returns immediately with a flat ambient term and casts no new rays. What it actually does is Whitted-style recursive specular reflection (mirrors only), with distribution-style stochastic sampling layered on top for soft shadows, depth of field, and AA. Group the stochastic features (soft shadows, DOF, AA) under that qualifier; keep mirror reflections, checker floor, and sky gradient as separate non-stochastic features.
101 lines
3.7 KiB
Markdown
101 lines
3.7 KiB
Markdown
# card-raytracer
|
|
|
|
A study of [Andrew Kensler's "business card raytracer"][aek] — a complete
|
|
**Whitted-style raytracer with stochastic sampling** for soft shadows,
|
|
depth of field, and anti-aliasing, plus mirror reflections, a checker
|
|
floor, and a sky gradient, all crammed into a single C++ source file
|
|
small enough to print on the back of a business card.
|
|
|
|
> **`card.cc` is exactly 1337 bytes.** Yes, **leet**. Verify it yourself:
|
|
>
|
|
> ```sh
|
|
> $ wc -c card.cc
|
|
> 1337 card.cc
|
|
> ```
|
|
>
|
|
> That is suspiciously round — hand-golfed C++ rarely lands on a famous
|
|
> leet number by accident. The compression tricks all show their work:
|
|
> typedefs (`i` for `int`, `f` for `float`), operator overloads on
|
|
> `struct v` that turn `+` / `%` / `^` / `!` into vector math, and not
|
|
> a wasted space anywhere.
|
|
|
|
The original code is preserved verbatim as [`card.cc`](card.cc). A
|
|
de-obfuscated, heavily-annotated rewrite lives in
|
|
[`card_explained.cc`](card_explained.cc). Both produce the same image:
|
|
|
|

|
|
|
|
The spheres spell **a · e · k**, Andrew Kensler's initials, encoded as a
|
|
9-column bitmap in the `G[]` array of the original source.
|
|
|
|
## Quick start
|
|
|
|
Requires a C++14 compiler, CMake ≥ 3.16, and (optionally) `netpbm` for
|
|
PPM → PNG conversion.
|
|
|
|
```sh
|
|
cmake -S . -B build
|
|
cmake --build build
|
|
|
|
# Render with the original (PPM, 786 KiB, ~6 s on a modern laptop)
|
|
./build/card > aek.ppm
|
|
|
|
# Or via the convenience target:
|
|
cmake --build build --target image # → build/aek.ppm
|
|
cmake --build build --target image_explained # → build/aek_explained.ppm
|
|
|
|
# View it (PPM is supported by most image viewers; or convert):
|
|
pnmtopng aek.ppm > aek.png
|
|
```
|
|
|
|
## What's interesting about it
|
|
|
|
The whole renderer is built on **operator overloading**: a single `struct v`
|
|
provides `+` (add), `*` (scale), `%` (dot product), `^` (cross product), and
|
|
unary `!` (normalise). Once those are in place, the entire rendering
|
|
pipeline — sphere intersections, reflection vectors, Phong shading,
|
|
checker-pattern tile lookup, camera basis construction — collapses into
|
|
just a handful of expressions.
|
|
|
|
Other tricks worth admiring:
|
|
|
|
- **Soft shadows for free**: the light position is jittered each call, so
|
|
averaging 64 samples per pixel produces a real penumbra without any
|
|
area-light formalism.
|
|
- **Depth of field for free**: the camera origin is also jittered across
|
|
a 99-unit "lens" each sample, so points off the focal plane go blurry.
|
|
- **Anti-aliasing for free**: sub-pixel jitter on the ray direction.
|
|
|
|
The same 64-sample loop pays for all three at once.
|
|
|
|
A line-by-line walkthrough of the algorithm is the long-form
|
|
[comments inside `card_explained.cc`](card_explained.cc).
|
|
|
|
## Layout
|
|
|
|
```
|
|
.
|
|
├── card.cc # original, untouched — do not edit
|
|
├── card_explained.cc # de-obfuscated, documented rewrite
|
|
├── CMakeLists.txt
|
|
├── docs/
|
|
│ └── aek.png # published render
|
|
├── README.md
|
|
└── CLAUDE.md # working notes for AI assistance
|
|
```
|
|
|
|
## Credits
|
|
|
|
- Original raytracer: [Andrew Kensler][aek-home] ("aek").
|
|
- Excellent walkthrough that inspired this study:
|
|
Fabien Sanglard, [_Decyphering The Business Card Raytracer_][aek].
|
|
|
|
## Licence
|
|
|
|
The original `card.cc` is reproduced as-is from publicly published material
|
|
for educational purposes; credit and copyright belong to Andrew Kensler.
|
|
The supporting files in this repository (CMake, README, de-obfuscated
|
|
rewrite) are placed in the public domain by the repository author.
|
|
|
|
[aek]: https://fabiensanglard.net/rayTracing_back_of_business_card/
|
|
[aek-home]: https://www.cs.utah.edu/~aek/
|