From d71b29b60e5933e672a8d9df57c7de43451e1a20 Mon Sep 17 00:00:00 2001 From: yumcyawiz Date: Sat, 27 Nov 2021 15:27:28 +0900 Subject: [PATCH] fix scalling factor of Glass BTDF, don't scale when photon tracing --- include/integrator.h | 33 +++++++++-------- include/material.h | 86 +++++++++++++++++++++++++++++--------------- include/primitive.h | 17 +++++---- 3 files changed, 86 insertions(+), 50 deletions(-) diff --git a/include/integrator.h b/include/integrator.h index 1fe24a1..583e9a9 100644 --- a/include/integrator.h +++ b/include/integrator.h @@ -59,7 +59,8 @@ class PathTracing : public Integrator { Vec3f dir; float pdf_dir; Vec3f f = info.hitPrimitive->sampleBxDF( - -ray.direction, info.surfaceInfo, sampler, dir, pdf_dir); + -ray.direction, info.surfaceInfo, TransportDirection::FROM_CAMERA, + sampler, dir, pdf_dir); // update throughput and ray throughput *= f * std::abs(dot(dir, info.surfaceInfo.normal)) / pdf_dir; @@ -109,8 +110,8 @@ class PhotonMapping : public Integrator { Vec3f Lo; for (const int photon_idx : photon_indices) { const Photon& photon = globalPhotonMap.getIthPhoton(photon_idx); - const Vec3f f = - info.hitPrimitive->evaluateBxDF(wo, photon.wi, info.surfaceInfo); + const Vec3f f = info.hitPrimitive->evaluateBxDF( + wo, photon.wi, info.surfaceInfo, TransportDirection::FROM_CAMERA); Lo += f * photon.throughput; } if (photon_indices.size() > 0) { @@ -131,8 +132,8 @@ class PhotonMapping : public Integrator { Vec3f Lo; for (const int photon_idx : photon_indices) { const Photon& photon = causticsPhotonMap.getIthPhoton(photon_idx); - const Vec3f f = - info.hitPrimitive->evaluateBxDF(wo, photon.wi, info.surfaceInfo); + const Vec3f f = info.hitPrimitive->evaluateBxDF( + wo, photon.wi, info.surfaceInfo, TransportDirection::FROM_CAMERA); Lo += f * photon.throughput; } if (photon_indices.size() > 0) { @@ -171,7 +172,8 @@ class PhotonMapping : public Integrator { IntersectInfo info_shadow; if (!scene.intersect(ray_shadow, info_shadow)) { const Vec3f Le = light->Le(light_surf, -wi); - const Vec3f f = info.hitPrimitive->evaluateBxDF(wo, wi, info.surfaceInfo); + const Vec3f f = info.hitPrimitive->evaluateBxDF( + wo, wi, info.surfaceInfo, TransportDirection::FROM_CAMERA); const float cos = std::abs(dot(wi, info.surfaceInfo.normal)); Ld = f * cos * Le / (pdf_choose_light * pdf_dir); } @@ -191,8 +193,9 @@ class PhotonMapping : public Integrator { // sample direction by BxDF Vec3f dir; float pdf_dir; - const Vec3f f = info.hitPrimitive->sampleBxDF(wo, info.surfaceInfo, sampler, - dir, pdf_dir); + const Vec3f f = info.hitPrimitive->sampleBxDF( + wo, info.surfaceInfo, TransportDirection::FROM_CAMERA, sampler, dir, + pdf_dir); const float cos = std::abs(dot(info.surfaceInfo.normal, dir)); // trace final gathering ray @@ -294,7 +297,8 @@ class PhotonMapping : public Integrator { Vec3f dir; float pdf_dir; const Vec3f f = info.hitPrimitive->sampleBxDF( - -ray.direction, info.surfaceInfo, sampler, dir, pdf_dir); + -ray.direction, info.surfaceInfo, TransportDirection::FROM_CAMERA, + sampler, dir, pdf_dir); // recursively raytrace const Ray next_ray(info.surfaceInfo.position, dir); @@ -309,8 +313,8 @@ class PhotonMapping : public Integrator { else { // sample all direction const std::vector dir_pairs = - info.hitPrimitive->sampleAllBxDF(-ray.direction, - info.surfaceInfo); + info.hitPrimitive->sampleAllBxDF(-ray.direction, info.surfaceInfo, + TransportDirection::FROM_CAMERA); // recursively raytrace Vec3f Lo; @@ -413,9 +417,9 @@ class PhotonMapping : public Integrator { // sample direction by BxDF Vec3f dir; float pdf_dir; - const Vec3f f = - info.hitPrimitive->sampleBxDF(-ray.direction, info.surfaceInfo, - sampler_per_thread, dir, pdf_dir); + const Vec3f f = info.hitPrimitive->sampleBxDF( + -ray.direction, info.surfaceInfo, TransportDirection::FROM_LIGHT, + sampler_per_thread, dir, pdf_dir); // update throughput and ray throughput *= @@ -501,6 +505,7 @@ class PhotonMapping : public Integrator { float pdf_dir; const Vec3f f = info.hitPrimitive->sampleBxDF(-ray.direction, info.surfaceInfo, + TransportDirection::FROM_LIGHT, sampler_per_thread, dir, pdf_dir); // update throughput and ray diff --git a/include/material.h b/include/material.h index bad055a..014f89b 100644 --- a/include/material.h +++ b/include/material.h @@ -7,6 +7,8 @@ enum class BxDFType { DIFFUSE, SPECULAR }; +enum class TransportDirection { FROM_LIGHT, FROM_CAMERA }; + using DirectionPair = std::pair; // represent BRDF or BTDF @@ -51,18 +53,21 @@ class BxDF { BxDFType getType() const { return type; } // evaluate BxDF - virtual Vec3f evaluate(const Vec3f& wo, const Vec3f& wi) const = 0; + virtual Vec3f evaluate(const Vec3f& wo, const Vec3f& wi, + const TransportDirection& transport_dir) const = 0; // sample direction by BxDF. // its pdf is propotional to the shape of BxDF - virtual Vec3f sampleDirection(const Vec3f& wo, Sampler& sampler, Vec3f& wi, + virtual Vec3f sampleDirection(const Vec3f& wo, + const TransportDirection& transport_dir, + Sampler& sampler, Vec3f& wi, float& pdf) const = 0; // get all samplable direction // NOTE: for specular only // NOTE: used for drawing fresnel reflection nicely at low number of samples virtual std::vector sampleAllDirection( - const Vec3f& wo) const = 0; + const Vec3f& wo, const TransportDirection& transport_dir) const = 0; }; class Lambert : public BxDF { @@ -72,7 +77,8 @@ class Lambert : public BxDF { public: Lambert(const Vec3f& rho) : BxDF(BxDFType::DIFFUSE), rho(rho) {} - Vec3f evaluate(const Vec3f& wo, const Vec3f& wi) const override { + Vec3f evaluate(const Vec3f& wo, const Vec3f& wi, + const TransportDirection& transport_dir) const override { // when wo, wi is under the surface, return 0 const float cosThetaO = cosTheta(wo); const float cosThetaI = cosTheta(wi); @@ -81,16 +87,18 @@ class Lambert : public BxDF { return rho / PI; } - Vec3f sampleDirection(const Vec3f& wo, Sampler& sampler, Vec3f& wi, + Vec3f sampleDirection(const Vec3f& wo, + const TransportDirection& transport_dir, + Sampler& sampler, Vec3f& wi, float& pdf) const override { // cosine weighted hemisphere sampling wi = sampleCosineHemisphere(sampler.getNext2D(), pdf); - return evaluate(wo, wi); + return evaluate(wo, wi, transport_dir); } std::vector sampleAllDirection( - const Vec3f& wo) const override { + const Vec3f& wo, const TransportDirection& transport_dir) const override { std::vector ret; return ret; } @@ -104,11 +112,14 @@ class Mirror : public BxDF { Mirror(const Vec3f& rho) : BxDF(BxDFType::SPECULAR), rho(rho) {} // NOTE: delta function - Vec3f evaluate(const Vec3f& wo, const Vec3f& wi) const override { + Vec3f evaluate(const Vec3f& wo, const Vec3f& wi, + const TransportDirection& transport_dir) const override { return Vec3f(0); } - Vec3f sampleDirection(const Vec3f& wo, Sampler& sampler, Vec3f& wi, + Vec3f sampleDirection(const Vec3f& wo, + const TransportDirection& transport_dir, + Sampler& sampler, Vec3f& wi, float& pdf) const override { wi = reflect(wo, Vec3f(0, 1, 0)); pdf = 1.0f; @@ -117,7 +128,7 @@ class Mirror : public BxDF { } std::vector sampleAllDirection( - const Vec3f& wo) const override { + const Vec3f& wo, const TransportDirection& transport_dir) const override { std::vector ret; const Vec3f wi = reflect(wo, Vec3f(0, 1, 0)); ret.emplace_back(wi, rho / absCosTheta(wi)); @@ -125,6 +136,9 @@ class Mirror : public BxDF { } }; +// NOTE: due to the asymmetry of BSDF, we need to use a different scaling factor +// for photon tracing +// https://pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/The_Path-Space_Measurement_Equation#Non-symmetricScattering class Glass : public BxDF { private: Vec3f rho; @@ -135,27 +149,30 @@ class Glass : public BxDF { : BxDF(BxDFType::SPECULAR), rho(rho), ior(ior) {} // NOTE: delta function - Vec3f evaluate(const Vec3f& wo, const Vec3f& wi) const override { + Vec3f evaluate(const Vec3f& wo, const Vec3f& wi, + const TransportDirection& transport_dir) const override { return Vec3f(0); } - Vec3f sampleDirection(const Vec3f& wo, Sampler& sampler, Vec3f& wi, + Vec3f sampleDirection(const Vec3f& wo, + const TransportDirection& transport_dir, + Sampler& sampler, Vec3f& wi, float& pdf) const override { // set appropriate ior, normal - float iorI, iorT; + float iorO, iorI; Vec3f n; if (wo[1] > 0) { - iorI = 1.0f; - iorT = ior; + iorO = 1.0f; + iorI = ior; n = Vec3f(0, 1, 0); } else { - iorI = ior; - iorT = 1.0f; + iorO = ior; + iorI = 1.0f; n = Vec3f(0, -1, 0); } // fresnel reflectance - const float fr = fresnel(dot(wo, n), iorI, iorT); + const float fr = fresnel(dot(wo, n), iorO, iorI); // reflection if (sampler.getNext1D() < fr) { @@ -166,10 +183,16 @@ class Glass : public BxDF { // refraction else { Vec3f tr; - if (refract(wo, n, iorI, iorT, tr)) { + if (refract(wo, n, iorO, iorI, tr)) { wi = tr; pdf = 1.0f; - return rho / absCosTheta(wi); + + float scalling = 1.0f; + if (transport_dir == TransportDirection::FROM_CAMERA) { + scalling = (iorO * iorO) / (iorI * iorI); + } + + return scalling * rho / absCosTheta(wi); } // total reflection else { @@ -181,24 +204,24 @@ class Glass : public BxDF { } std::vector sampleAllDirection( - const Vec3f& wo) const override { + const Vec3f& wo, const TransportDirection& transport_dir) const override { std::vector ret; // set appropriate ior, normal - float iorI, iorT; + float iorO, iorI; Vec3f n; if (wo[1] > 0) { - iorI = 1.0f; - iorT = ior; + iorO = 1.0f; + iorI = ior; n = Vec3f(0, 1, 0); } else { - iorI = ior; - iorT = 1.0f; + iorO = ior; + iorI = 1.0f; n = Vec3f(0, -1, 0); } // fresnel reflectance - const float fr = fresnel(dot(wo, n), iorI, iorT); + const float fr = fresnel(dot(wo, n), iorO, iorI); // reflection const Vec3f wr = reflect(wo, n); @@ -206,8 +229,13 @@ class Glass : public BxDF { // refraction Vec3f tr; - if (refract(wo, n, iorI, iorT, tr)) { - ret.emplace_back(tr, (1.0f - fr) * rho / absCosTheta(tr)); + if (refract(wo, n, iorO, iorI, tr)) { + float scalling = 1.0f; + if (transport_dir == TransportDirection::FROM_CAMERA) { + scalling = (iorO * iorO) / (iorI * iorI); + } + + ret.emplace_back(tr, (1.0f - fr) * scalling * rho / absCosTheta(tr)); } else { ret[0].second = rho / absCosTheta(wr); } diff --git a/include/primitive.h b/include/primitive.h index 665d1d1..7de4ddc 100644 --- a/include/primitive.h +++ b/include/primitive.h @@ -30,27 +30,29 @@ class Primitive { BxDFType getBxDFType() const { return bxdf->getType(); } Vec3f evaluateBxDF(const Vec3f& wo, const Vec3f& wi, - const SurfaceInfo& surfInfo) const { + const SurfaceInfo& surfInfo, + const TransportDirection& mode) const { // world to local transform const Vec3f wo_l = worldToLocal(wo, surfInfo.dpdu, surfInfo.normal, surfInfo.dpdv); const Vec3f wi_l = worldToLocal(wi, surfInfo.dpdu, surfInfo.normal, surfInfo.dpdv); - return bxdf->evaluate(wo_l, wi_l); + return bxdf->evaluate(wo_l, wi_l, mode); } // sample direction by BxDF // its pdf is propotional to the shape od BxDF Vec3f sampleBxDF(const Vec3f& wo, const SurfaceInfo& surfInfo, - Sampler& sampler, Vec3f& wi, float& pdf) const { + const TransportDirection& mode, Sampler& sampler, Vec3f& wi, + float& pdf) const { // world to local transform const Vec3f wo_l = worldToLocal(wo, surfInfo.dpdu, surfInfo.normal, surfInfo.dpdv); // sample direction in tangent space Vec3f wi_l; - const Vec3f f = bxdf->sampleDirection(wo_l, sampler, wi_l, pdf); + const Vec3f f = bxdf->sampleDirection(wo_l, mode, sampler, wi_l, pdf); // local to world transform wi = localToWorld(wi_l, surfInfo.dpdu, surfInfo.normal, surfInfo.dpdv); @@ -59,14 +61,15 @@ class Primitive { } // get all samplable direction - std::vector sampleAllBxDF(const Vec3f& wo, - const SurfaceInfo& surfInfo) const { + std::vector sampleAllBxDF( + const Vec3f& wo, const SurfaceInfo& surfInfo, + const TransportDirection& mode) const { // world to local transform const Vec3f wo_l = worldToLocal(wo, surfInfo.dpdu, surfInfo.normal, surfInfo.dpdv); // sample all direction in tangent space - std::vector dir_pairs = bxdf->sampleAllDirection(wo_l); + std::vector dir_pairs = bxdf->sampleAllDirection(wo_l, mode); // local to world transform for (auto& dp : dir_pairs) {