erebus

annotate liberebus/src/brdf.cc @ 31:53a98c148bf8

- introduced SurfaceGeometry to carry all the geometric information input to BRDF sampling and evaluation functions. - made Reflectance keep an (optional) pointer to its material - simplified PhongRefl::sample_dir, with the help of SurfaceGeometry - worked around microsoft's broken std::thread implementation's deadlock on join
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 07 Jun 2014 09:14:17 +0300
parents e78f68d03ae9
children b1fc96c71bcc
rev   line source
nuclear@31 1 #include <math.h>
nuclear@30 2 #include <algorithm>
nuclear@2 3 #include "brdf.h"
nuclear@31 4 #include "material.h"
nuclear@2 5 #include "erebus_impl.h"
nuclear@2 6
nuclear@31 7 // --- class SurfaceGeometry ---
nuclear@31 8 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, VecLength st)
nuclear@31 9 : SurfaceGeometry(norm, Vector2(0, 0), st)
nuclear@31 10 {
nuclear@31 11 }
nuclear@31 12
nuclear@31 13 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector2 &uvarg, VecLength st)
nuclear@31 14 : normal(norm), uv(uvarg)
nuclear@31 15 {
nuclear@31 16 if(st == unknown) {
nuclear@31 17 normal.normalize();
nuclear@31 18 }
nuclear@31 19
nuclear@31 20 tangent = Vector3(1.0f, 0.0f, 0.0f);
nuclear@31 21 if(fabs(dot_product(normal, tangent)) - 1.0f < 1e-4f) {
nuclear@31 22 tangent = Vector3(0.0f, 0.0f, 1.0f);
nuclear@31 23 }
nuclear@31 24 Vector3 bitan = cross_product(normal, tangent);
nuclear@31 25 tangent = cross_product(bitan, normal);
nuclear@31 26 }
nuclear@31 27
nuclear@31 28 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, VecLength st)
nuclear@31 29 : SurfaceGeometry(norm, tang, Vector2(0, 0), st)
nuclear@31 30 {
nuclear@31 31 }
nuclear@31 32
nuclear@31 33 SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, const Vector2 &uvarg, VecLength st)
nuclear@31 34 : normal(norm), tangent(tang), uv(uvarg)
nuclear@31 35 {
nuclear@31 36 if(st == unknown) {
nuclear@31 37 normal.normalize();
nuclear@31 38 tangent.normalize();
nuclear@31 39 }
nuclear@31 40 }
nuclear@31 41
nuclear@31 42 Vector3 SurfaceGeometry::spherical_to_world(float theta, float phi) const
nuclear@31 43 {
nuclear@31 44 float x = cos(theta) * sin(phi);
nuclear@31 45 float y = sin(theta) * sin(phi);
nuclear@31 46 float z = cos(phi);
nuclear@31 47 return sample_to_world(Vector3(x, y, z));
nuclear@31 48 }
nuclear@31 49
nuclear@31 50 Vector3 SurfaceGeometry::sample_to_world(const Vector3 &v) const
nuclear@31 51 {
nuclear@31 52 Matrix3x3 xform;
nuclear@31 53 xform.set_column_vector(tangent, 0);
nuclear@31 54 xform.set_column_vector(cross_product(normal, tangent), 1);
nuclear@31 55 xform.set_column_vector(normal, 2);
nuclear@31 56 return v.transformed(xform);
nuclear@31 57 }
nuclear@31 58
nuclear@31 59 Vector3 SurfaceGeometry::world_to_sample(const Vector3 &v) const
nuclear@31 60 {
nuclear@31 61 Matrix3x3 xform;
nuclear@31 62 xform.set_row_vector(tangent, 0);
nuclear@31 63 xform.set_row_vector(cross_product(normal, tangent), 1);
nuclear@31 64 xform.set_row_vector(normal, 2);
nuclear@31 65 return v.transformed(xform);
nuclear@31 66 }
nuclear@31 67
nuclear@2 68 // ---- class Reflectance ----
nuclear@2 69 Reflectance::Reflectance()
nuclear@2 70 {
nuclear@31 71 mtl = 0;
nuclear@2 72 }
nuclear@2 73
nuclear@31 74 Reflectance::Reflectance(const Material *mtl)
nuclear@2 75 {
nuclear@31 76 this->mtl = mtl;
nuclear@31 77 }
nuclear@31 78
nuclear@31 79 float Reflectance::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
nuclear@31 80 {
nuclear@31 81 *indir = sample_dir(geom, outdir);
nuclear@31 82 return eval(geom, outdir, *indir);
nuclear@2 83 }
nuclear@2 84
nuclear@21 85 // --- class CompositeRefl ----
nuclear@21 86 CompositeRefl::CompositeRefl()
nuclear@21 87 {
nuclear@21 88 valid_checked = false;
nuclear@21 89 }
nuclear@21 90
nuclear@31 91 CompositeRefl::CompositeRefl(const Material *mtl)
nuclear@31 92 : Reflectance(mtl)
nuclear@31 93 {
nuclear@31 94 valid_checked = false;
nuclear@31 95 }
nuclear@31 96
nuclear@31 97 int CompositeRefl::pick_brdf(const SurfaceGeometry &geom, const Vector3 &outdir) const
nuclear@21 98 {
nuclear@21 99 if(sub_brdf.empty()) {
nuclear@21 100 return -1;
nuclear@21 101 }
nuclear@21 102
nuclear@21 103 int brdf_count = (int)sub_brdf.size();
nuclear@21 104 if(brdf_count == 1) {
nuclear@21 105 return 0;
nuclear@21 106 }
nuclear@21 107
nuclear@21 108 // construct CDF from sub-brdf probabilities
nuclear@21 109 float *cdf = (float*)alloca(brdf_count * sizeof *cdf);
nuclear@21 110 for(int i=0; i<brdf_count; i++) {
nuclear@21 111 const SubRefl &sub = sub_brdf[i];
nuclear@31 112 float w = sub.weight_func ? sub.weight_func(geom, outdir) : sub.weight;
nuclear@21 113 cdf[i] = i > 0 ? cdf[i - 1] + w : w;
nuclear@21 114 }
nuclear@21 115
nuclear@21 116 float rval = randf(0.0, cdf[brdf_count - 1]);
nuclear@21 117 for(int i=0; i<brdf_count - 1; i++) {
nuclear@21 118 if(rval <= cdf[i]) return i;
nuclear@21 119 }
nuclear@21 120 return brdf_count - 1;
nuclear@21 121 }
nuclear@21 122
nuclear@21 123 void CompositeRefl::add_brdf(Reflectance *brdf, float weight)
nuclear@21 124 {
nuclear@21 125 SubRefl sub;
nuclear@21 126 sub.brdf = brdf;
nuclear@21 127 sub.weight = weight;
nuclear@21 128 sub.weight_func = 0;
nuclear@21 129 sub_brdf.push_back(sub);
nuclear@21 130
nuclear@21 131 valid_checked = false;
nuclear@21 132 }
nuclear@21 133
nuclear@21 134 void CompositeRefl::add_brdf(Reflectance *brdf, CompReflWeightFunc weight_func)
nuclear@21 135 {
nuclear@21 136 SubRefl sub;
nuclear@21 137 sub.brdf = brdf;
nuclear@21 138 sub.weight = 0.0;
nuclear@21 139 sub.weight_func = weight_func;
nuclear@21 140 sub_brdf.push_back(sub);
nuclear@21 141
nuclear@21 142 valid_checked = false;
nuclear@21 143 }
nuclear@21 144
nuclear@21 145 bool CompositeRefl::check_valid() const
nuclear@21 146 {
nuclear@21 147 float sum = 0.0;
nuclear@21 148 for(size_t i=0; i<sub_brdf.size(); i++) {
nuclear@21 149 if(!sub_brdf[i].weight_func) {
nuclear@21 150 sum += sub_brdf[i].weight;
nuclear@21 151 }
nuclear@21 152 }
nuclear@21 153 return sum < 1.0001;
nuclear@21 154 }
nuclear@21 155
nuclear@21 156 static bool warned_composite_usage;
nuclear@21 157 static const char *composite_usage_warnstr =
nuclear@21 158 "warning: don't call CompositeRefl's sample_dir and eval separately\n"
nuclear@21 159 " it'll pick different brdfs each time! use sample instead\n";
nuclear@21 160
nuclear@31 161 Vector3 CompositeRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
nuclear@21 162 {
nuclear@21 163 if(!warned_composite_usage) {
nuclear@21 164 fputs(composite_usage_warnstr, stderr);
nuclear@21 165 warned_composite_usage = true;
nuclear@21 166 }
nuclear@31 167 int bidx = pick_brdf(geom, outdir);
nuclear@31 168 return sub_brdf[bidx].brdf->sample_dir(geom, outdir);
nuclear@21 169 }
nuclear@21 170
nuclear@31 171 float CompositeRefl::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
nuclear@21 172 {
nuclear@31 173 int bidx = pick_brdf(geom, outdir);
nuclear@31 174 return sub_brdf[bidx].brdf->sample(geom, outdir, indir);
nuclear@21 175 }
nuclear@21 176
nuclear@31 177 float CompositeRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
nuclear@21 178 {
nuclear@21 179 if(!warned_composite_usage) {
nuclear@21 180 fputs(composite_usage_warnstr, stderr);
nuclear@21 181 warned_composite_usage = true;
nuclear@21 182 }
nuclear@31 183 int bidx = pick_brdf(geom, outdir);
nuclear@31 184 return sub_brdf[bidx].brdf->eval(geom, outdir, indir);
nuclear@21 185 }
nuclear@21 186
nuclear@2 187 // --- class LambertRefl ---
nuclear@31 188 Vector3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
nuclear@2 189 {
nuclear@2 190 Vector3 dir = Vector3{randf(-1, 1), randf(-1, 1), randf(-1, 1)}.normalized();
nuclear@31 191 return dot_product(dir, geom.normal) < 0.0 ? -dir : dir;
nuclear@2 192 }
nuclear@2 193
nuclear@31 194 float LambertRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
nuclear@2 195 {
nuclear@31 196 return dot_product(geom.normal, outdir);
nuclear@2 197 }
nuclear@21 198
nuclear@21 199 // --- class MirrorRefl ---
nuclear@31 200 Vector3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
nuclear@21 201 {
nuclear@31 202 return outdir.reflection(geom.normal);
nuclear@21 203 }
nuclear@21 204
nuclear@31 205 float MirrorRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
nuclear@21 206 {
nuclear@21 207 return 1.0f;
nuclear@21 208 }
nuclear@28 209
nuclear@28 210 // --- class PhongRefl ---
nuclear@31 211 Vector3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
nuclear@28 212 {
nuclear@31 213 Vector3 refl = outdir.reflection(geom.normal).normalized();
nuclear@31 214 SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit);
nuclear@28 215
nuclear@31 216 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
nuclear@28 217
nuclear@28 218 float phi = acos(pow(randf(), 1.0 / (shininess + 1)));
nuclear@28 219 float theta = 2.0 * M_PI * randf();
nuclear@28 220
nuclear@31 221 return refl_geom.spherical_to_world(theta, phi);
nuclear@28 222 }
nuclear@28 223
nuclear@31 224 float PhongRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
nuclear@28 225 {
nuclear@31 226 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
nuclear@31 227
nuclear@31 228 Vector3 refl = outdir.reflection(geom.normal);
nuclear@28 229 float dot = std::max<float>(dot_product(indir, refl), 0.0f);
nuclear@28 230 return pow(dot, shininess);
nuclear@28 231 }