erebus

view liberebus/src/brdf.cc @ 30:e78f68d03ae9

another missing <algorithm> include broke windows build
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 07 Jun 2014 06:10:21 +0300
parents 4a0a288ffb27
children 53a98c148bf8
line source
1 #include <algorithm>
2 #include "brdf.h"
3 #include "erebus_impl.h"
5 // ---- class Reflectance ----
6 Reflectance::Reflectance()
7 {
8 }
10 float Reflectance::sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const
11 {
12 *indir = sample_dir(norm, outdir);
13 return eval(norm, outdir, *indir);
14 }
16 // --- class CompositeRefl ----
17 CompositeRefl::CompositeRefl()
18 {
19 valid_checked = false;
20 }
22 int CompositeRefl::pick_brdf(const Vector3 &norm, const Vector3 &outdir) const
23 {
24 if(sub_brdf.empty()) {
25 return -1;
26 }
28 int brdf_count = (int)sub_brdf.size();
29 if(brdf_count == 1) {
30 return 0;
31 }
33 // construct CDF from sub-brdf probabilities
34 float *cdf = (float*)alloca(brdf_count * sizeof *cdf);
35 for(int i=0; i<brdf_count; i++) {
36 const SubRefl &sub = sub_brdf[i];
37 float w = sub.weight_func ? sub.weight_func(norm, outdir) : sub.weight;
38 cdf[i] = i > 0 ? cdf[i - 1] + w : w;
39 }
41 float rval = randf(0.0, cdf[brdf_count - 1]);
42 for(int i=0; i<brdf_count - 1; i++) {
43 if(rval <= cdf[i]) return i;
44 }
45 return brdf_count - 1;
46 }
48 void CompositeRefl::add_brdf(Reflectance *brdf, float weight)
49 {
50 SubRefl sub;
51 sub.brdf = brdf;
52 sub.weight = weight;
53 sub.weight_func = 0;
54 sub_brdf.push_back(sub);
56 valid_checked = false;
57 }
59 void CompositeRefl::add_brdf(Reflectance *brdf, CompReflWeightFunc weight_func)
60 {
61 SubRefl sub;
62 sub.brdf = brdf;
63 sub.weight = 0.0;
64 sub.weight_func = weight_func;
65 sub_brdf.push_back(sub);
67 valid_checked = false;
68 }
70 bool CompositeRefl::check_valid() const
71 {
72 float sum = 0.0;
73 for(size_t i=0; i<sub_brdf.size(); i++) {
74 if(!sub_brdf[i].weight_func) {
75 sum += sub_brdf[i].weight;
76 }
77 }
78 return sum < 1.0001;
79 }
81 static bool warned_composite_usage;
82 static const char *composite_usage_warnstr =
83 "warning: don't call CompositeRefl's sample_dir and eval separately\n"
84 " it'll pick different brdfs each time! use sample instead\n";
86 Vector3 CompositeRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
87 {
88 if(!warned_composite_usage) {
89 fputs(composite_usage_warnstr, stderr);
90 warned_composite_usage = true;
91 }
92 int bidx = pick_brdf(norm, outdir);
93 return sub_brdf[bidx].brdf->sample_dir(norm, outdir);
94 }
96 float CompositeRefl::sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const
97 {
98 int bidx = pick_brdf(norm, outdir);
99 return sub_brdf[bidx].brdf->sample(norm, outdir, indir);
100 }
102 float CompositeRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
103 {
104 if(!warned_composite_usage) {
105 fputs(composite_usage_warnstr, stderr);
106 warned_composite_usage = true;
107 }
108 int bidx = pick_brdf(norm, outdir);
109 return sub_brdf[bidx].brdf->eval(norm, outdir, indir);
110 }
112 // --- class LambertRefl ---
113 Vector3 LambertRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
114 {
115 Vector3 dir = Vector3{randf(-1, 1), randf(-1, 1), randf(-1, 1)}.normalized();
116 return dot_product(dir, norm) < 0.0 ? -dir : dir;
117 }
119 float LambertRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
120 {
121 return dot_product(norm, outdir);
122 }
124 // --- class MirrorRefl ---
125 Vector3 MirrorRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
126 {
127 return outdir.reflection(norm);
128 }
130 float MirrorRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
131 {
132 return 1.0f;
133 }
135 // --- class PhongRefl ---
136 PhongRefl::PhongRefl()
137 {
138 shininess = 42.0;
139 }
141 Vector3 PhongRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
142 {
143 // construct orthonormal basis with k being the reflection dir
144 Vector3 refl = outdir.reflection(norm).normalized();
145 Vector3 up = Vector3(0, 0, 1);
146 if(fabs(dot_product(refl, up)) - 1.0 < 0.001) {
147 up = Vector3(0, 1, 0);
148 }
149 Vector3 right = cross_product(up, refl);
150 up = cross_product(refl, right);
152 // construct the matrix transposed to move the sample from reflection
153 // space to world space
154 Matrix3x3 xform;
155 xform.set_column_vector(right, 0);
156 xform.set_column_vector(up, 1);
157 xform.set_column_vector(refl, 2);
159 float phi = acos(pow(randf(), 1.0 / (shininess + 1)));
160 float theta = 2.0 * M_PI * randf();
162 Vector3 v;
163 v.x = cos(theta) * sin(phi);
164 v.y = sin(theta) * sin(phi);
165 v.z = cos(phi);
166 v.transform(xform);
167 return v;
168 }
170 float PhongRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
171 {
172 Vector3 refl = outdir.reflection(norm);
173 float dot = std::max<float>(dot_product(indir, refl), 0.0f);
174 return pow(dot, shininess);
175 }