# Considering a path generated by bidirectional path tracing under a different strategy

Say I have constructed a path, using bidirectional path tracing (BDPT), consisting of a light subpath $$y$$ with $$s=2$$ vertices and a camera subpath $$z$$ with $$t=3$$ camera vertices.

Now I want to compute the contribution (measurement contribution over sampling density) of the same path considered under the BDPT strategy with $$s'=3$$ and $$t'=2$$ light and camera vertices, respectively.

Doing this involves the computation of the density of sampling the direction $$\omega_{y_{s-1}\to z_{t-1}}$$. Assuming the path has positive luminance, can I expect this density to be positive?

I'm only considering "lambertian reflection" components of the involved BSDFs. Here's my example code in PBRT:

auto const omega1 = pbrt::Normalize(z[t - 1].p() - y[s - 1].p());
auto const omega2 = y[s - 1].si.wo;
auto const p = y[s - 1].si.bsdf->Pdf(omega1, omega2, lambertian_reflection);

auto const c =
g(scene, z[t - 1], y[s - 1]) *                          // g(Z_{t-1} ↔ Y_{s-1})
z[t - 1].f(y[s - 1], pbrt::TransportMode::Radiance) *   // f_s(Y_{s-1} → Z_{t-1} → Z_{t-2})
y[s - 1].f(z[t - 1], pbrt::TransportMode::Importance);  // f_s(Y_{s-2} → Y_{s-1} → Z_{t-1})


I've encountered a situation where c is nonzero, but the density p is 0 since internally si.bsdf->Pdf checks whether omega1 and omega2 are on the same side of the hemisphere induced by the normal at y[s - 1] and they were not in that situation.

However, this is confusing. If they are not on the same side, shouldn't this imply that y[s - 2] and z[t - 1] are occluded from each other (and hence the geometry term g(scene, z[t - 1], y[s - 1]) should be $$0$$?). Am I missing something?

EDIT: Note that the density of sampling a path of length $$k$$ using the $$(s,t)$$-strategy is $$q_{s,\:t}(x)=q_{0,\:t}(x_0,\ldots,x_{t-1})q_{s,\:0}(x_t,\ldots,x_k)$$, where $$$$\label{eq:path-tracing-strategy}\begin{split}q_{0,\:t}(z)=&\prod_{i=1}^kg(z_{i-1}\leftrightarrow z_i)\left.\begin{cases}p_0\left(z_0,\omega_{z_0\to z_1}\right)&\text{, if }q_0(z_0)>0\\0&\text{, otherwise}\end{cases}\right\}\\&\prod_{i=2}^kp\left(z_{i-1},\omega_{z_{i-1}\to z_{i-2}},\omega_{z_{i-1}\to z_i}\right)\end{split}$$$$ where $$p_0$$ is the density of sampling the inital camera ray (with respect to the path throughput measure as defined by Veach), $$q_0$$ is the marginal density of sampling the initial vertex and $$p$$ is the conditional density (with respect to projected solid angle) of sampling the third argument given the first and second argument. The form of $$q_{s,\:0}$$ is analogous.

EDIT 2: The relevant PBRT code:

Float BxDF::Pdf(const Vector3f &wo, const Vector3f &wi) const {
return SameHemisphere(wo, wi) ? AbsCosTheta(wi) * InvPi : 0;
}

inline bool SameHemisphere(const Vector3f &w, const Vector3f &wp) {
return w.z * wp.z > 0;
}

// BSDF Method Definitions
Spectrum BSDF::f(const Vector3f &woW, const Vector3f &wiW,
BxDFType flags) const {
ProfilePhase pp(Prof::BSDFEvaluation);
Vector3f wi = WorldToLocal(wiW), wo = WorldToLocal(woW);
if (wo.z == 0) return 0.;
bool reflect = Dot(wiW, ng) * Dot(woW, ng) > 0;
Spectrum f(0.f);
for (int i = 0; i < nBxDFs; ++i)
if (bxdfs[i]->MatchesFlags(flags) &&
((reflect && (bxdfs[i]->type & BSDF_REFLECTION)) ||
(!reflect && (bxdfs[i]->type & BSDF_TRANSMISSION))))
f += bxdfs[i]->f(wo, wi);
return f;
}


EDIT 3: Here is an example: PBRT is using a local coordinate system for shading where the $$z$$-axis is aligned with the shading normal. The following picture shows the local coordinates of the geometric normal at $$y_1$$, the direction wi from $$y_1$$ to $$z_2$$ and the direction wo from $$y_1$$ to $$y_0$$,

wi = (0.93675665128065100,0.34934790470253263,-0.021047987110668076)
wo = (0.37751541702974800,0.28880688322120657,0.87981401108922330)
ng (local) = (0.025315795417292675,-0.0035753744029505263,0.99967311017165450)


ng is slightly off from the shading normal ns = (0,0,1). With respect to ng, wi and wo are in the same hemisphere, but with respect to ns they are not.

• A non-zero path can always be reinterpreted as being generated by another strategy (provided that both have the same support). Say you have path 1: $x_1, ..., x_N$, and you know that $x_1$ is generated with probability $p_1(x_1)$, $x_2$ with $p_2(x_2)$ etc. Then the probability of the path is $\Pi p_i(x_i)$. Now introduce a different strategy with probabilities $q_1(x_1), q_2(x_2), ...$, then the probability for the path being generated by the new strategy is $\Pi q_i(x_i)$. All you have to do is compute the new probabilities to reinterpret it. Mar 18, 2020 at 19:02
• More precisely, the two primary estimators are: $\frac{f(x_1,...,x_N)}{\Pi_i p_i(x_i)}$ and $\frac{f(x_1,...,x_N)}{\Pi_i q_i(x_i)}$. In general you would prefer the one that yields a lower variance (that is, where the probability density better matches $f$). Mar 18, 2020 at 19:05
• @lightxbulb Thank you for your comment. Everything you wrote is clear to me; see my edit. I think that the problem is that .si.bsdf->Pdf is converting the pased directions to local coordinates and then determines whether they lie on the same hemisphere via pbrt::SameHemisphere. In my example, the $z$-components have different signs and hence SameHemisphere returns false. On the other hand, in f the computation of bool reflect = Dot(wiW, ng) * Dot(woW, ng) > 0; involves the normal and this product is positive. Mar 18, 2020 at 19:35
• @lightxbulb In my example c is not zero, but p is zero. And I'm unsure whether there is an error in my computations, since I'd actually expect that the density p of sampling the direction from $y_{s-1}$ to $z_{t-1}$ should be positive (assuming that the contribution under the strategy under which the path was constructed is nonvanishing). Mar 18, 2020 at 19:38
• @lightxbulb Actually, I don't understand why the computation in SameHemisphere doesn't involve the surface normal (ng). Maybe it does implicitly, since the directions are transformed via WorldToLocal. Mar 18, 2020 at 20:15