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