This path tracer renders scenes by tracing rays from a camera through a 3D environment, using Monte Carlo sampling to approximate global illumination. It iteratively traces rays up to a specified depth, computing pixel colors by averaging surface colors, normals, reflections, and adjusting for attenuation, distance, and surface angles for realistic lighting.
This VEX path tracer generates realistic lighting by simulating ray-object interactions in a 3D scene. It begins by defining user parameters like zoom
(camera field of view), depth
(max ray bounces), samples
(Monte Carlo iterations), and attenuation
(light falloff). Rays are cast from the origin (0,0,0) with direction normalized from the point position v@P
and zoom
using set
and normalize
to create a unit vector. The intersect
function checks for ray-object collisions, returning a primitive ID (pr
) and hit position (pos
) with intrinsic polygon UVs (st
) if a hit occurs within a large distance (1e3
). On a hit, primuv
retrieves the primitive's color (Cd
) and normal (N
), and reflect computes the reflected ray direction. For each sample, nrandom('qstrat')
generates stratified random values to drive sample_hemisphere
, which creates a new ray direction within the hemisphere around the reflection vector. The code iterates up to depth
, accumulating colors from new hits, tracking distance with distance
and angles via acos(dot())
, stopping if no hit occurs. Finally, it computes the pixel color v@C
by averaging sampled colors, scaled by attenuation, angle, and normalized by distance and sample counts.
float zoom = chf('zoom');
float depth = chi('depth');
float samples = chi('samples');
float atten = chf('attenuation');
vector pos;
vector st;
vector orig = vector(0.0);
vector dir = normalize(set(v@P.x, v@P.y, -zoom));
int pr = intersect(1, orig, dir * 1e3, pos, st);
vector clr = 0.0;
float dist = 0.0;
float angle = 0.0;
if(pr >= 0){
clr = primuv(1, 'Cd', pr, st);
vector nml = primuv(1, 'N', pr, st);
vector ref = reflect(dir, nml);
vector c = clr;
vector p = pos;
vector p2;
for(int i = 0; i < samples; i++){
pos = p;
for(int k = 0; k < depth; k++){
vector2 u = nrandom('qstrat');
dir = sample_hemisphere(ref, u);
p2 = pos;
pr = intersect(1, pos + dir * 1e-5, dir * 1e2, pos, st);
if(pr >= 0){
clr += primuv(1, 'Cd', pr, st);
nml = primuv(1, 'N', pr, st);
ref = reflect(dir, nml);
dist += distance(pos, p2);
angle += acos(dot(dir, nml));
}
else{
break;
}
}
}
}
float sxd = samples * depth;
float sxd_sq_2 = sxd * sxd * 2.0;
v@C = clr * atten * angle / (sxd_sq_2 * (atten + dist / sxd));