- Introduction
- System Architecture
- Path Tracing Algorithm
- BVH Construction and Traversal
- Ray-Primitive Intersection
- Physically-Based Materials
- Lighting Model
- GPU Data Flow, SSBOs, and Caching
- Debug Features and Dynamic Scenes
- Performance Considerations
- References
RayZen is a real-time path tracer leveraging OpenGL compute and fragment shaders for physically-based rendering. This document provides a deep technical and mathematical overview of the system, suitable for advanced users, researchers, and students.
- C++ Core: Scene setup, mesh loading, BVH construction, dynamic scene management, and OpenGL resource management.
- GLSL Shaders: Path tracing, BVH traversal, and physically-based shading.
- Data Flow: Scene data is uploaded to the GPU via Shader Storage Buffer Objects (SSBOs). BVH and triangle data are cached to disk for fast startup.
RayZen implements unidirectional path tracing:
- For each pixel, a primary ray is generated from the camera.
- Rays are traced through the scene, bouncing off surfaces according to material properties.
- At each intersection, direct and indirect lighting is computed.
- Russian roulette is used for path termination.
Mathematical Formulation: The rendering equation:
Where:
-
$L_o$ is outgoing radiance -
$L_e$ is emitted radiance -
$f_r$ is the BRDF -
$L_i$ is incoming radiance -
$n$ is the surface normal
- BVH (Bounding Volume Hierarchy): A binary tree where each node contains an AABB (Axis-Aligned Bounding Box) enclosing a subset of triangles.
- BLAS/TLAS: Bottom-level BVHs (BLAS) are built per mesh; a top-level BVH (TLAS) is built over mesh instances for instancing and dynamic scenes.
- Construction: Surface Area Heuristic (SAH) or midpoint splitting is used to partition triangles. BVH and triangle data are cached to disk for fast startup.
- Dynamic Scenes: BVH and SSBOs are rebuilt every frame for moving objects.
- Traversal: On the GPU, a stack-based traversal is implemented in GLSL. Only triangles in leaf nodes are tested for intersection.
AABB Intersection:
For a ray
If
- Triangles: Möller–Trumbore algorithm is used for efficient ray-triangle intersection.
Algorithm:
Given triangle vertices
-
$e_1 = v_1 - v_0$ ,$e_2 = v_2 - v_0$ -
$h = d \times e_2$ ,$a = e_1 \cdot h$ - If
$|a| < \epsilon$ , no intersection. -
$f = 1/a$ ,$s = o - v_0$ -
$u = f (s \cdot h)$ ,$v = f (d \cdot (s \times e_1))$ - If
$u < 0$ or$u > 1$ or$v < 0$ or$u + v > 1$ , no intersection. $t = f (e_2 \cdot (s \times e_1))$ - If
$t > \epsilon$ , intersection at$o + td$ .
- Material Parameters: Albedo, metallic, roughness, reflectivity, transparency, index of refraction (IOR).
- BRDF: Microfacet GGX for specular, Lambertian for diffuse.
- Fresnel: Schlick's approximation.
- Point and Directional Lights: Both supported, with physically-based attenuation.
- Multiple Importance Sampling: Not yet implemented, but the code is structured for future extension.
- Shadow Rays: Use BVH for occlusion checks.
- Triangles, Materials, Lights, BVH Nodes, and Indices are uploaded to the GPU as SSBOs.
- Shader Bindings:
- 0: Triangles
- 1: Materials
- 2: Lights
- 5: TLAS Nodes
- 6: TLAS Triangle Indices
- 7: BLAS Nodes
- 8: BLAS Triangle Indices
- 9: BVH Instances
- BVH/SSBO Caching: BVH and triangle data are cached to
build/bvh_cache/for fast startup. If geometry or transforms change, the cache is rebuilt. - Camera and other uniforms are sent per-frame.
- Debug Overlays: Toggle light markers, BVH wireframes, and BLAS/TLAS debug modes with keyboard shortcuts (L, B, N).
- Dynamic Scene Support: BVH and SSBOs are rebuilt every frame for moving objects and animated meshes.
- Performance Logging: Shader compile times, buffer upload times, and FPS are logged to the terminal.
-
BVH: Reduces intersection tests from
$O(N)$ to$O(\log N)$ per ray. - GPU Parallelism: Each pixel is computed independently in the fragment shader.
- Dynamic Scenes: For moving meshes, the BVH must be rebuilt and re-uploaded each frame.
- SSBO Caching: Reduces startup time by reusing previous BVH/triangle data if geometry is unchanged.
- Russian Roulette: Used to probabilistically terminate low-contribution paths.
- Physically Based Rendering: From Theory to Implementation
- Real-Time Rendering, 4th Edition
- Möller–Trumbore Intersection Algorithm
- OpenGL 4.3 Specification
For further questions or contributions, please see the main README.md or open an issue on GitHub.