Skip to content

Commit

Permalink
fix scalling factor of Glass BTDF, don't scale when photon tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
yumcyaWiz committed Nov 27, 2021
1 parent 01c65ca commit d71b29b
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 50 deletions.
33 changes: 19 additions & 14 deletions include/integrator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -309,8 +313,8 @@ class PhotonMapping : public Integrator {
else {
// sample all direction
const std::vector<DirectionPair> dir_pairs =
info.hitPrimitive->sampleAllBxDF(-ray.direction,
info.surfaceInfo);
info.hitPrimitive->sampleAllBxDF(-ray.direction, info.surfaceInfo,
TransportDirection::FROM_CAMERA);

// recursively raytrace
Vec3f Lo;
Expand Down Expand Up @@ -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 *=
Expand Down Expand Up @@ -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
Expand Down
86 changes: 57 additions & 29 deletions include/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

enum class BxDFType { DIFFUSE, SPECULAR };

enum class TransportDirection { FROM_LIGHT, FROM_CAMERA };

using DirectionPair = std::pair<Vec3f, Vec3f>;

// represent BRDF or BTDF
Expand Down Expand Up @@ -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<DirectionPair> sampleAllDirection(
const Vec3f& wo) const = 0;
const Vec3f& wo, const TransportDirection& transport_dir) const = 0;
};

class Lambert : public BxDF {
Expand All @@ -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);
Expand All @@ -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<DirectionPair> sampleAllDirection(
const Vec3f& wo) const override {
const Vec3f& wo, const TransportDirection& transport_dir) const override {
std::vector<DirectionPair> ret;
return ret;
}
Expand All @@ -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;
Expand All @@ -117,14 +128,17 @@ class Mirror : public BxDF {
}

std::vector<DirectionPair> sampleAllDirection(
const Vec3f& wo) const override {
const Vec3f& wo, const TransportDirection& transport_dir) const override {
std::vector<DirectionPair> ret;
const Vec3f wi = reflect(wo, Vec3f(0, 1, 0));
ret.emplace_back(wi, rho / absCosTheta(wi));
return ret;
}
};

// 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;
Expand All @@ -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) {
Expand All @@ -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 {
Expand All @@ -181,33 +204,38 @@ class Glass : public BxDF {
}

std::vector<DirectionPair> sampleAllDirection(
const Vec3f& wo) const override {
const Vec3f& wo, const TransportDirection& transport_dir) const override {
std::vector<DirectionPair> 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);
ret.emplace_back(wr, fr * rho / absCosTheta(wr));

// 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);
}
Expand Down
17 changes: 10 additions & 7 deletions include/primitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -59,14 +61,15 @@ class Primitive {
}

// get all samplable direction
std::vector<DirectionPair> sampleAllBxDF(const Vec3f& wo,
const SurfaceInfo& surfInfo) const {
std::vector<DirectionPair> 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<DirectionPair> dir_pairs = bxdf->sampleAllDirection(wo_l);
std::vector<DirectionPair> dir_pairs = bxdf->sampleAllDirection(wo_l, mode);

// local to world transform
for (auto& dp : dir_pairs) {
Expand Down

0 comments on commit d71b29b

Please sign in to comment.