card-raytracer/README.md
Ole-Morten Duesund a18f68badb README: lead with the 1337-byte 'leet' fact
The exact byte count is the whole punchline — surface it explicitly with
a verifiable wc -c invocation rather than hiding it as '~1337 characters'
in a subordinate clause.
2026-05-28 13:52:13 +02:00

100 lines
3.6 KiB
Markdown

# card-raytracer
A study of [Andrew Kensler's "business card raytracer"][aek] — a complete
recursive path-tracer with soft shadows, depth of field, anti-aliasing,
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 not a happy accident — Kensler golfed the source until it hit
> the magic number. Every typedef (`i` for `int`, `f` for `float`),
> every operator-overload trick on `struct v`, and every absent space
> exists to land on **1337**.
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:
![Rendered output: the letters "aek" as reflective spheres on a red and white checker floor under a blue-purple sky, with soft shadows and depth-of-field blur](docs/aek.png)
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/