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