A physically-based ray tracer written in C++, featuring a BVH acceleration structure, PBR shading, real-time interactive preview, and support for multiple materials and geometry types.
Scenes rendered with the engine — fractals, showcase stages, path tracing, and material stress tests.
A Mandelbulb-style 3D fractal rendered with the engine's full PBR pipeline. Intricate self-similar geometry stress-tests the BVH traversal at extreme depth.
A showcase scene featuring every supported shape type — spheres, cylinders, triangles, and planes — each displayed on individual pedestals under dramatic studio lighting. The definitive engine demo.
A minimalist path tracing scene pairing metallic and diffuse materials. Clean geometry lets the light transport speak for itself — soft indirect illumination and accurate Fresnel reflections on the metal surfaces.
A dense grid of spheres covering the full material palette: Default (Lambertian diffuse + specular), Mirror, Chrome, and Glass (dielectric with refraction). Used for regression testing shading correctness across engine builds.
- Physically-Based Rendering (PBR) — GGX normal distribution, Smith geometry term, Schlick Fresnel
- Reflections — Recursive ray bouncing with configurable depth
- Refractions — Snell's law with total internal reflection handling
- Soft Shadows — Stratified grid sampling over area lights with jitter
- Textures — UV-mapped texture support on all geometry types
- BVH (Bounding Volume Hierarchy) — SAH-optimized binary tree for fast ray traversal
- AABB intersection — Slab method with precomputed inverse directions
- Tile-based rendering — 16×16 tiles for better cache locality
- Progressive refinement — Binary pixel skipping on camera movement for real-time feedback
- Sphere
- Triangle (with
.objmesh loading) - Cylinder (with caps)
- RectangleXZ (infinite plane segment)
| Material | Properties |
|---|---|
Default |
Lambertian diffuse + specular |
Mirror |
Perfect reflection, low roughness |
Chrome |
Metallic mirror variant |
Glass |
Dielectric with refraction & Fresnel reflection |
- libconfig scene file format (
.cfg) - Camera with configurable FOV, position, and direction
- Interactive camera movement (keyboard-driven, real-time)
.objfile import with per-mesh transform (position, rotation, scale)
| Key | Action |
|---|---|
Z / S |
Move forward / backward |
Q / D |
Strafe left / right |
↑ / ↓ |
Move up / down |
Numpad 4 / 6 |
Rotate camera left / right (yaw) |
Numpad 8 / 5 |
Rotate camera up / down (pitch) |
main.cpp
├── ParserCmd — Command-line argument parsing
├── Scene — Scene loading from .cfg + .obj files
│ ├── Camera
│ ├── ShapeFactory — Sphere, Triangle, Cylinder, RectangleXZ
│ └── MaterialFactory — Mirror, Chrome, Glass, Default
├── BVH — SAH-based bounding volume hierarchy
├── Render — Tile renderer + progressive refinement
│ ├── FillRayBuffer — Per-pixel ray dispatch
│ ├── Shade — Recursive shading (PBR + reflection + refraction)
│ └── Graphical — SFML window, input, display
└── AABB — Axis-aligned bounding box
- C++20 or later
- SFML 3 — window, rendering, input
- libconfig++ — scene file parsing
make./raytracer --scene=scenes/my_scene.cfgOptional flags:
--log=DEBUG|INFO|WARNING|ERROR|NONE
--pathtracing=true
--sample X-sample(int)
--output "scene_render.ppm"
--server ip:port
--slave ip:port
--gui true
Scene files use libconfig syntax (.cfg):
scene:
{
fov = 60;
pos = [0.0, 2.0, -10.0];
dir = [0.0, 0.0, 1.0];
objects = (
{
shape: {
type = "sphere";
pos = [0.0, 1.0, 5.0];
radius = 1.0;
color = [255, 255, 255];
}
material: {
type = "glass";
}
},
{
shape: {
type = "rectangleXZ";
x = [-10.0, 10.0];
z = [-10.0, 10.0];
y = 0.0;
color = [200, 200, 200];
}
}
);
lights = (
{
type = "Area";
pos = [-5.0, 4.0, 0.0];
color = [1.0, 1.0, 1.0];
size = 1.5;
},
{
type = "Directional";
pos = [-20.0, 50.0, 50.0];
color = [0.8, 0.8, 0.9];
size = 0.0;
},
{
type = "Ambiant";
pos = [0.0, 10.0, 0.0];
color = [0.08, 0.08, 0.1];
size = 8.0;
}
);
obj = (
{
path = "assets/manhattan.obj";
pos = [0.0, 0.0, 0.0];
rot = [0.0, 0.0, 0.0];
scale = [1.0, 1.0, 1.0];
}
);
};The BVH is built using a full SAH sweep across the longest axis of each node. Left/right AABB prefix arrays are precomputed to evaluate all split candidates in O(n) per node. Leaf threshold is tuned to balance traversal vs intersection cost.
- NDF: GGX / Trowbridge-Reitz
- Geometry: Smith with Schlick-GGX approximation
- Fresnel: Schlick approximation
- Metallic workflow:
F0 = lerp(0.04, albedo, metallic)
A 4×4 stratified grid is jittered per sample over an area light, giving smooth penumbra. Shadow rays skip transparent objects (glass).
On camera movement, the renderer progressively fills in pixels using a binary bitmask pattern (every 4th pixel → every 2nd → full resolution) to give immediate visual feedback.



