rev |
line source |
nuclear@31
|
1 #include <math.h>
|
nuclear@30
|
2 #include <algorithm>
|
nuclear@2
|
3 #include "brdf.h"
|
nuclear@31
|
4 #include "material.h"
|
nuclear@2
|
5 #include "erebus_impl.h"
|
nuclear@2
|
6
|
nuclear@31
|
7 // --- class SurfaceGeometry ---
|
nuclear@31
|
8 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, VecLength st)
|
nuclear@31
|
9 : SurfaceGeometry(norm, Vector2(0, 0), st)
|
nuclear@31
|
10 {
|
nuclear@31
|
11 }
|
nuclear@31
|
12
|
nuclear@31
|
13 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector2 &uvarg, VecLength st)
|
nuclear@31
|
14 : normal(norm), uv(uvarg)
|
nuclear@31
|
15 {
|
nuclear@31
|
16 if(st == unknown) {
|
nuclear@31
|
17 normal.normalize();
|
nuclear@31
|
18 }
|
nuclear@31
|
19
|
nuclear@31
|
20 tangent = Vector3(1.0f, 0.0f, 0.0f);
|
nuclear@34
|
21 if(1.0 - fabs(dot_product(normal, tangent)) < 1e-4f) {
|
nuclear@31
|
22 tangent = Vector3(0.0f, 0.0f, 1.0f);
|
nuclear@31
|
23 }
|
nuclear@31
|
24 Vector3 bitan = cross_product(normal, tangent);
|
nuclear@31
|
25 tangent = cross_product(bitan, normal);
|
nuclear@31
|
26 }
|
nuclear@31
|
27
|
nuclear@31
|
28 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, VecLength st)
|
nuclear@31
|
29 : SurfaceGeometry(norm, tang, Vector2(0, 0), st)
|
nuclear@31
|
30 {
|
nuclear@31
|
31 }
|
nuclear@31
|
32
|
nuclear@31
|
33 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, const Vector2 &uvarg, VecLength st)
|
nuclear@31
|
34 : normal(norm), tangent(tang), uv(uvarg)
|
nuclear@31
|
35 {
|
nuclear@31
|
36 if(st == unknown) {
|
nuclear@31
|
37 normal.normalize();
|
nuclear@31
|
38 tangent.normalize();
|
nuclear@31
|
39 }
|
nuclear@31
|
40 }
|
nuclear@31
|
41
|
nuclear@31
|
42 Vector3 SurfaceGeometry::spherical_to_world(float theta, float phi) const
|
nuclear@31
|
43 {
|
nuclear@31
|
44 float x = cos(theta) * sin(phi);
|
nuclear@31
|
45 float y = sin(theta) * sin(phi);
|
nuclear@31
|
46 float z = cos(phi);
|
nuclear@31
|
47 return sample_to_world(Vector3(x, y, z));
|
nuclear@31
|
48 }
|
nuclear@31
|
49
|
nuclear@31
|
50 Vector3 SurfaceGeometry::sample_to_world(const Vector3 &v) const
|
nuclear@31
|
51 {
|
nuclear@31
|
52 Matrix3x3 xform;
|
nuclear@31
|
53 xform.set_column_vector(tangent, 0);
|
nuclear@31
|
54 xform.set_column_vector(cross_product(normal, tangent), 1);
|
nuclear@31
|
55 xform.set_column_vector(normal, 2);
|
nuclear@31
|
56 return v.transformed(xform);
|
nuclear@31
|
57 }
|
nuclear@31
|
58
|
nuclear@31
|
59 Vector3 SurfaceGeometry::world_to_sample(const Vector3 &v) const
|
nuclear@31
|
60 {
|
nuclear@31
|
61 Matrix3x3 xform;
|
nuclear@31
|
62 xform.set_row_vector(tangent, 0);
|
nuclear@31
|
63 xform.set_row_vector(cross_product(normal, tangent), 1);
|
nuclear@31
|
64 xform.set_row_vector(normal, 2);
|
nuclear@31
|
65 return v.transformed(xform);
|
nuclear@31
|
66 }
|
nuclear@31
|
67
|
nuclear@2
|
68 // ---- class Reflectance ----
|
nuclear@2
|
69 Reflectance::Reflectance()
|
nuclear@2
|
70 {
|
nuclear@31
|
71 mtl = 0;
|
nuclear@2
|
72 }
|
nuclear@2
|
73
|
nuclear@31
|
74 Reflectance::Reflectance(const Material *mtl)
|
nuclear@2
|
75 {
|
nuclear@31
|
76 this->mtl = mtl;
|
nuclear@31
|
77 }
|
nuclear@31
|
78
|
nuclear@31
|
79 float Reflectance::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
|
nuclear@31
|
80 {
|
nuclear@31
|
81 *indir = sample_dir(geom, outdir);
|
nuclear@31
|
82 return eval(geom, outdir, *indir);
|
nuclear@2
|
83 }
|
nuclear@2
|
84
|
nuclear@21
|
85 // --- class CompositeRefl ----
|
nuclear@21
|
86 CompositeRefl::CompositeRefl()
|
nuclear@21
|
87 {
|
nuclear@21
|
88 valid_checked = false;
|
nuclear@21
|
89 }
|
nuclear@21
|
90
|
nuclear@31
|
91 CompositeRefl::CompositeRefl(const Material *mtl)
|
nuclear@31
|
92 : Reflectance(mtl)
|
nuclear@31
|
93 {
|
nuclear@31
|
94 valid_checked = false;
|
nuclear@31
|
95 }
|
nuclear@31
|
96
|
nuclear@31
|
97 int CompositeRefl::pick_brdf(const SurfaceGeometry &geom, const Vector3 &outdir) const
|
nuclear@21
|
98 {
|
nuclear@21
|
99 if(sub_brdf.empty()) {
|
nuclear@21
|
100 return -1;
|
nuclear@21
|
101 }
|
nuclear@21
|
102
|
nuclear@21
|
103 int brdf_count = (int)sub_brdf.size();
|
nuclear@21
|
104 if(brdf_count == 1) {
|
nuclear@21
|
105 return 0;
|
nuclear@21
|
106 }
|
nuclear@21
|
107
|
nuclear@21
|
108 // construct CDF from sub-brdf probabilities
|
nuclear@21
|
109 float *cdf = (float*)alloca(brdf_count * sizeof *cdf);
|
nuclear@21
|
110 for(int i=0; i<brdf_count; i++) {
|
nuclear@21
|
111 const SubRefl &sub = sub_brdf[i];
|
nuclear@31
|
112 float w = sub.weight_func ? sub.weight_func(geom, outdir) : sub.weight;
|
nuclear@21
|
113 cdf[i] = i > 0 ? cdf[i - 1] + w : w;
|
nuclear@21
|
114 }
|
nuclear@21
|
115
|
nuclear@21
|
116 float rval = randf(0.0, cdf[brdf_count - 1]);
|
nuclear@21
|
117 for(int i=0; i<brdf_count - 1; i++) {
|
nuclear@21
|
118 if(rval <= cdf[i]) return i;
|
nuclear@21
|
119 }
|
nuclear@21
|
120 return brdf_count - 1;
|
nuclear@21
|
121 }
|
nuclear@21
|
122
|
nuclear@21
|
123 void CompositeRefl::add_brdf(Reflectance *brdf, float weight)
|
nuclear@21
|
124 {
|
nuclear@21
|
125 SubRefl sub;
|
nuclear@21
|
126 sub.brdf = brdf;
|
nuclear@21
|
127 sub.weight = weight;
|
nuclear@21
|
128 sub.weight_func = 0;
|
nuclear@21
|
129 sub_brdf.push_back(sub);
|
nuclear@21
|
130
|
nuclear@21
|
131 valid_checked = false;
|
nuclear@21
|
132 }
|
nuclear@21
|
133
|
nuclear@21
|
134 void CompositeRefl::add_brdf(Reflectance *brdf, CompReflWeightFunc weight_func)
|
nuclear@21
|
135 {
|
nuclear@21
|
136 SubRefl sub;
|
nuclear@21
|
137 sub.brdf = brdf;
|
nuclear@21
|
138 sub.weight = 0.0;
|
nuclear@21
|
139 sub.weight_func = weight_func;
|
nuclear@21
|
140 sub_brdf.push_back(sub);
|
nuclear@21
|
141
|
nuclear@21
|
142 valid_checked = false;
|
nuclear@21
|
143 }
|
nuclear@21
|
144
|
nuclear@21
|
145 bool CompositeRefl::check_valid() const
|
nuclear@21
|
146 {
|
nuclear@21
|
147 float sum = 0.0;
|
nuclear@21
|
148 for(size_t i=0; i<sub_brdf.size(); i++) {
|
nuclear@21
|
149 if(!sub_brdf[i].weight_func) {
|
nuclear@21
|
150 sum += sub_brdf[i].weight;
|
nuclear@21
|
151 }
|
nuclear@21
|
152 }
|
nuclear@21
|
153 return sum < 1.0001;
|
nuclear@21
|
154 }
|
nuclear@21
|
155
|
nuclear@21
|
156 static bool warned_composite_usage;
|
nuclear@21
|
157 static const char *composite_usage_warnstr =
|
nuclear@21
|
158 "warning: don't call CompositeRefl's sample_dir and eval separately\n"
|
nuclear@21
|
159 " it'll pick different brdfs each time! use sample instead\n";
|
nuclear@21
|
160
|
nuclear@31
|
161 Vector3 CompositeRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
|
nuclear@21
|
162 {
|
nuclear@21
|
163 if(!warned_composite_usage) {
|
nuclear@21
|
164 fputs(composite_usage_warnstr, stderr);
|
nuclear@21
|
165 warned_composite_usage = true;
|
nuclear@21
|
166 }
|
nuclear@31
|
167 int bidx = pick_brdf(geom, outdir);
|
nuclear@31
|
168 return sub_brdf[bidx].brdf->sample_dir(geom, outdir);
|
nuclear@21
|
169 }
|
nuclear@21
|
170
|
nuclear@31
|
171 float CompositeRefl::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
|
nuclear@21
|
172 {
|
nuclear@31
|
173 int bidx = pick_brdf(geom, outdir);
|
nuclear@31
|
174 return sub_brdf[bidx].brdf->sample(geom, outdir, indir);
|
nuclear@21
|
175 }
|
nuclear@21
|
176
|
nuclear@31
|
177 float CompositeRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
|
nuclear@21
|
178 {
|
nuclear@21
|
179 if(!warned_composite_usage) {
|
nuclear@21
|
180 fputs(composite_usage_warnstr, stderr);
|
nuclear@21
|
181 warned_composite_usage = true;
|
nuclear@21
|
182 }
|
nuclear@31
|
183 int bidx = pick_brdf(geom, outdir);
|
nuclear@31
|
184 return sub_brdf[bidx].brdf->eval(geom, outdir, indir);
|
nuclear@21
|
185 }
|
nuclear@21
|
186
|
nuclear@2
|
187 // --- class LambertRefl ---
|
nuclear@31
|
188 Vector3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
|
nuclear@2
|
189 {
|
nuclear@32
|
190 // TODO: write a better uniform disk sampling that doesn't rely in rejection
|
nuclear@32
|
191 Vector3 dir;
|
nuclear@32
|
192 do {
|
nuclear@32
|
193 dir = Vector3(randf(-1, 1), randf(-1, 1), 0);
|
nuclear@32
|
194 } while(dir.length_sq() > 1.0);
|
nuclear@32
|
195 dir.z = sqrt(1.0 - (dir.x * dir.x + dir.y * dir.y));
|
nuclear@32
|
196
|
nuclear@32
|
197 return geom.sample_to_world(dir);
|
nuclear@2
|
198 }
|
nuclear@2
|
199
|
nuclear@31
|
200 float LambertRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
|
nuclear@2
|
201 {
|
nuclear@31
|
202 return dot_product(geom.normal, outdir);
|
nuclear@2
|
203 }
|
nuclear@21
|
204
|
nuclear@21
|
205 // --- class MirrorRefl ---
|
nuclear@31
|
206 Vector3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
|
nuclear@21
|
207 {
|
nuclear@31
|
208 return outdir.reflection(geom.normal);
|
nuclear@21
|
209 }
|
nuclear@21
|
210
|
nuclear@31
|
211 float MirrorRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
|
nuclear@21
|
212 {
|
nuclear@21
|
213 return 1.0f;
|
nuclear@21
|
214 }
|
nuclear@28
|
215
|
nuclear@28
|
216 // --- class PhongRefl ---
|
nuclear@31
|
217 Vector3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
|
nuclear@28
|
218 {
|
nuclear@31
|
219 Vector3 refl = outdir.reflection(geom.normal).normalized();
|
nuclear@31
|
220 SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit);
|
nuclear@28
|
221
|
nuclear@31
|
222 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
|
nuclear@28
|
223
|
nuclear@28
|
224 float phi = acos(pow(randf(), 1.0 / (shininess + 1)));
|
nuclear@28
|
225 float theta = 2.0 * M_PI * randf();
|
nuclear@28
|
226
|
nuclear@31
|
227 return refl_geom.spherical_to_world(theta, phi);
|
nuclear@28
|
228 }
|
nuclear@28
|
229
|
nuclear@31
|
230 float PhongRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
|
nuclear@28
|
231 {
|
nuclear@31
|
232 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
|
nuclear@31
|
233
|
nuclear@31
|
234 Vector3 refl = outdir.reflection(geom.normal);
|
nuclear@28
|
235 float dot = std::max<float>(dot_product(indir, refl), 0.0f);
|
nuclear@28
|
236 return pow(dot, shininess);
|
nuclear@28
|
237 }
|