summaryrefslogblamecommitdiff
path: root/hw6/src/pathtracing.cpp
blob: dc9dda185032b0b4a5bbf5f728b0aa778bc7f8a1 (plain) (tree)














































































































































































                                                                                                    
/******************************************************************/
/* 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;
}