-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBRDF.cpp
More file actions
216 lines (194 loc) · 7.49 KB
/
BRDF.cpp
File metadata and controls
216 lines (194 loc) · 7.49 KB
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/******************************************************************************
* @file BRDF.cpp
* @author Andrés Gavín Murillo, 716358
* @author Abel Naya Forcano, 544125
* @date Enero 2020
* @coms Informática Gráfica - Trabajo 4: Path tracer
******************************************************************************/
#include "Transform.hpp"
#include "Render.hpp"
#include <cmath>
#include <cassert>
#include "BRDF.hpp"
#include "Random.hpp"
using namespace std;
/**
* Reflects a vector from another, like a two-sides mirror
* @param in input vector, like 'entering' a mirror
* @param n normal of the mirror
* @return output vector, like 'exiting' the mirror
*/
HCoord reflect(const HCoord &in, const HCoord &n) {
return norm(in - n * dot(in, n) * 2.0f);
}
/**
* Refracts a vector from another, like water
* @param in input vector, like 'entering water'
* @param n normal of water surface to air
* @return
*/
HCoord refract(const HCoord &in, HCoord n, stack<const Object *> &refractionStack,
float sceneRefractiveIndex, const Object *object) {
// https://en.wikipedia.org/wiki/Snell%27s_law#Vector_form
float c = -dot(n, in), refractionRatio;
refractionRatio = sceneRefractiveIndex / object->refractiveIndex;
if (c < 0) {
// the normal and the ray have the same direction
refractionRatio = 1.0f / refractionRatio;
c = -c;
n = -n;
}
// if (refractionStack.empty()) {
// the ray comes from the outside
// refractionRatio = sceneRefractiveIndex / object->refractiveIndex;
// refractionStack.push(object);
// } else if (object != refractionStack.top()) {
// // the ray comes from another object
// refractionRatio = refractionStack.top()->refractiveIndex / object->refractiveIndex;
// refractionStack.push(object);
// } else {
// // the ray comes from the inside, the refraction index is the opposite
// refractionRatio = object->refractiveIndex / refractionStack.top()->refractiveIndex;
// refractionStack.pop();
// }
float radicand = 1.0f - refractionRatio * refractionRatio * (1.0f - c * c);
if (radicand < 0.0f) {
// Only happens reflection because the sine of the angle of refraction is required to be greater than one.
// https://en.wikipedia.org/wiki/Snell%27s_law#Total_internal_reflection_and_critical_angle
return reflect(in, n);
} else {
return norm(in * refractionRatio + n * (refractionRatio * c - sqrt(radicand)));
}
}
/**
* Returns a new direction to follow (the 'bounce')
* @param event which type of event to use
* @param position position of bounce
* @param direction incoming direction of bounce (towards position)
* @param object where the bounce occurs
* @return new direction
*/
HCoord getNewDirection(EVENT event, const HCoord &position, const HCoord &direction, const Object &object,
stack<const Object *> &refractionStack, float sceneRefractiveIndex) {
HCoord n = normal(object.geometry, position);
switch (event) {
case REFRACTION:
return refract(direction, n, refractionStack, sceneRefractiveIndex, &object);
case REFLECTION:
return reflect(direction, n);
case PHONG_DIFFUSE: {
HCoord Z;
HCoord Y;
HCoord X;
if (dot(direction, n) < 0) {
// good side
Z = n;
} else {
// opposite side
Z = -n;
}
Y = norm(cross(Z, direction));
X = norm(cross(Y, Z));
float theta = acos(sqrt(random_zero_one()));
float phi = 2.0f * (float) M_PI * random_zero_one();
return norm(changeFromBase(X, Y, Z, position) * hVector(cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta)));
}
case PHONG_SPECULAR: {
HCoord ref = reflect(direction, n);
HCoord Z;
HCoord Y;
HCoord X;
if (abs(dot(direction, n)) < EPS) {
Z = direction;
Y = n;
X = cross(Y, Z);
} else {
X = norm(cross(direction, ref));
Y = ref;
Z = cross(Y, X);
}
float theta = acos(pow(random_zero_one(), (1.0f / (object.material.property.reflectance.s + 1.0f))));
float phi = 2.0f * (float) M_PI * random_zero_one();
return norm(changeFromBase(X, Y, Z, position) *
hVector(cos(phi) * sin(theta), cos(theta), sin(phi) * sin(theta)));
}
case DEAD:
return V_ZERO;
}
}
/**
* The 'Russian roulette'
* @param object of intersection
* @param position of intersection
* @return a random valid event
*/
EVENT getRandomEvent(const Object &object, const HCoord &position) {
// Russian roulette
// get percentages based on the max value of each color
float maxKd = getColor(object.material.property.reflectance.kd, position).max();
float maxKs = getColor(object.material.property.reflectance.ks, position).max();
float maxKdPhong = getColor(object.material.property.reflectance.kdPhong, position).max();
float maxKsPhong = getColor(object.material.property.reflectance.ksPhong, position).max();
// cap to max value
const float MAX = 0.99f;
float sum = maxKd + maxKs + maxKdPhong + maxKsPhong;
if (sum > MAX) {
maxKd *= MAX / sum;
maxKs *= MAX / sum;
maxKdPhong *= MAX / sum;
maxKsPhong *= MAX / sum;
}
float randomZeroToOne = random_zero_one();
if ((randomZeroToOne -= maxKd) < 0) {
// Perfect refraction case (delta BTDF)
return REFRACTION;
} else if ((randomZeroToOne -= maxKs) < 0) {
// Perfect specular reflectance case (delta BRDF)
return REFLECTION;
} else if ((randomZeroToOne -= maxKdPhong) < 0) {
// Perfect Phong case (Phong BRDF)
return PHONG_DIFFUSE;
} else if ((randomZeroToOne -= maxKsPhong) < 0) {
// Perfect Phong case (Phong BRDF)
return PHONG_SPECULAR;
} else {
// Path deaths
return DEAD;
}
}
/**
* The Equation
* @param event which event to compute
* @param in input vector, towards the bounce
* @param out output vector, away from the bounce
* @param position where the bounce ocurred
* @param object involved in the bounce
* @return the factor (color) of the bounce
*/
Color getBRDF(EVENT event, const HCoord &in, const HCoord &out, const HCoord &position, const Object &object) {
HCoord n = normal(object.geometry, position);
float c = abs(dot(norm(n), norm(in)));
switch (event) {
case REFRACTION: {
return getColor(object.material.property.reflectance.kd, position)
;//* c;
}
case REFLECTION: {
return getColor(object.material.property.reflectance.ks, position)
;//* c;
}
case PHONG_DIFFUSE: {
return getColor(object.material.property.reflectance.kdPhong, position) / (float) M_PI
* c;
}
case PHONG_SPECULAR: {
HCoord reflected = reflect(in, n);
return getColor(object.material.property.reflectance.ksPhong, position)
* (object.material.property.reflectance.s + 2.0f) / (2.0f * (float) M_PI)
* pow(abs(dot(reflected, out)), object.material.property.reflectance.s)
* c;
}
case DEAD:
return C_BLACK;
}
}