<p>Say I have constructed a path, using bidirectional path tracing (BDPT), consisting of a light subpath <span class="math-container">$y$</span> with <span class="math-container">$s=2$</span> vertices and a camera subpath <span class="math-container">$z$</span> with <span class="math-container">$t=3$</span> camera vertices.</p>

<p>Now I want to compute the contribution (measurement contribution over sampling density) of the same path considered under the BDPT strategy with <span class="math-container">$s'=3$</span> and <span class="math-container">$t'=2$</span> light and camera vertices, respectively.</p>

<p>Doing this involves the computation of the density of sampling the direction <span class="math-container">$\omega_{y_{s-1}\to z_{t-1}}$</span>. Assuming the path has positive luminance, can I expect this density to be positive?</p>

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

<pre><code>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-&gt;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})
</code></pre>

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

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

<p><strong>EDIT</strong>: Note that the density of sampling a path of length <span class="math-container">$k$</span> using the <span class="math-container">$(s,t)$</span>-strategy is <span class="math-container">$q_{s,\:t}(x)=q_{0,\:t}(x_0,\ldots,x_{t-1})q_{s,\:0}(x_t,\ldots,x_k)$</span>, where <span class="math-container">$$\begin{equation}\label{eq:path-tracing-strategy}\begin{split}q_{0,\:t}(z)=&amp;\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)&amp;\text{, if }q_0(z_0)&gt;0\\0&amp;\text{, otherwise}\end{cases}\right\}\\&amp;\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}\end{equation}$$</span> where <span class="math-container">$p_0$</span> is the density of sampling the inital camera ray (with respect to the path throughput measure as defined by Veach), <span class="math-container">$q_0$</span> is the marginal density of sampling the initial vertex and <span class="math-container">$p$</span> is the conditional density (with respect to projected solid angle) of sampling the third argument given the first and second argument. The form of <span class="math-container">$q_{s,\:0}$</span> is analogous.</p>

<p><strong>EDIT 2</strong>: The relevant PBRT code:</p>

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

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

// BSDF Method Definitions
Spectrum BSDF::f(const Vector3f &amp;woW, const Vector3f &amp;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) &gt; 0;
    Spectrum f(0.f);
    for (int i = 0; i &lt; nBxDFs; ++i)
        if (bxdfs[i]-&gt;MatchesFlags(flags) &amp;&amp;
            ((reflect &amp;&amp; (bxdfs[i]-&gt;type &amp; BSDF_REFLECTION)) ||
             (!reflect &amp;&amp; (bxdfs[i]-&gt;type &amp; BSDF_TRANSMISSION))))
            f += bxdfs[i]-&gt;f(wo, wi);
    return f;
}
</code></pre>

<p><strong>EDIT 3</strong>: Here is an example: PBRT is using a local coordinate system for shading where the <span class="math-container">$z$</span>-axis is aligned with the shading normal. The following picture shows the local coordinates of the geometric normal at <span class="math-container">$y_1$</span>, the direction <code>wi</code> from <span class="math-container">$y_1$</span> to <span class="math-container">$z_2$</span> and the direction <code>wo</code> from <span class="math-container">$y_1$</span> to <span class="math-container">$y_0$</span>,</p>

<pre><code>wi = (0.93675665128065100,0.34934790470253263,-0.021047987110668076)
wo = (0.37751541702974800,0.28880688322120657,0.87981401108922330)
ng (local) = (0.025315795417292675,-0.0035753744029505263,0.99967311017165450)
</code></pre>

<p><a href="https://i.sstatic.net/7RDSl.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/7RDSl.png" alt="enter image description here"></a></p>

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

