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