Path-traced Cornell box, fully converged.

Path tracer

A physically-based path tracer written from scratch in WebGL 1.0, rendering the Cornell box. Advanced graphics course project, NCSU.

Early Cornell box render, noisy with only a few samples per pixel.

Overview

Light, simulated

I built a Monte Carlo path tracer from scratch, no engine, no ray-tracing library, and pointed it at a version of Cornell box wih spheres. Path tracing simulates light in reverse: rays leave the camera, bounce randomly around the room, and gather the light they find. Averaging many samples per pixel produces a physically accurate image.

Cornell box render showing color bleeding from the red and green walls.

Global illumination

Color bleeding

Light bouncing off the red and blue walls tints the white surfaces nearby. That subtle color bleed is indirect lighting, the effect that only global illumination, not traditional direct lighting, can reproduce.

Nearly noise-free, converged Cornell box render.

Convergence

Thousands of samples later

Monte Carlo noise falls off slowly, the finished image is the average of thousands of light paths per pixel. What's left is soft shadows, smooth gradients, and clean indirect light.

Oops

Pretty bugs

Writing a renderer from scratch means breaking it in visually spectacular ways. A few of my favorite failures...

Cornell box render peppered with bright firefly noise.

Bug

Fireflies

High-variance samples divided by a near-zero probability blow up into bright specks scattered across the frame. Mathematically wrong, weirdly starry.

Cornell box render with light leaking through the walls.

Bug

Light leak

A missing clamp let energy escape through the walls, washing the whole scene in an impossible, dreamy glow.

Cornell box render with a plaid grid pattern, each square converging at a different rate.

Bug

Plaid

An error in spacial sample frequency. A scene split into an even grid of squares, each converging at its own pace.

Reflection

My experience

Building a path tracer bottom-up made the rendering equation click in a way no lecture could, and the bugs were half the fun. Next time I'd reach for WebGL 2.0 or WebGPU to escape the 1.0 constraints.