erebus

annotate 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
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 }