/******************************************************************/
/* This file is part of the homework assignments for CSCI-427/527 */
/* at The College of William & Mary and authored by Pieter Peers. */
/* No part of this file, whether altered or in original form, can */
/* be distributed or used outside the context of CSCI-427/527 */
/* without consent of either the College of William & Mary or */
/* Pieter Peers. */
/******************************************************************/
#include "ray_util.h"
#include "pathtracing.h"
#include "random_number.h"
//////////////////
// Constructors //
//////////////////
pathtracing::pathtracing(unsigned int samplesPerPixel, bool directOnly)
{
_samples = samplesPerPixel;
_directOnly = directOnly;
}
/////////////
// Methods //
/////////////
image pathtracing::render(const scene& s) const
{
image result(s.getCamera().width(), s.getCamera().height());
// for every pixel
for(image::size_type y=0; y < result.height(); y++)
for(image::size_type x=0; x < result.width(); x++)
{
color pixelRadiance(0.0f, 0.0f, 0.0f);
// for every sample
for(unsigned int sample = 0; sample < _samples; sample++)
{
color sampleRadiance(0.0f, 0.0f, 0.0f);
// generate random camera ray through pixel:
float px = (float)(x) + random_float(); float py = (float)(y) + random_float();
ray r = s.getCamera()(px, py);
// get initial intersection point
intersectionPoint ip = s.intersect(r);
// get radiance from directly visible *area* light sources
if(ip.isHit())
sampleRadiance += ip.emittance();
// get scene radiance
sampleRadiance += radiance(s, r, ip);
// store
pixelRadiance += sampleRadiance / (float)(_samples);
}
// store in image
result(x,y) = pixelRadiance;
}
// Done.
return result;
}
/////////////////////
// Private Methods //
/////////////////////
color pathtracing::radiance(const scene& s, const ray& r, const intersectionPoint& ip) const
{
color radiance(0.0f, 0.0f, 0.0f);
// if scene hit
if(ip.isHit())
{
// solve rendering equation
radiance = directRadiance(s, ip);
if(!_directOnly) radiance += indirectRadiance(s, ip);
}
// if not hit
else if(s.hasEnvironmentMap())
{
radiance += s.evaluateEnvironmentMap(r.direction());
}
// Done.
return radiance;
}
color pathtracing::directRadiance(const scene& s, const intersectionPoint& ip) const
{
// HW6: implement this. Compute the direct radiance incident
// at the intersection point 'ip' according to the
// rendering equation.
// Modifies: nothing
// Returns: computed direct radiance.
color result = color(0.0f);
// for every light source,
for (int l = 0; l < s.numberOfLightsources(); l++) {
// sample the light source
lightSample ls = s.getLightsource(l).emittanceAt(ip.position(), random_float(), random_float());
// check if occluded
ray shadowRay = createRay(ip, ls.directionToLight());
intersectionPoint shadowIp = s.intersect(shadowRay);
// if not occluded,
if (ls < shadowIp && ls.distance() > sqrt(EPSILON)) {
float cosine = ls.directionToLight().dot(ip.normal()) / ls.distance();
// if no division by zero,
if (ls.pdf() > EPSILON && cosine >= 0) {
// compute radiance via rendering equation for direct illumination
result += (ip.reflectance(ls.directionToLight()) // fr(x, -psi<->theta) (brdf)
* ls.emittance() // Le(y->psi)
* cosine // cos(nx, -psi)
* ls.foreshortening() // cos(ny, psi)
/ ls.distance() // r^2 (other r in cosine calculation)
/ ls.pdf() // pdf (monte-carlo integration probability)
);
}
}
}
return result;
}
color pathtracing::indirectRadiance(const scene& s, const intersectionPoint& ip) const
{
// HW6: implement this. Computed the indirect radiance incident
// at the intersection point 'ip' according to the rendering
// equation.
// Modifies: nothing
// Returns: computed indirect radiance.
color result = color(0.0f);
// if absorption falls above a random value, end recursion (russian roulette)
if (1 - ip.reflectivity() > random_float(1.0f)) { return result; }
// sample the surface
brdfSample brdf = ip.sample(random_float(), random_float());
// create exitant ray
ray exitantRay = createRay(ip, brdf.exitantDirection());
// recursively compute radiance from reflected point
color reflectedColor = radiance(s, exitantRay, s.intersect(exitantRay));
// compute cosine
float cosine = brdf.exitantDirection().dot(ip.normal());
// if no division by zero via pdf and cosines are not negative,
if (brdf.pdf() > EPSILON && cosine > EPSILON) {
// compute radiance via rendering equation for indirect illumination
result += (brdf.reflectance() // fr(x, -psi<->theta) (brdf)
* reflectedColor // Le(y->psi)
* cosine // cos(nx, -psi)
/ brdf.pdf() // pdf (monte-carlo integration probability)
/ ip.reflectivity() // russian roulette
);
}
return result;
}