Path Tracer & Ray Tracer
Overview
A CPU-based renderer built from scratch in C++, extended from a ray tracer into a full Monte Carlo path tracer. The path tracer simulates global illumination by tracing light paths recursively through a scene, using cosine-weighted hemisphere sampling, Russian Roulette termination, and Next Event Estimation to produce physically accurate renders.
The ray tracer foundation implements Phong shading, shadow rays, and sphere/triangle intersections - extended with antialiasing and recursive reflections on separate branches.
Full source code on GitHub, with branches for antialiasing, recursive reflections, and path tracing.
global illumination
Path Tracer Viewer
SIGGRAPH Β· 512 spp
Rendering Equation Lo = Le + β« fr Β· Li Β· cosΞΈ dΟ
Where f_r is the BRDF, L_i is incoming radiance, cosΞΈ is the angle to the surface normal.
sample count comparison
64 spp vs 512 spp
Drag to compare Β· 64 spp left Β· 512 spp right
renderer progression
Ray Tracer Viewer
Snow Β· Stage 1 of 5
Formula -
Where -
What Is a Path Tracer?
A path tracer extends a ray tracer by simulating the full physical behavior of light. Rather than approximating indirect lighting with an ambient term, a path tracer recursively traces rays as they bounce through a scene - each bounce picking up color from whatever surface it hits next, and combining it with direct illumination from light sources.
The result is physically accurate global illumination: color bleeding between surfaces, soft shadows from area lights, and realistic inter-reflections - all emerging naturally from the simulation rather than being approximated.
The key tradeoff is noise. Each pixel is estimated by averaging many independent random paths. Fewer samples means faster renders but grainier images. The 64 vs 512 spp comparison above shows this directly.
Core Features
Monte Carlo Global Illumination
Each pixel samples 64 or 512 random light paths. Every path bounces up to 5 times, accumulating color from each surface it hits. The average of all samples approximates the rendering equation integral, producing physically accurate indirect lighting without precomputed light maps.
Cosine-Weighted Hemisphere Sampling
Bounce directions are sampled proportionally to the cosine of the angle from the surface normal. This concentrates samples where they contribute most energy, reducing variance compared to uniform hemisphere sampling. The cosine term in the rendering equation cancels against the PDF, simplifying the estimator.
Russian Roulette Termination
Paths are terminated probabilistically based on surface albedo rather than a fixed depth limit. High-albedo surfaces have a higher survival probability; low-albedo paths terminate early. Surviving paths divide their contribution by the survival probability to maintain an unbiased estimate.
Next Event Estimation
At every bounce, a shadow ray is fired directly to each point light and its contribution is added to the path. This explicit direct lighting sampling dramatically reduces variance in the indirect lighting estimate, especially in scenes where random rays rarely hit light sources.
Ray Tracer Foundation
The path tracer extends a from-scratch ray tracer implementing sphere and triangle intersection, Phong shading with barycentric normal interpolation, shadow rays, 2Γ2 supersampling anti-aliasing, and configurable-depth recursive reflections - all built without reliance on existing rendering engines.
What Is a Ray Tracer?
Think about how you see the world. Light travels from a source, bounces off objects, and eventually reaches your eye. A ray tracer reverses that process. Instead of simulating light going outward from a source, it starts at the camera and works backwards, firing one ray per pixel into the scene and asking: what does this pixel see, and how is it lit?
Most real-time graphics use a faster technique called rasterization instead. While rasterization is great for speed, it struggles to simulate how light actually behaves. Shadows, reflections, and the way light bounces between surfaces all have to be faked using clever tricks. Ray tracing does not need those tricks - shadows fall naturally because a blocked ray means no light, and reflections work because a ray can bounce and pick up color from whatever it hits next.
Sources: NVIDIA Blog, Wikipedia, Scratchapixel
Core Features
Ray Generation
For every pixel in a 640x480 image, a ray is fired from the camera into the scene. The direction of each ray is computed from the field of view and aspect ratio, mapping screen coordinates into 3D camera space.
Geometry Intersection
Two primitive types are supported. Spheres use a quadratic intersection formula, solving for where the ray meets the sphere surface. Triangles use a plane intersection followed by a barycentric coordinate test to confirm the hit lands inside the triangle boundary. The same barycentric weights are reused later for smooth shading.
Phong Illumination
Lighting is computed per hit point using the Phong model, combining an ambient base, a diffuse term that responds to the angle between the surface and light, and a specular highlight that sharpens on glossy materials. Triangle surfaces interpolate normals and material properties across the surface using barycentric coordinates, giving smooth gradients instead of flat facets.
Shadow Rays
After every surface hit, a secondary ray is fired toward each light source. If any geometry blocks the path, that light is excluded from the final color. Scenes with multiple lights produce partial shadows correctly.
Extensions
Two extra features were implemented in separate branches. Antialiasing fires a 2x2 grid of rays per pixel and averages the result, smoothing jagged silhouette edges. Recursive reflections bounce rays off surfaces up to a configurable depth, blending reflected scene color with local Phong shading weighted by the surface's specular value.
Applications and Tradeoffs
Ray tracing produces physically accurate images by simulating how light actually behaves. Here is how it compares to rasterization, the technique used in most real-time applications:
| Ray Tracing | Rasterization | |
|---|---|---|
| Shadows | Natural, no tricks needed | Approximated via shadow maps |
| Reflections | Accurate, recursive | Faked with cubemaps or screen-space tricks |
| Light bouncing | Physically correct | Not supported without extra passes |
| Speed | Slow, expensive per pixel | Fast, optimized for GPUs |
| Primary use | Film, VFX, product rendering | Games, real-time applications |
Where it gets used today
Ray tracing is now finding its way into real-time pipelines. Games like Cyberpunk 2077 and Alan Wake 2 use ray tracing selectively for shadows and reflections while rasterizing everything else. The tradeoff is still real - enabling full ray tracing tanks frame rates even on high-end hardware.
Building this renderer made those tradeoffs tangible. Every feature added - shadow rays, reflection recursion, antialiasing - had a direct and measurable impact on render time. That hands-on understanding of where the cost comes from is something you do not get from using an engine.
Source Code
Full Monte Carlo path tracer with cosine-weighted sampling, Russian Roulette termination, Next Event Estimation, and 64/512 spp jittered sampling.
Core ray tracer - ray generation, sphere and triangle intersection, Phong shading, and shadow rays. Built in C++ from scratch.
2Γ2 grid supersampling - fires four rays per pixel and averages the result, smoothing jagged edges on curved surfaces.
Recursive reflection rays with configurable bounce depth. Final color blends local Phong shading with reflected scene color weighted by specularity.