nuclear@31: #include nuclear@30: #include nuclear@2: #include "brdf.h" nuclear@31: #include "material.h" nuclear@2: #include "erebus_impl.h" nuclear@2: nuclear@31: // --- class SurfaceGeometry --- nuclear@31: SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, VecLength st) nuclear@31: : SurfaceGeometry(norm, Vector2(0, 0), st) nuclear@31: { nuclear@31: } nuclear@31: nuclear@31: SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector2 &uvarg, VecLength st) nuclear@31: : normal(norm), uv(uvarg) nuclear@31: { nuclear@31: if(st == unknown) { nuclear@31: normal.normalize(); nuclear@31: } nuclear@31: nuclear@31: tangent = Vector3(1.0f, 0.0f, 0.0f); nuclear@34: if(1.0 - fabs(dot_product(normal, tangent)) < 1e-4f) { nuclear@31: tangent = Vector3(0.0f, 0.0f, 1.0f); nuclear@31: } nuclear@31: Vector3 bitan = cross_product(normal, tangent); nuclear@31: tangent = cross_product(bitan, normal); nuclear@31: } nuclear@31: nuclear@31: SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, VecLength st) nuclear@31: : SurfaceGeometry(norm, tang, Vector2(0, 0), st) nuclear@31: { nuclear@31: } nuclear@31: nuclear@31: SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, const Vector2 &uvarg, VecLength st) nuclear@31: : normal(norm), tangent(tang), uv(uvarg) nuclear@31: { nuclear@31: if(st == unknown) { nuclear@31: normal.normalize(); nuclear@31: tangent.normalize(); nuclear@31: } nuclear@31: } nuclear@31: nuclear@31: Vector3 SurfaceGeometry::spherical_to_world(float theta, float phi) const nuclear@31: { nuclear@31: float x = cos(theta) * sin(phi); nuclear@31: float y = sin(theta) * sin(phi); nuclear@31: float z = cos(phi); nuclear@31: return sample_to_world(Vector3(x, y, z)); nuclear@31: } nuclear@31: nuclear@31: Vector3 SurfaceGeometry::sample_to_world(const Vector3 &v) const nuclear@31: { nuclear@31: Matrix3x3 xform; nuclear@31: xform.set_column_vector(tangent, 0); nuclear@31: xform.set_column_vector(cross_product(normal, tangent), 1); nuclear@31: xform.set_column_vector(normal, 2); nuclear@31: return v.transformed(xform); nuclear@31: } nuclear@31: nuclear@31: Vector3 SurfaceGeometry::world_to_sample(const Vector3 &v) const nuclear@31: { nuclear@31: Matrix3x3 xform; nuclear@31: xform.set_row_vector(tangent, 0); nuclear@31: xform.set_row_vector(cross_product(normal, tangent), 1); nuclear@31: xform.set_row_vector(normal, 2); nuclear@31: return v.transformed(xform); nuclear@31: } nuclear@31: nuclear@2: // ---- class Reflectance ---- nuclear@2: Reflectance::Reflectance() nuclear@2: { nuclear@31: mtl = 0; nuclear@2: } nuclear@2: nuclear@31: Reflectance::Reflectance(const Material *mtl) nuclear@2: { nuclear@31: this->mtl = mtl; nuclear@31: } nuclear@31: nuclear@31: float Reflectance::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const nuclear@31: { nuclear@31: *indir = sample_dir(geom, outdir); nuclear@31: return eval(geom, outdir, *indir); nuclear@2: } nuclear@2: nuclear@21: // --- class CompositeRefl ---- nuclear@21: CompositeRefl::CompositeRefl() nuclear@21: { nuclear@21: valid_checked = false; nuclear@21: } nuclear@21: nuclear@31: CompositeRefl::CompositeRefl(const Material *mtl) nuclear@31: : Reflectance(mtl) nuclear@31: { nuclear@31: valid_checked = false; nuclear@31: } nuclear@31: nuclear@31: int CompositeRefl::pick_brdf(const SurfaceGeometry &geom, const Vector3 &outdir) const nuclear@21: { nuclear@21: if(sub_brdf.empty()) { nuclear@21: return -1; nuclear@21: } nuclear@21: nuclear@21: int brdf_count = (int)sub_brdf.size(); nuclear@21: if(brdf_count == 1) { nuclear@21: return 0; nuclear@21: } nuclear@21: nuclear@21: // construct CDF from sub-brdf probabilities nuclear@21: float *cdf = (float*)alloca(brdf_count * sizeof *cdf); nuclear@21: for(int i=0; i 0 ? cdf[i - 1] + w : w; nuclear@21: } nuclear@21: nuclear@21: float rval = randf(0.0, cdf[brdf_count - 1]); nuclear@21: for(int i=0; isample_dir(geom, outdir); nuclear@21: } nuclear@21: nuclear@31: float CompositeRefl::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const nuclear@21: { nuclear@31: int bidx = pick_brdf(geom, outdir); nuclear@31: return sub_brdf[bidx].brdf->sample(geom, outdir, indir); nuclear@21: } nuclear@21: nuclear@31: float CompositeRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const nuclear@21: { nuclear@21: if(!warned_composite_usage) { nuclear@21: fputs(composite_usage_warnstr, stderr); nuclear@21: warned_composite_usage = true; nuclear@21: } nuclear@31: int bidx = pick_brdf(geom, outdir); nuclear@31: return sub_brdf[bidx].brdf->eval(geom, outdir, indir); nuclear@21: } nuclear@21: nuclear@2: // --- class LambertRefl --- nuclear@31: Vector3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const nuclear@2: { nuclear@32: // TODO: write a better uniform disk sampling that doesn't rely in rejection nuclear@32: Vector3 dir; nuclear@32: do { nuclear@32: dir = Vector3(randf(-1, 1), randf(-1, 1), 0); nuclear@32: } while(dir.length_sq() > 1.0); nuclear@32: dir.z = sqrt(1.0 - (dir.x * dir.x + dir.y * dir.y)); nuclear@32: nuclear@32: return geom.sample_to_world(dir); nuclear@2: } nuclear@2: nuclear@31: float LambertRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const nuclear@2: { nuclear@31: return dot_product(geom.normal, outdir); nuclear@2: } nuclear@21: nuclear@21: // --- class MirrorRefl --- nuclear@31: Vector3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const nuclear@21: { nuclear@31: return outdir.reflection(geom.normal); nuclear@21: } nuclear@21: nuclear@31: float MirrorRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const nuclear@21: { nuclear@21: return 1.0f; nuclear@21: } nuclear@28: nuclear@28: // --- class PhongRefl --- nuclear@31: Vector3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const nuclear@28: { nuclear@31: Vector3 refl = outdir.reflection(geom.normal).normalized(); nuclear@31: SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit); nuclear@28: nuclear@31: float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0; nuclear@28: nuclear@28: float phi = acos(pow(randf(), 1.0 / (shininess + 1))); nuclear@28: float theta = 2.0 * M_PI * randf(); nuclear@28: nuclear@31: return refl_geom.spherical_to_world(theta, phi); nuclear@28: } nuclear@28: nuclear@31: float PhongRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const nuclear@28: { nuclear@31: float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0; nuclear@31: nuclear@31: Vector3 refl = outdir.reflection(geom.normal); nuclear@28: float dot = std::max(dot_product(indir, refl), 0.0f); nuclear@28: return pow(dot, shininess); nuclear@28: }