erebus
diff 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 |
line diff
1.1 --- a/liberebus/src/brdf.cc Sat Jun 07 06:10:21 2014 +0300 1.2 +++ b/liberebus/src/brdf.cc Sat Jun 07 09:14:17 2014 +0300 1.3 @@ -1,16 +1,85 @@ 1.4 +#include <math.h> 1.5 #include <algorithm> 1.6 #include "brdf.h" 1.7 +#include "material.h" 1.8 #include "erebus_impl.h" 1.9 1.10 +// --- class SurfaceGeometry --- 1.11 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, VecLength st) 1.12 + : SurfaceGeometry(norm, Vector2(0, 0), st) 1.13 +{ 1.14 +} 1.15 + 1.16 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector2 &uvarg, VecLength st) 1.17 + : normal(norm), uv(uvarg) 1.18 +{ 1.19 + if(st == unknown) { 1.20 + normal.normalize(); 1.21 + } 1.22 + 1.23 + tangent = Vector3(1.0f, 0.0f, 0.0f); 1.24 + if(fabs(dot_product(normal, tangent)) - 1.0f < 1e-4f) { 1.25 + tangent = Vector3(0.0f, 0.0f, 1.0f); 1.26 + } 1.27 + Vector3 bitan = cross_product(normal, tangent); 1.28 + tangent = cross_product(bitan, normal); 1.29 +} 1.30 + 1.31 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, VecLength st) 1.32 + : SurfaceGeometry(norm, tang, Vector2(0, 0), st) 1.33 +{ 1.34 +} 1.35 + 1.36 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, const Vector2 &uvarg, VecLength st) 1.37 + : normal(norm), tangent(tang), uv(uvarg) 1.38 +{ 1.39 + if(st == unknown) { 1.40 + normal.normalize(); 1.41 + tangent.normalize(); 1.42 + } 1.43 +} 1.44 + 1.45 +Vector3 SurfaceGeometry::spherical_to_world(float theta, float phi) const 1.46 +{ 1.47 + float x = cos(theta) * sin(phi); 1.48 + float y = sin(theta) * sin(phi); 1.49 + float z = cos(phi); 1.50 + return sample_to_world(Vector3(x, y, z)); 1.51 +} 1.52 + 1.53 +Vector3 SurfaceGeometry::sample_to_world(const Vector3 &v) const 1.54 +{ 1.55 + Matrix3x3 xform; 1.56 + xform.set_column_vector(tangent, 0); 1.57 + xform.set_column_vector(cross_product(normal, tangent), 1); 1.58 + xform.set_column_vector(normal, 2); 1.59 + return v.transformed(xform); 1.60 +} 1.61 + 1.62 +Vector3 SurfaceGeometry::world_to_sample(const Vector3 &v) const 1.63 +{ 1.64 + Matrix3x3 xform; 1.65 + xform.set_row_vector(tangent, 0); 1.66 + xform.set_row_vector(cross_product(normal, tangent), 1); 1.67 + xform.set_row_vector(normal, 2); 1.68 + return v.transformed(xform); 1.69 +} 1.70 + 1.71 // ---- class Reflectance ---- 1.72 Reflectance::Reflectance() 1.73 { 1.74 + mtl = 0; 1.75 } 1.76 1.77 -float Reflectance::sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const 1.78 +Reflectance::Reflectance(const Material *mtl) 1.79 { 1.80 - *indir = sample_dir(norm, outdir); 1.81 - return eval(norm, outdir, *indir); 1.82 + this->mtl = mtl; 1.83 +} 1.84 + 1.85 +float Reflectance::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const 1.86 +{ 1.87 + *indir = sample_dir(geom, outdir); 1.88 + return eval(geom, outdir, *indir); 1.89 } 1.90 1.91 // --- class CompositeRefl ---- 1.92 @@ -19,7 +88,13 @@ 1.93 valid_checked = false; 1.94 } 1.95 1.96 -int CompositeRefl::pick_brdf(const Vector3 &norm, const Vector3 &outdir) const 1.97 +CompositeRefl::CompositeRefl(const Material *mtl) 1.98 + : Reflectance(mtl) 1.99 +{ 1.100 + valid_checked = false; 1.101 +} 1.102 + 1.103 +int CompositeRefl::pick_brdf(const SurfaceGeometry &geom, const Vector3 &outdir) const 1.104 { 1.105 if(sub_brdf.empty()) { 1.106 return -1; 1.107 @@ -34,7 +109,7 @@ 1.108 float *cdf = (float*)alloca(brdf_count * sizeof *cdf); 1.109 for(int i=0; i<brdf_count; i++) { 1.110 const SubRefl &sub = sub_brdf[i]; 1.111 - float w = sub.weight_func ? sub.weight_func(norm, outdir) : sub.weight; 1.112 + float w = sub.weight_func ? sub.weight_func(geom, outdir) : sub.weight; 1.113 cdf[i] = i > 0 ? cdf[i - 1] + w : w; 1.114 } 1.115 1.116 @@ -83,93 +158,74 @@ 1.117 "warning: don't call CompositeRefl's sample_dir and eval separately\n" 1.118 " it'll pick different brdfs each time! use sample instead\n"; 1.119 1.120 -Vector3 CompositeRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const 1.121 +Vector3 CompositeRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const 1.122 { 1.123 if(!warned_composite_usage) { 1.124 fputs(composite_usage_warnstr, stderr); 1.125 warned_composite_usage = true; 1.126 } 1.127 - int bidx = pick_brdf(norm, outdir); 1.128 - return sub_brdf[bidx].brdf->sample_dir(norm, outdir); 1.129 + int bidx = pick_brdf(geom, outdir); 1.130 + return sub_brdf[bidx].brdf->sample_dir(geom, outdir); 1.131 } 1.132 1.133 -float CompositeRefl::sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const 1.134 +float CompositeRefl::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const 1.135 { 1.136 - int bidx = pick_brdf(norm, outdir); 1.137 - return sub_brdf[bidx].brdf->sample(norm, outdir, indir); 1.138 + int bidx = pick_brdf(geom, outdir); 1.139 + return sub_brdf[bidx].brdf->sample(geom, outdir, indir); 1.140 } 1.141 1.142 -float CompositeRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const 1.143 +float CompositeRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const 1.144 { 1.145 if(!warned_composite_usage) { 1.146 fputs(composite_usage_warnstr, stderr); 1.147 warned_composite_usage = true; 1.148 } 1.149 - int bidx = pick_brdf(norm, outdir); 1.150 - return sub_brdf[bidx].brdf->eval(norm, outdir, indir); 1.151 + int bidx = pick_brdf(geom, outdir); 1.152 + return sub_brdf[bidx].brdf->eval(geom, outdir, indir); 1.153 } 1.154 1.155 // --- class LambertRefl --- 1.156 -Vector3 LambertRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const 1.157 +Vector3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const 1.158 { 1.159 Vector3 dir = Vector3{randf(-1, 1), randf(-1, 1), randf(-1, 1)}.normalized(); 1.160 - return dot_product(dir, norm) < 0.0 ? -dir : dir; 1.161 + return dot_product(dir, geom.normal) < 0.0 ? -dir : dir; 1.162 } 1.163 1.164 -float LambertRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const 1.165 +float LambertRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const 1.166 { 1.167 - return dot_product(norm, outdir); 1.168 + return dot_product(geom.normal, outdir); 1.169 } 1.170 1.171 // --- class MirrorRefl --- 1.172 -Vector3 MirrorRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const 1.173 +Vector3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const 1.174 { 1.175 - return outdir.reflection(norm); 1.176 + return outdir.reflection(geom.normal); 1.177 } 1.178 1.179 -float MirrorRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const 1.180 +float MirrorRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const 1.181 { 1.182 return 1.0f; 1.183 } 1.184 1.185 // --- class PhongRefl --- 1.186 -PhongRefl::PhongRefl() 1.187 +Vector3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const 1.188 { 1.189 - shininess = 42.0; 1.190 -} 1.191 + Vector3 refl = outdir.reflection(geom.normal).normalized(); 1.192 + SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit); 1.193 1.194 -Vector3 PhongRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const 1.195 -{ 1.196 - // construct orthonormal basis with k being the reflection dir 1.197 - Vector3 refl = outdir.reflection(norm).normalized(); 1.198 - Vector3 up = Vector3(0, 0, 1); 1.199 - if(fabs(dot_product(refl, up)) - 1.0 < 0.001) { 1.200 - up = Vector3(0, 1, 0); 1.201 - } 1.202 - Vector3 right = cross_product(up, refl); 1.203 - up = cross_product(refl, right); 1.204 - 1.205 - // construct the matrix transposed to move the sample from reflection 1.206 - // space to world space 1.207 - Matrix3x3 xform; 1.208 - xform.set_column_vector(right, 0); 1.209 - xform.set_column_vector(up, 1); 1.210 - xform.set_column_vector(refl, 2); 1.211 + float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0; 1.212 1.213 float phi = acos(pow(randf(), 1.0 / (shininess + 1))); 1.214 float theta = 2.0 * M_PI * randf(); 1.215 1.216 - Vector3 v; 1.217 - v.x = cos(theta) * sin(phi); 1.218 - v.y = sin(theta) * sin(phi); 1.219 - v.z = cos(phi); 1.220 - v.transform(xform); 1.221 - return v; 1.222 + return refl_geom.spherical_to_world(theta, phi); 1.223 } 1.224 1.225 -float PhongRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const 1.226 +float PhongRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const 1.227 { 1.228 - Vector3 refl = outdir.reflection(norm); 1.229 + float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0; 1.230 + 1.231 + Vector3 refl = outdir.reflection(geom.normal); 1.232 float dot = std::max<float>(dot_product(indir, refl), 0.0f); 1.233 return pow(dot, shininess); 1.234 }