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