Ray Tracing¶
Whitted-Style Ray Tracing¶
Why Ray Tracing?¶
- Rasterization couldn't handle global effects well
- Rasterization is fast, but quality is relatively low
- Ray tracing is accurate, but is very slow
Basic Ray-Tracing Algorithm¶
Ray-Surface Intersection¶
Ray Equation¶
- : point along ray
- t : "time"
- : origin
- : (normalized) direction
Plane Equation¶
- : all points on plane
- : one point on plane
- : normal vector
Ray Intersection With Plane¶
Check:
Möller–Trumbore Algorithm¶
A faster approach, giving barycentric coordinate directly
where
- ,
- ,
Cost = (1 div, 27 mul, 17 add)
Bounding Volumes¶
Axis-Aligned Bounding Box(AABB)¶
,
Using AABBs to accelerate ray tracing¶
Preprocess - Build Acceleration Grid¶
- Find bounding box
- Create grid
- Store each object in overlapping cells
Step through grid in ray traversal order
For each grid cell, test intersection with all objects stored at that cell
Grid Resolution¶
Heuristic:
- #cells = C * #objs
- in 3D
Spatial Partitions¶
KD-Tree Pre-Processing¶
Internal nodes store
- split axis: x-, y-, or z-axis
- split position: coordinate of split plane along axis
- children: pointers to child nodes
- No objects are stored in internal nodes
Leaf nodes store
- list of objects
Object Partitions & Bounding Volume Hierarchy (BVH)¶
Building BVH¶
- Find bounding box
- Recursively split set of objects in two subsets
- Recompute the bounding box of the subsets
- Stop when necessary
- Store objects in each leaf node
Internal nodes store
-
Bounding box
-
Children: pointers to child nodes
Leaf nodes store
-
Bounding box
-
List of objects
Nodes represent subset of primitives in scene
- All objects in subtree
BVH Traversal¶
Intersect(Ray ray, BVH node) {
if (ray misses node.bbox) return;
if (node is a leaf node) {
test intersection with all objs;
return closest iontersection;
}
hit1 = Intersect(ray, node.child1);
hit2 = Intersect(ray, node.child2);
return the closest of hit1, hit2;
}
Spatial vs Object Partitions¶
Spatial partition (e.g. KD-tree)
- Partition space into non-overlapping regions
- An object can be contained in multiple regions
Object partition (e.g. BVH)
- Partition set of objects into disjoint subsets
- Bounding boxes for each set may overlap in space
Basic Radiometry¶
Measurement system and units for illumination
Accurately measure the spatial properties of light
- Radiant flux, intensity, irradiance, radiance
Perform lighting calculations in a physically correct manner
Radiant Energy and Flux (Power)¶
Flux - #photons flowing through a sensor in unit time¶
Solid Angle¶
Definition: Ratio of subtended area on sphere to radius squared.
Radiant Intensity¶
Definition: The radiant (luminous) intensity is the power per unit solid angle emitted by a point light source.
Irradiance¶
Definition: The irradiance is the power per projected unit area incident on a surface point.
Lambert's Cosine Law¶
Radiance¶
Definition: The radiance (luminance) is the power per unit solid angle per projected unit area. Recall
- Irradiance: power per projected unit area
- Intensity: power per solid angle
So
- Radiance: Irradiance per solid angle
- Radiance: Intensity per projected unit area
Irradiance vs. Radiance¶
Irradiance: total power received by area
Radiance: power received by area from "direction"
Bidirectional Reflectance Distribution Function(BRDF)¶
BRDF¶
The BRDF represents how much light is reflected into each outgoing direction from each incoming direction
The Reflection Equation¶
Adding an Emission term to make it general:
Note: now, we assume that all directions are pointing outwards!
Understanding the Rendering Equation¶
Linear Operator Equation¶
Path Tracing¶
Problem¶
-
Where should the ray be reflected for glossy materials?
-
No reflections between diffuse materials?
A Simple Monte Carlo Solution¶
Compute the radiance at towards the camera
Monte Carlo Integration:
"":
PDF: (assume uniformly sampling the hemisphere)
Introducing Global Illumination¶
What if a ray hits an object? The direct illumination at the object!
Sampling the Light¶
Now we consider the radiance coming from two parts:
-
light source (direct, no need to have RR)
-
other reflectors (indirect, RR)
shade(p, wo)
// Contribution from the light source.
Uniformly sample the light at x’ (pdf_light = 1 / A)
Shoot a ray from p to x'
if the ray is not blocked in the middle
L_dir = L_i * f_r * cos θ * cos θ' / |x' - p| ^2 / pdf_light
// Contribution from other reflectors.
L_indir = 0.0
Test Russian Roulette with probability P_RR
Uniformly sample the hemisphere toward wi (pdf_hemi = 1 / 2pi)
Trace a ray r(p, wi)
if ray r hit a non-emitting object at q
L_indir = shade(q, -wi) * f_r * cos θ / pdf_hemi / P_RR
return L_dir + L_indir
Core Code¶
Vector3f Scene::castRay(const Ray &ray, int depth) const {
if (depth > maxDepth) {
return Vector3f(0.0f);
}
Intersection inter = intersect(ray);
if (!inter.happened) {
return Vector3f(0.0f);
}
if (inter.m->hasEmission()) {
return inter.m->getEmission();
}
Vector3f hitPoint = inter.coords;
Vector3f N = inter.normal;
Vector3f L_dir(0.0f);
Intersection lightInter;
float pdf_light = 0.0f;
sampleLight(lightInter, pdf_light);
Vector3f x = lightInter.coords;
Vector3f ws = (x - hitPoint).normalized();
Vector3f NN = lightInter.normal;
Ray shadowRay(hitPoint, ws);
Intersection shadowInter = intersect(shadowRay);
if (shadowInter.distance - (x - hitPoint).norm() > -0.005f) {
L_dir = lightInter.emit * inter.m->eval(ray.direction, ws, N) * dotProduct(ws, N) * dotProduct(-ws, NN) / dotProduct(x - hitPoint, x - hitPoint) / pdf_light;
}
Vector3f L_indir(0.0f);
if (get_random_float() < RussianRoulette) {
Vector3f wi = inter.m->sample(ray.direction, N).normalized();
Ray reflectionRay(hitPoint, wi);
Intersection reflectionInter = intersect(reflectionRay);
if (reflectionInter.happened && !reflectionInter.m->hasEmission()) {
float pdf_bsdf = inter.m->pdf(ray.direction, wi, N);
if (pdf_bsdf > 0) {
L_indir = castRay(reflectionRay, depth + 1) * inter.m->eval(ray.direction, wi, N) * dotProduct(wi, N) / pdf_bsdf / RussianRoulette;
}
}
}
return L_dir + L_indir;
}