erebus

view liberebus/src/brdf.cc @ 31:53a98c148bf8

- introduced SurfaceGeometry to carry all the geometric information input to BRDF sampling and evaluation functions. - made Reflectance keep an (optional) pointer to its material - simplified PhongRefl::sample_dir, with the help of SurfaceGeometry - worked around microsoft's broken std::thread implementation's deadlock on join
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 07 Jun 2014 09:14:17 +0300
parents e78f68d03ae9
children b1fc96c71bcc
line source
1 #include <math.h>
2 #include <algorithm>
3 #include "brdf.h"
4 #include "material.h"
5 #include "erebus_impl.h"
7 // --- class SurfaceGeometry ---
8 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, VecLength st)
9 : SurfaceGeometry(norm, Vector2(0, 0), st)
10 {
11 }
13 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector2 &uvarg, VecLength st)
14 : normal(norm), uv(uvarg)
15 {
16 if(st == unknown) {
17 normal.normalize();
18 }
20 tangent = Vector3(1.0f, 0.0f, 0.0f);
21 if(fabs(dot_product(normal, tangent)) - 1.0f < 1e-4f) {
22 tangent = Vector3(0.0f, 0.0f, 1.0f);
23 }
24 Vector3 bitan = cross_product(normal, tangent);
25 tangent = cross_product(bitan, normal);
26 }
28 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, VecLength st)
29 : SurfaceGeometry(norm, tang, Vector2(0, 0), st)
30 {
31 }
33 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, const Vector2 &uvarg, VecLength st)
34 : normal(norm), tangent(tang), uv(uvarg)
35 {
36 if(st == unknown) {
37 normal.normalize();
38 tangent.normalize();
39 }
40 }
42 Vector3 SurfaceGeometry::spherical_to_world(float theta, float phi) const
43 {
44 float x = cos(theta) * sin(phi);
45 float y = sin(theta) * sin(phi);
46 float z = cos(phi);
47 return sample_to_world(Vector3(x, y, z));
48 }
50 Vector3 SurfaceGeometry::sample_to_world(const Vector3 &v) const
51 {
52 Matrix3x3 xform;
53 xform.set_column_vector(tangent, 0);
54 xform.set_column_vector(cross_product(normal, tangent), 1);
55 xform.set_column_vector(normal, 2);
56 return v.transformed(xform);
57 }
59 Vector3 SurfaceGeometry::world_to_sample(const Vector3 &v) const
60 {
61 Matrix3x3 xform;
62 xform.set_row_vector(tangent, 0);
63 xform.set_row_vector(cross_product(normal, tangent), 1);
64 xform.set_row_vector(normal, 2);
65 return v.transformed(xform);
66 }
68 // ---- class Reflectance ----
69 Reflectance::Reflectance()
70 {
71 mtl = 0;
72 }
74 Reflectance::Reflectance(const Material *mtl)
75 {
76 this->mtl = mtl;
77 }
79 float Reflectance::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
80 {
81 *indir = sample_dir(geom, outdir);
82 return eval(geom, outdir, *indir);
83 }
85 // --- class CompositeRefl ----
86 CompositeRefl::CompositeRefl()
87 {
88 valid_checked = false;
89 }
91 CompositeRefl::CompositeRefl(const Material *mtl)
92 : Reflectance(mtl)
93 {
94 valid_checked = false;
95 }
97 int CompositeRefl::pick_brdf(const SurfaceGeometry &geom, const Vector3 &outdir) const
98 {
99 if(sub_brdf.empty()) {
100 return -1;
101 }
103 int brdf_count = (int)sub_brdf.size();
104 if(brdf_count == 1) {
105 return 0;
106 }
108 // construct CDF from sub-brdf probabilities
109 float *cdf = (float*)alloca(brdf_count * sizeof *cdf);
110 for(int i=0; i<brdf_count; i++) {
111 const SubRefl &sub = sub_brdf[i];
112 float w = sub.weight_func ? sub.weight_func(geom, outdir) : sub.weight;
113 cdf[i] = i > 0 ? cdf[i - 1] + w : w;
114 }
116 float rval = randf(0.0, cdf[brdf_count - 1]);
117 for(int i=0; i<brdf_count - 1; i++) {
118 if(rval <= cdf[i]) return i;
119 }
120 return brdf_count - 1;
121 }
123 void CompositeRefl::add_brdf(Reflectance *brdf, float weight)
124 {
125 SubRefl sub;
126 sub.brdf = brdf;
127 sub.weight = weight;
128 sub.weight_func = 0;
129 sub_brdf.push_back(sub);
131 valid_checked = false;
132 }
134 void CompositeRefl::add_brdf(Reflectance *brdf, CompReflWeightFunc weight_func)
135 {
136 SubRefl sub;
137 sub.brdf = brdf;
138 sub.weight = 0.0;
139 sub.weight_func = weight_func;
140 sub_brdf.push_back(sub);
142 valid_checked = false;
143 }
145 bool CompositeRefl::check_valid() const
146 {
147 float sum = 0.0;
148 for(size_t i=0; i<sub_brdf.size(); i++) {
149 if(!sub_brdf[i].weight_func) {
150 sum += sub_brdf[i].weight;
151 }
152 }
153 return sum < 1.0001;
154 }
156 static bool warned_composite_usage;
157 static const char *composite_usage_warnstr =
158 "warning: don't call CompositeRefl's sample_dir and eval separately\n"
159 " it'll pick different brdfs each time! use sample instead\n";
161 Vector3 CompositeRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
162 {
163 if(!warned_composite_usage) {
164 fputs(composite_usage_warnstr, stderr);
165 warned_composite_usage = true;
166 }
167 int bidx = pick_brdf(geom, outdir);
168 return sub_brdf[bidx].brdf->sample_dir(geom, outdir);
169 }
171 float CompositeRefl::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
172 {
173 int bidx = pick_brdf(geom, outdir);
174 return sub_brdf[bidx].brdf->sample(geom, outdir, indir);
175 }
177 float CompositeRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
178 {
179 if(!warned_composite_usage) {
180 fputs(composite_usage_warnstr, stderr);
181 warned_composite_usage = true;
182 }
183 int bidx = pick_brdf(geom, outdir);
184 return sub_brdf[bidx].brdf->eval(geom, outdir, indir);
185 }
187 // --- class LambertRefl ---
188 Vector3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
189 {
190 Vector3 dir = Vector3{randf(-1, 1), randf(-1, 1), randf(-1, 1)}.normalized();
191 return dot_product(dir, geom.normal) < 0.0 ? -dir : dir;
192 }
194 float LambertRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
195 {
196 return dot_product(geom.normal, outdir);
197 }
199 // --- class MirrorRefl ---
200 Vector3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
201 {
202 return outdir.reflection(geom.normal);
203 }
205 float MirrorRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
206 {
207 return 1.0f;
208 }
210 // --- class PhongRefl ---
211 Vector3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
212 {
213 Vector3 refl = outdir.reflection(geom.normal).normalized();
214 SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit);
216 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
218 float phi = acos(pow(randf(), 1.0 / (shininess + 1)));
219 float theta = 2.0 * M_PI * randf();
221 return refl_geom.spherical_to_world(theta, phi);
222 }
224 float PhongRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
225 {
226 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
228 Vector3 refl = outdir.reflection(geom.normal);
229 float dot = std::max<float>(dot_product(indir, refl), 0.0f);
230 return pow(dot, shininess);
231 }