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  }