Raytracing Pseudocode
function traceImage (scene):
for each pixel (i,j) in image
S = PointInPixel
P = CameraOrigin
d = (S - P)/|| S – P||
I(i,j) = traceRay(scene, P, d)
end for
end function
function traceRay(scene, P, d):
(t, N, mtrl) ← [Link] (P, d)
Q ! ray (P, d) evaluated at t
I = shade(mtrl, scene, Q, N, d)
R = reflectDirection(N, -d)
I ← I + [Link] ∗ traceRay(scene, Q, R)
if ray is entering object then
n_i = index_of_air
n_t = [Link]
else
n_i = [Link]
n_t = index_of_air
if (mtrl.k_t > 0 and notTIR (n_i, n_t, N, -d)) then
T = refractDirection (n_i, n_t, N, -d)
I ← I + [Link] ∗ traceRay(scene, Q, T)
end if
return I
end function
Thinking About Refraction
Remember Snell’s law?
• ηisinθi = ηtsinθt
When does light bend?
• Must account for entering and
leaving!
• How do we know if we’re entering or
leaving? (hint: all geometry has a
“front face” and a “back face”)
Calculating Refraction
N⃗
I⃗ ⃗
N cosθ ⃗
i−I
⃗
unit vector perpendicular to N in plane of I and N
N cosθi
θi
sinθt M ⃗ ⃗ ⃗
⃗
M=
( N cosθ i − I)
sinθi
θt
−cosθ N ⃗
t
−N⃗ T ⃗ = sinθt M ⃗ − cosθt N ⃗
Adapted from Computer Graphics (James Foley)
Calculating Refraction
N⃗
⃗
T =
sinθt ⃗
( N cosθi − I )⃗ − cosθt N ⃗ I⃗ ⃗
N cosθi−I
⃗
sinθi
ηi sinθt ⃗
Let ηr = = (Snell’s Law) N cosθi
ηt sinθi ⃗ ⃗
θi ⃗ ( N cosθ i − I)
sinθt M ⃗
M=
T ⃗ = (ηr cosθi − cosθt) N ⃗ − ηr I ⃗
sinθi
cosθi = N ⃗ ⋅ I ⃗
θt
−cosθ N ⃗
t
cosθt = 1 − sin 2θt = 1 − ηr2sin 2θi
−N⃗
T ⃗ = sinθt M ⃗ − cosθt N ⃗
cosθt = 1 − ηr2(1 − ( N ⃗ ⋅ I )⃗ 2)
( )
T ⃗= ηr( N ⃗ ⋅ I )⃗ − 1 − ηr2(1 − ( N ⃗ ⋅ I )⃗ 2) N ⃗ − ηr I ⃗
Determining TIR
( )
T ⃗= ηr( N ⃗ ⋅ I )⃗ − 1 − ηr2(1 − ( N ⃗ ⋅ I )⃗ 2) N ⃗ − ηr I ⃗
• TIR occurs when index of refraction of current
medium (ηi) > index of refraction of other
medium (ηt)
• going from more dense to less dense
medium
• Critical angle is value of sinϴi when sinϴt is 1
• Critical angle ϴc = sin-1(ηt/ηi)
• TIR occurs when square root of expanded
cosϴt is imaginary
function shade(mtrl, scene, Q, N, d):
I ← [Link] + mtrl. ka * scene->Ia
for each light source l do:
atten = l -> distanceAttenuation( Q ) *
l -> shadowAttenuation( scene, Q )
I ← I + atten*(diffuse term + spec term)
end for
return I
end function
function PointLight::shadowAttenuation(scene, P)
d = ([Link] - P).normalize()
(t, N, mtrl) ← [Link](P, d)
Q ← ray(t)
if Q is before the light source then:
atten = 0
else
atten = 1
end if
return atten
end function
Some Additional Notes
The raytracer skeleton code is extensive
but largely undocumented
• Taking time to look through the code
to understand what it does is
essential
• Mathematical elegance doesn’t mean
there’s a simple codebase
Passing by Reference
Many important values are passed by
reference!
• Look carefully to determine where/how
values are being updated
• Very common in C and C++
codebases
tmax and tmin
Parametric values that define the
bounding box around the scene
• Returned t values are within this range
Scene can be further subdivided for
additional intersect optimizations!
Debugging Visually: What Happened?
Casting Shadow Rays
at what t does the
ray hit an object?
Casting Shadow Rays
at what t does the
ray hit an object?
if lucky: {-1.2, 0.0}
if unlucky: {-1.2, 1e-12}
Shadow Rounding Error
Classic fix: move slightly in normal
direction before shooting shadow ray
• RAY_EPSILON provided for this
But Shadows Don’t Look Like This!
Hard vs Soft Shadows
Calculate Penumbra
Use full lighting equation or calculate
geometrically (not required for A1!)