erebus

view liberebus/src/brdf.cc @ 28:4a0a288ffb27

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