CC BY-SA 4.0
2020-03-18T19:02:56.507Z
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.
lightxbulb
CC BY-SA 4.0
2020-03-18T19:05:40.780Z
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$).
lightxbulb
CC BY-SA 4.0
2020-03-18T19:35:38.407Z
@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.
0xbadf00d
CC BY-SA 4.0
2020-03-18T19:38:17.160Z
@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).
0xbadf00d
CC BY-SA 4.0
2020-03-18T20:15:26.417Z
@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`.
0xbadf00d
CC BY-SA 4.0
2020-03-18T20:15:31.573Z
@lightxbulb In any case, if you take a look at http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Sampling_Reflection_Functions.html#BxDF::Pdf, they explicitly state *Samples from this distribution will give correct results for any BRDF that isn’t described by a delta distribution, since there is some probability of sampling all directions where the BRDF’s value is non-0* (see the fourth paragraph). So, it seems like there is something wrong with my computations.
0xbadf00d
CC BY-SA 4.0
2020-03-19T07:14:09.227Z
@lightxbulb I guess I've figured out where the problem comes from. It seems like `pdf` is considering the hemisphere wrt the "shading normal" but `f` is considering the hemisphere wrt the "geometry normal" ... I'm not sure what I should conclude from that ...
0xbadf00d
CC BY-SA 4.0
2020-03-19T11:22:15.373Z
Seems to be a question specific to the PBRT implementation. They most likely work in a space where the geometric normal is aligned with one of the axes, and then the transformation aligns it with the actual normal, I haven't looked into the details of PBRT so take that with a grain of salt. If you are considering shading normals however, you must account for the lack of symmetry - see page 150 in Veach's thesis and figure 5.6.
lightxbulb
CC BY-SA 4.0
2020-03-19T15:14:51.083Z
@lightxbulb (a) It seems like that their local coordinate system is such that the positive $z$-axis points in the direction of the *shading* normal. So, `BxDF::Sample_f` samples directions over the hemisphere induced by the shading normal. On the other hand, `y[s - 1].f(z[t - 1], pbrt::TransportMode::Importance)` is evaluating reflection if the direction from `y[s - 1]` to `z[t - 1]` lies in hemisphere wrt the geometric normal (`bool reflect = Dot(wiW, ng) * Dot(woW, ng) > 0;`).
0xbadf00d
CC BY-SA 4.0
2020-03-19T15:15:30.467Z
@lightxbulb So, when the subpaths are connected, `y[s - 1].f(z[t - 1], pbrt::TransportMode::Importance)` might be nonzero even when `BxDF::Sample_f` would never have sampled a direction from `y[s - 1]` to `z[t - 1]`. Does this make sense? This would imply (and that's the important thing for me) that we cannot conclude that a path with a nonzero luminance wrt a $(s,t)$-strategy has a nonzero luminance wrt all other strategies. (b) Are we considering `z[t - 1]` and `y[s - 1]` as mutually visible if the direction to `z[t - 1]` lies in the same hemisphere over the shading or the geometric normal?
0xbadf00d
CC BY-SA 4.0
2020-03-19T16:39:14.623Z
`This would imply (and that's the important thing for me) that we cannot conclude that a path with a nonzero luminance wrt a (s,t)-strategy has a nonzero luminance wrt all other strategies.` - this should be impossible for a correct implementation. A path always carries the same radiance, only the probability with which the vertices are sampled may change. b) See Figure 5.6 in Veach's thesis - it needs to be in the same hemisphere as both the geometric and shading normal.
lightxbulb
CC BY-SA 4.0
2020-03-19T17:11:31.747Z
@lightxbulb Sorry, I've phrased (a) in the wrong way. The problem is that I've constructed a path with nonzero luminance using the $(2,3)$-strategy observed that the density of sampling this path using the $(3,2)$-strategy is zero. See my edit 3.
0xbadf00d
CC BY-SA 4.0
2020-03-19T17:54:09.030Z
Then either you have a bug, or your estimator is both biased and inconsistent. It will never converge to the actual solution, if it's missing paths that are otherwise viable. Your densities should be non-zero for parts of the domain where your function is non-zero, otherwise you get inconsistent estimators, that will never yield the correct solution regardless of the number of samples. Also, concerning your last edit, please see the Figure in Veach's thesis that I mentioned.
lightxbulb
CC BY-SA 4.0
2020-03-19T18:04:58.577Z
@lightxbulb As I've written above, the problem is that `Sample_f` would never sample the direction from $y_1$ to $z_2$, but the connection has still a nonzero contribution `c`.
0xbadf00d
CC BY-SA 4.0
2020-03-19T18:07:05.233Z
Then there are 3 options: 1) that's a bug, 2) the estimator is inconsistent, 3) the contribution is actually zero (may be evaluated to zero at some other place in the code).
lightxbulb
CC BY-SA 4.0
2020-03-19T18:38:18.770Z
@lightxbulb I can guarantee that neither (2) nor (3) is the case. The problem really is that `wi` is not on the hemisphere wrt ´ns` (which is obvious), but `BxDF::Sample_f` samples only directions on that hemisphere (via `CosineSampleHemisphere`). On the other hand, `y[s - 1].f(z[t - 1], pbrt::TransportMode::Importance)` evaluates the BSDF `f(wo, w_i)` (which is nonzero, since the BSDF is Lambertian) as long as `bool reflect = Dot(*wiWorld, ng) * Dot(woWorld, ng) > 0;` is true (which is the case).
0xbadf00d
CC BY-SA 4.0
2020-03-19T18:45:02.170Z
@lightxbulb So, to me it seems like the connection step (which is shown here http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/Bidirectional_Path_Tracing.html#fragment-Handleallotherbidirectionalconnectioncases-0) should not evaluate `f` in this way (in the link it is `qs.f(pt)`). The only step where it should be detected is the evaluation of `G(scene, sampler, qs, pt);`. But it returns a nonzero value. That's why I've asked you (b).
0xbadf00d
CC BY-SA 4.0
2020-03-19T18:45:14.160Z
@lightxbulb As you can see in the implementation of `BSDF::f` in the question, they ony check whether `wi` and `wo` are on the hemisphere wrt `ng`, but they don't check the hemisphere wrt `ns`. I think this is in accordance what they write here http://www.pbr-book.org/3ed-2018/Materials/BSDFs.html#BSDF::f: *In evaluating the scattering equation, however, the dot product of the normal and the incident direction is still taken with the shading normal rather than the geometric normal.*
0xbadf00d
CC BY-SA 4.0
2020-03-19T18:47:53.737Z
```they ony check whether wi and wo are on the hemisphere wrt ng, but they don't check the hemisphere wrt ns``` - then their bsdf is not actually a bsdf since it is not symmetric. Note, this is only the case if you're reading the code correctly (as I mentioned I haven't gone into such details in their code). The reason this implies a non-symmetric bsdf can be found in Veach's thesis as already mentioned. May also be what Veach refers to as "light leaks".
lightxbulb
CC BY-SA 4.0
2020-03-19T18:50:52.560Z
@lightxbulb The relevant function should be [CorrectShadingNormal](http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/The_Path-Space_Measurement_Equation.html#CorrectShadingNormal). But therein they take the absolute value of the inner product: `AbsDot(wo, isect.shading.n) * AbsDot(wi, isect.n);`.
0xbadf00d
CC BY-SA 4.0
2020-03-19T18:53:51.380Z
@lightxbulb Regarding the light leak. They write: *Now it should be clear why pbrt requires BxDFs to evaluate their values without regard to whether and are in the same or different hemispheres. This convention means that light leaks are avoided, since we will only evaluate the BTDFs for the situation in Figure 9.2(a), giving no reflection for a purely reflective surface.* (http://www.pbr-book.org/3ed-2018/Materials/BSDFs.html#BSDF::f)
0xbadf00d
CC BY-SA 4.0
2020-03-19T18:57:57.490Z
I guess this means that they take care of this somewhere (meaning that the contribution is actually correctly zeroed out when required). Anyways, as mentioned, the opposite would imply either an unintentional bug or an inconsistent estimator. Considering that PBRT's been thoroughly tested, I guess it makes sense that it's neither.
lightxbulb
CC BY-SA 4.0
2020-03-19T19:26:13.030Z
@lightxbulb You're right. I've find the place where they state this: http://www.pbr-book.org/3ed-2018/Reflection_Models.html (the last bullet point at the very end of the page). However, at least in the context of `ConnectBDPT`, I don't see where *Higher level code in pbrt [..] ensure[s] that only reflective or transmissive scattering routines are evaluated as appropriate.*.
0xbadf00d
<raytracing><pathtracing><global-illumination><pbr><pbrt>
Considering a path generated by bidirectional path tracing under a different strategy
0xbadf00d
# Considering a path generated by bidirectional path tracing under a different strategy

(Asked by: username_0 on 2020-03-18)

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 $$\begin{equation}\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}\end{equation}$$ 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)

```

[![enter image description here](https://i.sstatic.net/7RDSl.png)](https://i.sstatic.net/7RDSl.png)

`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.

**Tags:** raytracing, pathtracing, global-illumination, pbr, pbrt

> **Comments:**
> **username_1**: 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.  
> **username_1**: 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$).  
> **username_0**: @username_1 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.  
> **username_0**: @username_1 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).  
> **username_0**: @username_1 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`.  
> **username_0**: @username_1 In any case, if you take a look at http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Sampling_Reflection_Functions.html#BxDF::Pdf, they explicitly state *Samples from this distribution will give correct results for any BRDF that isn’t described by a delta distribution, since there is some probability of sampling all directions where the BRDF’s value is non-0* (see the fourth paragraph). So, it seems like there is something wrong with my computations.  
> **username_0**: @username_1 I guess I've figured out where the problem comes from. It seems like `pdf` is considering the hemisphere wrt the "shading normal" but `f` is considering the hemisphere wrt the "geometry normal" ... I'm not sure what I should conclude from that ...  
> **username_1**: Seems to be a question specific to the PBRT implementation. They most likely work in a space where the geometric normal is aligned with one of the axes, and then the transformation aligns it with the actual normal, I haven't looked into the details of PBRT so take that with a grain of salt. If you are considering shading normals however, you must account for the lack of symmetry - see page 150 in Veach's thesis and figure 5.6.  
> **username_0**: @username_1 (a) It seems like that their local coordinate system is such that the positive $z$-axis points in the direction of the *shading* normal. So, `BxDF::Sample_f` samples directions over the hemisphere induced by the shading normal. On the other hand, `y[s - 1].f(z[t - 1], pbrt::TransportMode::Importance)` is evaluating reflection if the direction from `y[s - 1]` to `z[t - 1]` lies in hemisphere wrt the geometric normal (`bool reflect = Dot(wiW, ng) * Dot(woW, ng) > 0;`).  
> **username_0**: @username_1 So, when the subpaths are connected, `y[s - 1].f(z[t - 1], pbrt::TransportMode::Importance)` might be nonzero even when `BxDF::Sample_f` would never have sampled a direction from `y[s - 1]` to `z[t - 1]`. Does this make sense? This would imply (and that's the important thing for me) that we cannot conclude that a path with a nonzero luminance wrt a $(s,t)$-strategy has a nonzero luminance wrt all other strategies. (b) Are we considering `z[t - 1]` and `y[s - 1]` as mutually visible if the direction to `z[t - 1]` lies in the same hemisphere over the shading or the geometric normal?  
> **username_1**: `This would imply (and that's the important thing for me) that we cannot conclude that a path with a nonzero luminance wrt a (s,t)-strategy has a nonzero luminance wrt all other strategies.` - this should be impossible for a correct implementation. A path always carries the same radiance, only the probability with which the vertices are sampled may change. b) See Figure 5.6 in Veach's thesis - it needs to be in the same hemisphere as both the geometric and shading normal.  
> **username_0**: @username_1 Sorry, I've phrased (a) in the wrong way. The problem is that I've constructed a path with nonzero luminance using the $(2,3)$-strategy observed that the density of sampling this path using the $(3,2)$-strategy is zero. See my edit 3.  
> **username_1**: Then either you have a bug, or your estimator is both biased and inconsistent. It will never converge to the actual solution, if it's missing paths that are otherwise viable. Your densities should be non-zero for parts of the domain where your function is non-zero, otherwise you get inconsistent estimators, that will never yield the correct solution regardless of the number of samples. Also, concerning your last edit, please see the Figure in Veach's thesis that I mentioned.  
> **username_0**: @username_1 As I've written above, the problem is that `Sample_f` would never sample the direction from $y_1$ to $z_2$, but the connection has still a nonzero contribution `c`.  
> **username_1**: Then there are 3 options: 1) that's a bug, 2) the estimator is inconsistent, 3) the contribution is actually zero (may be evaluated to zero at some other place in the code).  
> **username_0**: @username_1 I can guarantee that neither (2) nor (3) is the case. The problem really is that `wi` is not on the hemisphere wrt ´ns` (which is obvious), but `BxDF::Sample_f` samples only directions on that hemisphere (via `CosineSampleHemisphere`). On the other hand, `y[s - 1].f(z[t - 1], pbrt::TransportMode::Importance)` evaluates the BSDF `f(wo, w_i)` (which is nonzero, since the BSDF is Lambertian) as long as `bool reflect = Dot(*wiWorld, ng) * Dot(woWorld, ng) > 0;` is true (which is the case).  
> **username_0**: @username_1 So, to me it seems like the connection step (which is shown here http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/Bidirectional_Path_Tracing.html#fragment-Handleallotherbidirectionalconnectioncases-0) should not evaluate `f` in this way (in the link it is `qs.f(pt)`). The only step where it should be detected is the evaluation of `G(scene, sampler, qs, pt);`. But it returns a nonzero value. That's why I've asked you (b).  
> **username_0**: @username_1 As you can see in the implementation of `BSDF::f` in the question, they ony check whether `wi` and `wo` are on the hemisphere wrt `ng`, but they don't check the hemisphere wrt `ns`. I think this is in accordance what they write here http://www.pbr-book.org/3ed-2018/Materials/BSDFs.html#BSDF::f: *In evaluating the scattering equation, however, the dot product of the normal and the incident direction is still taken with the shading normal rather than the geometric normal.*  
> **username_1**: ```they ony check whether wi and wo are on the hemisphere wrt ng, but they don't check the hemisphere wrt ns``` - then their bsdf is not actually a bsdf since it is not symmetric. Note, this is only the case if you're reading the code correctly (as I mentioned I haven't gone into such details in their code). The reason this implies a non-symmetric bsdf can be found in Veach's thesis as already mentioned. May also be what Veach refers to as "light leaks".  
> **username_0**: @username_1 The relevant function should be [CorrectShadingNormal](http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/The_Path-Space_Measurement_Equation.html#CorrectShadingNormal). But therein they take the absolute value of the inner product: `AbsDot(wo, isect.shading.n) * AbsDot(wi, isect.n);`.  
> **username_0**: @username_1 Regarding the light leak. They write: *Now it should be clear why pbrt requires BxDFs to evaluate their values without regard to whether and are in the same or different hemispheres. This convention means that light leaks are avoided, since we will only evaluate the BTDFs for the situation in Figure 9.2(a), giving no reflection for a purely reflective surface.* (http://www.pbr-book.org/3ed-2018/Materials/BSDFs.html#BSDF::f)  
> **username_1**: I guess this means that they take care of this somewhere (meaning that the contribution is actually correctly zeroed out when required). Anyways, as mentioned, the opposite would imply either an unintentional bug or an inconsistent estimator. Considering that PBRT's been thoroughly tested, I guess it makes sense that it's neither.  
> **username_0**: @username_1 You're right. I've find the place where they state this: http://www.pbr-book.org/3ed-2018/Reflection_Models.html (the last bullet point at the very end of the page). However, at least in the context of `ConnectBDPT`, I don't see where *Higher level code in pbrt [..] ensure[s] that only reflective or transmissive scattering routines are evaluated as appropriate.*.  

---

## 0 answers

