136 lines
No EOL
4.9 KiB
Markdown
136 lines
No EOL
4.9 KiB
Markdown
# Cornell Box Raytracer
|
|
|
|
A pure Python raytracer that renders the Cornell Box scene with:
|
|
|
|
- **Reflections** - mirror/chrome spheres reflect surrounding geometry
|
|
- **Caustics & Refraction** - glass spheres bend light (dielectric material with Schlick approximation)
|
|
- **Multiple Bounces** - recursive path tracing up to configurable depth
|
|
- **Next Event Estimation (NEE)** - explicit light sampling for fast convergence
|
|
- **Anti-aliasing** - supersampled pixels with jittered rays
|
|
- **Multiprocessing** - parallel rendering across CPU cores
|
|
|
|
## Requirements
|
|
|
|
- Python 3.8+
|
|
- numpy
|
|
- Pillow
|
|
- rich (progress bars, colored tables)
|
|
- matplotlib (render metrics graphs)
|
|
|
|
```bash
|
|
pip install numpy Pillow rich matplotlib
|
|
```
|
|
|
|
## Features
|
|
|
|
- **Rich Progress Bar** - animated progress with ETA, speed, and row count
|
|
- **Scene Configuration Table** - formatted parameter display
|
|
- **Region Brightness Analysis** - color-coded brightness per region (left/right wall, floor, ceiling, center)
|
|
- **Render Metrics Graph** - saved as `render_metrics.png`, shows brightness convergence and render preview
|
|
- **Live Preview** - `preview.png` updated during rendering
|
|
- **Reflections** - mirror/chrome spheres reflect surrounding geometry
|
|
- **Caustics & Refraction** - glass spheres bend light (dielectric material with Schlick approximation)
|
|
- **Multiple Bounces** - recursive path tracing up to configurable depth
|
|
- **Next Event Estimation (NEE)** - explicit light sampling for fast convergence
|
|
- **Anti-aliasing** - supersampled pixels with jittered rays
|
|
- **Multiprocessing** - parallel rendering across CPU cores
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
python raytracer.py [width] [height] [samples_per_pixel] [max_bounces] [output]
|
|
```
|
|
|
|
### Examples
|
|
|
|
```bash
|
|
# Quick preview (4 min)
|
|
python raytracer.py 200 150 20 10 preview.png
|
|
|
|
# Good quality (~10 min)
|
|
python raytracer.py 400 300 50 15 cornell.png
|
|
|
|
# High quality (~40 min)
|
|
python raytracer.py 800 600 100 15 cornell_hq.png
|
|
|
|
# Custom camera and scene parameters can be set by editing the __main__ block
|
|
```
|
|
|
|
### Parameters
|
|
|
|
| Parameter | Default | Description |
|
|
|-----------|---------|-------------|
|
|
| width | 400 | Image width in pixels |
|
|
| height | 300 | Image height in pixels |
|
|
| samples_per_pixel | 50 | Anti-aliasing samples per pixel |
|
|
| max_bounces | 15 | Maximum ray bounce depth |
|
|
| output | cornell_box.png | Output PNG filename |
|
|
|
|
### Workers
|
|
|
|
Multiprocessing uses 4 workers by default. Change `workers=4` in the `render()` call.
|
|
|
|
## Scene Description
|
|
|
|
The scene is the classic **Cornell Box**:
|
|
|
|
- 5 walls (floor, ceiling, left=red, right=green, back=white) with the front face open
|
|
- A small bright light source on the left wall
|
|
- A **glass sphere** (refractive, IOR=1.5) - demonstrates refraction and caustics
|
|
- A **mirror sphere** (perfect reflection) - demonstrates reflections
|
|
- A **red diffuse sphere** - demonstrates diffuse scattering
|
|
|
|
## Camera
|
|
|
|
- Position: (278, 278, -800)
|
|
- Look-at: (278, 278, 278) (box center)
|
|
- FOV: 40°
|
|
- Focus distance: 1078
|
|
|
|
## Materials
|
|
|
|
| Material | Implementation |
|
|
|----------|---------------|
|
|
| Diffuse (Lambertian) | Cosine-distributed random scattering + NEE |
|
|
| Mirror | Perfect specular reflection |
|
|
| Dielectric | Snell refraction + Schlick fresnel + random bounce |
|
|
| Light | Emissive surface, sampled via NEE |
|
|
|
|
## Progress Indicators
|
|
|
|
The renderer uses **Rich** for terminal UI and **matplotlib** for metrics graphs:
|
|
|
|
- **Progress Bar** - animated with spinner, bar, percentage, ETA, and row count
|
|
- **Settings Table** - formatted parameter display with colored values
|
|
- **Region Brightness** - color-coded ASCII bar chart showing brightness per region
|
|
- **Metrics Graph** - saved as `render_metrics.png`, shows brightness convergence curve and live render preview
|
|
- **Preview Image** - saved as `preview.png` and updated during rendering
|
|
|
|
## Performance
|
|
|
|
Measured on 4 workers, single machine:
|
|
|
|
| Resolution | spp | Time |
|
|
|------------|-----|------|
|
|
| 200x150 | 20 | ~40s |
|
|
| 400x300 | 50 | ~10 min |
|
|
| 800x600 | 50 | ~40 min |
|
|
|
|
## How It Works
|
|
|
|
1. **Primary rays** are cast from the camera through each pixel (with supersampling jitter)
|
|
2. **Ray-object intersection** tests against planes (walls), spheres, and boxes (light)
|
|
3. **Surface scattering**:
|
|
- Diffuse: random hemisphere direction + explicit light sampling (NEE)
|
|
- Mirror: perfect reflection vector
|
|
- Dielectric: refraction with Snell's law, Schlick approximation for total internal reflection
|
|
4. **Recursive bouncing**: each scattered ray continues tracing up to `max_bounces`
|
|
5. **Radiance estimation**: `L = Le + ∫ f_r * L_i * cos θ dω` approximated via Monte Carlo integration
|
|
|
|
### Next Event Estimation
|
|
|
|
For diffuse surfaces, the direct light contribution is computed by explicitly sampling points on the light source, testing visibility via shadow rays, and weighting by the BRDF and light PDF. This dramatically reduces variance compared to pure path tracing.
|
|
|
|
## Output
|
|
|
|
Renders save as PNG with gamma correction (sqrt/gamma 2.0 applied). |