PowerPoint Presentation
16. Practical Raytracing
Dr. Hamish Carr
COMP 5812M: Foundations of Modelling & Rendering
Building a Raytracer
Pixels – rays – geometric intersections
BSSDFs & functions for them
Monte Carlo sampling for rays
Point & area light sources
Shadow rays
Reflective/refractive impulses vs. reflections
COMP 5812M: Foundations of Modelling & Rendering
Core Path Tracer Code
main()
{ // main
for (each pixel)
set pixelRadiance to (0)
Ray r = pixel – eye
for (sample = 0 to nSamples)
pixelRadiance += pathTrace(ray, true)
} // main
COMP 5812M: Foundations of Modelling & Rendering
Path Tracing
Radiance3 pathTrace(Ray ray, bool isEyeRay)
{ // pathTrace
// initialise output sum to zero
Radiance3 output(0.0, 0.0, 0.0);
if (surfel = world->intersect(ray, dist))
{ // ray intersects something, stores it in surfel
// if eye ray hits a light, add emission
if (isEyeRay && surfel.emits)
output += surfel.material.emission;
// compute direct illumination from point & area lights
if ((!isEyeRay) || surfel.reflectsDirect)
{ // direct illumination
output += estimateDirectPointLight(surfel, ray)
output += estimateDirectAreaLight(surfel, ray)
} // direct illumination
// use recursion for impulse scattering
if ((!isEyeRay) || surfel.scattersImpulse)
output += estimateImpulseScattering(surfel, ray, isEyeRay)
// finally, add in indirect illumination
if (!(isEyeRay) || m_indirect)
output += estimateIndirectLight(surfel, ray, isEyeRay)
} // ray intersects something, stores it in surfel
// return the sum
return output;
} // pathTrace
COMP 5812M: Foundations of Modelling & Rendering
Stage I: Emitted Light
A ray might hit an area light source
but not a point source
So all area light sources are also emitters
If we hit them, add an emissive term
But only on the first bounce!
COMP 5812M: Foundations of Modelling & Rendering
Path Tracing, I
Radiance3 pathTrace(Ray ray, bool isEyeRay)
{ // pathTrace
// initialise output sum to zero
Radiance3 output(0.0, 0.0, 0.0);
if (surfel = world->intersect(ray, dist))
{ // ray intersects something, stores it in surfel
// if eye ray hits a light, add emission
if (isEyeRay && surfel.emits)
output += surfel.material.emission;
// compute direct illumination from point & area lights
if ((!isEyeRay) || surfel.reflectsDirect)
{ // direct illumination
output += estimateDirectPointLight(surfel, ray)
output += estimateDirectAreaLight(surfel, ray)
} // direct illumination
// use recursion for impulse scattering
if ((!isEyeRay) || surfel.scattersImpulse)
output += estimateImpulseScattering(surfel, ray, isEyeRay)
// finally, add in indirect illumination
if (!(isEyeRay) || m_indirect)
output += estimateIndirectLight(surfel, ray, isEyeRay)
} // ray intersects something, stores it in surfel
// return the sum
return output;
} // pathTrace
COMP 5812M: Foundations of Modelling & Rendering
Stage II: Direct Light
Direct light can come from:
point sources (an impulse light)
area sources (a distribution)
So we treat them separately
COMP 5812M: Foundations of Modelling & Rendering
Path Tracing, II
Radiance3 pathTrace(Ray ray, bool isEyeRay)
{ // pathTrace
// initialise output sum to zero
Radiance3 output(0.0, 0.0, 0.0);
if (surfel = world->intersect(ray, dist))
{ // ray intersects something, stores it in surfel
// if eye ray hits a light, add emission
if (isEyeRay && surfel.emits)
output += surfel.material.emission;
// compute direct illumination from point & area lights
if ((!isEyeRay) || surfel.reflectsDirect)
{ // direct illumination
output += estimateDirectPointLight(surfel, ray)
output += estimateDirectAreaLight(surfel, ray)
} // direct illumination
// use recursion for impulse scattering
if ((!isEyeRay) || surfel.scattersImpulse)
output += estimateImpulseScattering(surfel, ray, isEyeRay)
// finally, add in indirect illumination
if (!(isEyeRay) || m_indirect)
output += estimateIndirectLight(surfel, ray, isEyeRay)
} // ray intersects something, stores it in surfel
// return the sum
return output;
} // pathTrace
COMP 5812M: Foundations of Modelling & Rendering
Direct Point Lights, I
Radiance3 estimateDirectPointLight(SurfaceElement surfel, Ray ray)
{ // estimateDirectPointLight
Radiance3 output (0)
for (source = 0; source < nPointLights; ++source)
{ // per point light source
// cast shadow ray to determine visibility
if (lineOfSight(Ray(surfel.location,surfel.normal,pLight[source].position)))
{ // if not in shadow
// compute the incoming vector
Vector omega_i = light.position – surfel.location
float distance = omega_i.length()
// compute inverse square attenuation
Irradiance3 E_i = light.colour / (4 * PI * distance * distance)
// use BSDF to convert to light output
output += surfel.BSDF(omega_i, -ray.direction) *
E_i * max(0, omega_i.dot(surfel.normal))
} // if not in shadow
} // per point light source
return output;
} // estimateDirectPointLight
COMP 5812M: Foundations of Modelling & Rendering
Direct Point Lights, II
Radiance3 estimateDirectPointLight(SurfaceElement surfel, Ray ray)
{ // estimateDirectPointLight
Radiance3 output (0)
for (source = 0; source < nPointLights; ++source)
{ // per point light source
// cast shadow ray to determine visibility
if (lineOfSight(Ray(surfel.location, pointLight[source].position)))
{ // if not in shadow
// compute the incoming vector
Vector omega_i = light.position – surfel.location
float distance = omega_i.length()
// compute inverse square attenuation
Irradiance3 E_i = light.colour / (4 * PI * distance * distance)
// use BSDF to convert to light output
output += surfel.BSDF(omega_i, -ray.direction) *
E_i * max(0, omega_i.dot(surfel.normal))
} // if not in shadow
} // per point light source
return output;
} // estimateDirectPointLight
COMP 5812M: Foundations of Modelling & Rendering
Area Light Sources
Iterate over many points:
Generate random position on the light source
with Monte Carlo sampling
Trace rays to there
COMP 5812M: Foundations of Modelling & Rendering
Direct Area Lights
Radiance3 estimateDirectAreaLight(SurfaceElement surfel, Ray ray)
{ // estimateDirectAreaLight
Radiance3 output (0)
for (source = 0; source < nAreaLights; ++source)
{ // per point light source
// generate a random point on the surface of the light
SurfaceElement lightSurfel = aLight[source].samplePoint()
// cast shadow ray to determine visibility
if (lineOfSight(Ray(surfel.location, lightSurfel.location)))
{ // if not in shadow
// compute the incoming vector
Vector omega_i = lightSurfel.location – surfel.location
float distance = omega_i.length()
// use BSDF to convert to light output
output += surfel.BSDF(omega_i, -ray.direction) *
aLight[source].power * PI *
max(0, omega_i.dot(surfel.normal))
} // if not in shadow
} // per point light source
return output;
} // estimateDirectAreaLight
COMP 5812M: Foundations of Modelling & Rendering
Stage III: Impulse Scattering
We can treat this in the indirect computation
OR we can trace the perfect reflection as a ray
And follow it back an extra bounce
i.e. use recursion to simplify the code
COMP 5812M: Foundations of Modelling & Rendering
Path Tracing, III
Radiance3 pathTrace(Ray ray, bool isEyeRay)
{ // pathTrace
// initialise output sum to zero
Radiance3 output(0.0, 0.0, 0.0);
if (surfel = world->intersect(ray, dist))
{ // ray intersects something, stores it in surfel
// if eye ray hits a light, add emission
if (isEyeRay && surfel.emits)
output += surfel.material.emission;
// compute direct illumination from point & area lights
if ((!isEyeRay) || surfel.reflectsDirect)
{ // direct illumination
output += estimateDirectPointLight(surfel, ray)
output += estimateDirectAreaLight(surfel, ray)
} // direct illumination
// compute impulse scattering
if ((!isEyeRay) || surfel.scattersImpulse)
output += estimateImpulseScattering(surfel, ray, isEyeRay)
// finally, add in indirect illumination
if (!(isEyeRay) || m_indirect)
output += estimateIndirectLight(surfel, ray, isEyeRay)
} // ray intersects something, stores it in surfel
// return the sum
return output;
} // pathTrace
COMP 5812M: Foundations of Modelling & Rendering
Impulse Scattering
Radiance3 estimateImpulseScattering(SurfaceElement surfel, Ray ray, bool isEyeRay)
{ // estimateImpulseScattering
// call routine to do either reflection or refraction
Vector impulseDirection = surfel.getImpulseScatterDirection(-ray.direction)
Ray secondaryRay = Ray(surfel.location, impulseDirection)
// call recursively
return pathTrace(secondaryRay, isEyeRay, isEyeRay) * surfel.impulse.magnitude
} // estimateImpulseScattering
COMP 5812M: Foundations of Modelling & Rendering
Stage IV: Indirect Light
Create a bounce ray
Use extinction factor to terminate
i.e. 100% – total reflectance of surface
eventually all rays will disappear
Path trace it recursively
Set isEyeRay to false
COMP 5812M: Foundations of Modelling & Rendering
Path Tracing, IV
Radiance3 pathTrace(Ray ray, bool isEyeRay)
{ // pathTrace
// initialise output sum to zero
Radiance3 output(0.0, 0.0, 0.0);
if (surfel = world->intersect(ray, dist))
{ // ray intersects something, stores it in surfel
// if eye ray hits a light, add emission
if (isEyeRay && surfel.emits)
output += surfel.material.emission;
// compute direct illumination from point & area lights
if ((!isEyeRay) || surfel.reflectsDirect)
{ // direct illumination
output += estimateDirectPointLight(surfel, ray)
output += estimateDirectAreaLight(surfel, ray)
} // direct illumination
// compute impulse scattering
if ((!isEyeRay) || surfel.scattersImpulse)
output += estimateImpulseScattering(surfel, ray, isEyeRay)
// finally, add in indirect illumination
if (!(isEyeRay) || m_indirect)
output += estimateIndirectLight(surfel, ray, isEyeRay)
} // ray intersects something, stores it in surfel
// return the sum
return output;
} // pathTrace
COMP 5812M: Foundations of Modelling & Rendering
Indirect Light, I
Radiance3 estimateIndirectLight(SurfaceElement &surfel,
const Ray&ray, bool isEyeRay)
{ // estimateIndirectLight
// use materials to determine whether photon was extinguished
if (random() > surfel.extinction_probability())
return 0.0
else
{ // not extinguished
bounceVector = randomVector(surfel.normal)
Ray bounceRay(surfel, bounceVector)
return pathTrace(bounceRay, false)
} // not extinguished
} // estimateIndirectLight
COMP 5812M: Foundations of Modelling & Rendering