erebus

annotate liberebus/src/brdf.cc @ 48:9971a08f4104

merged
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 24 Feb 2016 00:29:31 +0200
parents d15ee526daa6
children
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@46 8 SurfaceGeometry::SurfaceGeometry(const Vec3 &norm, VecLength st)
nuclear@46 9 : SurfaceGeometry(norm, Vec2(0, 0), st)
nuclear@31 10 {
nuclear@31 11 }
nuclear@31 12
nuclear@46 13 SurfaceGeometry::SurfaceGeometry(const Vec3 &norm, const Vec2 &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@46 20 tangent = Vec3(1.0f, 0.0f, 0.0f);
nuclear@34 21 if(1.0 - fabs(dot_product(normal, tangent)) < 1e-4f) {
nuclear@46 22 tangent = Vec3(0.0f, 0.0f, 1.0f);
nuclear@31 23 }
nuclear@46 24 Vec3 bitan = cross_product(normal, tangent);
nuclear@31 25 tangent = cross_product(bitan, normal);
nuclear@31 26 }
nuclear@31 27
nuclear@46 28 SurfaceGeometry::SurfaceGeometry(const Vec3 &norm, const Vec3 &tang, VecLength st)
nuclear@46 29 : SurfaceGeometry(norm, tang, Vec2(0, 0), st)
nuclear@31 30 {
nuclear@31 31 }
nuclear@31 32
nuclear@46 33 SurfaceGeometry::SurfaceGeometry(const Vec3 &norm, const Vec3 &tang, const Vec2 &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@46 42 Vec3 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@46 47 return sample_to_world(Vec3(x, y, z));
nuclear@31 48 }
nuclear@31 49
nuclear@46 50 Vec3 SurfaceGeometry::sample_to_world(const Vec3 &v) const
nuclear@31 51 {
nuclear@46 52 Mat3x3 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@46 59 Vec3 SurfaceGeometry::world_to_sample(const Vec3 &v) const
nuclear@31 60 {
nuclear@46 61 Mat3x3 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@46 79 float Reflectance::sample(const SurfaceGeometry &geom, const Vec3 &outdir, Vec3 *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@46 97 int CompositeRefl::pick_brdf(const SurfaceGeometry &geom, const Vec3 &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@46 161 Vec3 CompositeRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &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@46 171 float CompositeRefl::sample(const SurfaceGeometry &geom, const Vec3 &outdir, Vec3 *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@46 177 float CompositeRefl::eval(const SurfaceGeometry &geom, const Vec3 &outdir, const Vec3 &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@46 188 Vec3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &outdir) const
nuclear@2 189 {
nuclear@32 190 // TODO: write a better uniform disk sampling that doesn't rely in rejection
nuclear@46 191 Vec3 dir;
nuclear@32 192 do {
nuclear@46 193 dir = Vec3(randf(-1, 1), randf(-1, 1), 0);
nuclear@32 194 } while(dir.length_sq() > 1.0);
nuclear@32 195 dir.z = sqrt(1.0 - (dir.x * dir.x + dir.y * dir.y));
nuclear@32 196
nuclear@32 197 return geom.sample_to_world(dir);
nuclear@2 198 }
nuclear@2 199
nuclear@46 200 float LambertRefl::eval(const SurfaceGeometry &geom, const Vec3 &outdir, const Vec3 &indir) const
nuclear@2 201 {
nuclear@31 202 return dot_product(geom.normal, outdir);
nuclear@2 203 }
nuclear@21 204
nuclear@21 205 // --- class MirrorRefl ---
nuclear@46 206 Vec3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &outdir) const
nuclear@21 207 {
nuclear@31 208 return outdir.reflection(geom.normal);
nuclear@21 209 }
nuclear@21 210
nuclear@46 211 float MirrorRefl::eval(const SurfaceGeometry &geom, const Vec3 &outdir, const Vec3 &indir) const
nuclear@21 212 {
nuclear@21 213 return 1.0f;
nuclear@21 214 }
nuclear@28 215
nuclear@28 216 // --- class PhongRefl ---
nuclear@46 217 Vec3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vec3 &outdir) const
nuclear@28 218 {
nuclear@46 219 Vec3 refl = outdir.reflection(geom.normal).normalized();
nuclear@31 220 SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit);
nuclear@28 221
nuclear@31 222 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
nuclear@28 223
nuclear@28 224 float phi = acos(pow(randf(), 1.0 / (shininess + 1)));
nuclear@28 225 float theta = 2.0 * M_PI * randf();
nuclear@28 226
nuclear@31 227 return refl_geom.spherical_to_world(theta, phi);
nuclear@28 228 }
nuclear@28 229
nuclear@46 230 float PhongRefl::eval(const SurfaceGeometry &geom, const Vec3 &outdir, const Vec3 &indir) const
nuclear@28 231 {
nuclear@31 232 float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
nuclear@31 233
nuclear@46 234 Vec3 refl = outdir.reflection(geom.normal);
nuclear@28 235 float dot = std::max<float>(dot_product(indir, refl), 0.0f);
nuclear@28 236 return pow(dot, shininess);
nuclear@28 237 }