summaryrefslogtreecommitdiff
path: root/hw6/src/pathtracing.cpp
blob: dc9dda185032b0b4a5bbf5f728b0aa778bc7f8a1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/******************************************************************/
/* 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;
}