erebus
view liberebus/src/brdf.cc @ 48:9971a08f4104
merged
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 24 Feb 2016 00:29:31 +0200 |
parents | d15ee526daa6 |
children |
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 Vec3 &norm, VecLength st)
9 : SurfaceGeometry(norm, Vec2(0, 0), st)
10 {
11 }
13 SurfaceGeometry::SurfaceGeometry(const Vec3 &norm, const Vec2 &uvarg, VecLength st)
14 : normal(norm), uv(uvarg)
15 {
16 if(st == unknown) {
17 normal.normalize();
18 }
20 tangent = Vec3(1.0f, 0.0f, 0.0f);
21 if(1.0 - fabs(dot_product(normal, tangent)) < 1e-4f) {
22 tangent = Vec3(0.0f, 0.0f, 1.0f);
23 }
24 Vec3 bitan = cross_product(normal, tangent);
25 tangent = cross_product(bitan, normal);
26 }
28 SurfaceGeometry::SurfaceGeometry(const Vec3 &norm, const Vec3 &tang, VecLength st)
29 : SurfaceGeometry(norm, tang, Vec2(0, 0), st)
30 {
31 }
33 SurfaceGeometry::SurfaceGeometry(const Vec3 &norm, const Vec3 &tang, const Vec2 &uvarg, VecLength st)
34 : normal(norm), tangent(tang), uv(uvarg)
35 {
36 if(st == unknown) {
37 normal.normalize();
38 tangent.normalize();
39 }
40 }
42 Vec3 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(Vec3(x, y, z));
48 }
50 Vec3 SurfaceGeometry::sample_to_world(const Vec3 &v) const
51 {
52 Mat3x3 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 Vec3 SurfaceGeometry::world_to_sample(const Vec3 &v) const
60 {
61 Mat3x3 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 Vec3 &outdir, Vec3 *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 Vec3 &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 Vec3 CompositeRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &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 Vec3 &outdir, Vec3 *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 Vec3 &outdir, const Vec3 &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 Vec3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &outdir) const
189 {
190 // TODO: write a better uniform disk sampling that doesn't rely in rejection
191 Vec3 dir;
192 do {
193 dir = Vec3(randf(-1, 1), randf(-1, 1), 0);
194 } while(dir.length_sq() > 1.0);
195 dir.z = sqrt(1.0 - (dir.x * dir.x + dir.y * dir.y));
197 return geom.sample_to_world(dir);
198 }
200 float LambertRefl::eval(const SurfaceGeometry &geom, const Vec3 &outdir, const Vec3 &indir) const
201 {
202 return dot_product(geom.normal, outdir);
203 }
205 // --- class MirrorRefl ---
206 Vec3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &outdir) const
207 {
208 return outdir.reflection(geom.normal);
209 }
211 float MirrorRefl::eval(const SurfaceGeometry &geom, const Vec3 &outdir, const Vec3 &indir) const
212 {
213 return 1.0f;
214 }
216 // --- class PhongRefl ---
217 Vec3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &outdir) const
218 {
219 Vec3 refl = outdir.reflection(geom.normal).normalized();
220 SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit);
222 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
224 float phi = acos(pow(randf(), 1.0 / (shininess + 1)));
225 float theta = 2.0 * M_PI * randf();
227 return refl_geom.spherical_to_world(theta, phi);
228 }
230 float PhongRefl::eval(const SurfaceGeometry &geom, const Vec3 &outdir, const Vec3 &indir) const
231 {
232 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
234 Vec3 refl = outdir.reflection(geom.normal);
235 float dot = std::max<float>(dot_product(indir, refl), 0.0f);
236 return pow(dot, shininess);
237 }