<p>I don't understand why the PBR implementation of Metorpolis Light Transport scales the final image by <code>b / mutationsPerPixel</code>.</p>
<p>The authors write:</p>
<blockquote>
<p><em>Each Metropolis iteration within &quot;Run nChains Markov chains in parallel&quot; has splatted contributions with weighted unit luminance to the Film so that the final average film luminance before Film::WriteImage() is exactly equal to mutationsPerPixel.</em></p>
</blockquote>
<p>(see <a href="http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/Metropolis_Light_Transport.html" rel="nofollow noreferrer">http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/Metropolis_Light_Transport.html</a> at the very end of the page.)</p>
<p>For simplicity, assume that we are considering a fixed path length <span class="math-container">$k\in\mathbb N$</span> and a single fixed <span class="math-container">$(s,t)$</span>-strategy. Formally, let</p>
<ul>
<li><span class="math-container">$M$</span> denote the scene surface set, <span class="math-container">$\mathcal B(M)$</span> denote the Borel <span class="math-container">$\sigma$</span>-algebra on <span class="math-container">$M$</span> and <span class="math-container">$\sigma_M$</span> denote the surface measure on <span class="math-container">$\mathcal B(M)$</span></li>
<li><span class="math-container">$E:=M^{\{0,\:\ldots\:,\:,k\}}$</span>, <span class="math-container">$\mathcal E:={\mathcal B(M)}^{\otimes\{0,\:\ldots\:,\:,k\}}$</span> and <span class="math-container">$\lambda:=\sigma_M^{\otimes\{0,\:\ldots\:,\:,k\}}$</span></li>
<li><span class="math-container">$f:E\to[0,\infty)^3$</span> denote the measurement contribution function</li>
<li><span class="math-container">$q$</span> be the probability density on <span class="math-container">$(E,\mathcal E,\lambda)$</span> corresponding to the <span class="math-container">$(s,t)$</span>-strategy satisfying <span class="math-container">$\{q=0\}\subseteq\{f=0\}$</span></li>
</ul>
<p>Once more, for simplicity, assume that the generated <span class="math-container">$(E,\mathcal E)$</span>-valued chain <span class="math-container">$(X_n)_{n\in\mathbb N}$</span> is independent and <span class="math-container">$$X_n\sim q\lambda\;\;\;\text{for all }n\in\mathbb N.$$</span> If it's easier to understand for you, you might want to take a look at the <code>importance_sampling_integrator::render()</code> method in <a href="https://coliru.stacked-crooked.com/view?id=7f55a67c5e1dab10" rel="nofollow noreferrer">this code</a>, where I've implemented these simplifications.</p>
<p>Now let's consider the measurement of a single pixel value <span class="math-container">$$I:=\int hf\:{\rm d}\lambda,$$</span> where <span class="math-container">$h:E\to[0,\infty)$</span> is the image reconstruction filter corresponding to this pixel. Assume (as PBRT does) that <span class="math-container">$h$</span> is a Box filter with radius 1/2. So, if <span class="math-container">$\psi:E\to R$</span> is the canonical mapping from the path space <span class="math-container">$E$</span> to the raster space <span class="math-container">$R=[0,a)\times[0,b)$</span>, where <span class="math-container">$a\in\mathbb N$</span> and <span class="math-container">$b\in\mathbb N$</span> are the vertical and horizontal resolution of the image, respectively, and the pixel we've fixed is pixel <span class="math-container">$(i,j)\in J:=\{0,\ldots,a-1\}\times\{0,\ldots,b-1\}$</span> then <span class="math-container">$h$</span> is simply the indicator function of the set <span class="math-container">$$B:=\left\{x\in E:\psi(x)\in\underbrace{[i,i+1)\times[j,j+1)}_{=:\:R_{(i,\:j)}}\right\},$$</span> i.e. <span class="math-container">$h=1_B$</span>. Now let <span class="math-container">$$U_n:=\psi(X_n)\;\;\;\text{for }n\in\mathbb N$$</span> and note that, by construction of <span class="math-container">$q$</span>, <span class="math-container">$$U_n\sim\mathcal U_R\;\;\;\text{for all }n\in\mathbb N\tag1,$$</span> where <span class="math-container">$\mathcal U_R$</span> denotes the uniform distribution on <span class="math-container">$R$</span>. This immediately yields that <span class="math-container">$$(q\lambda)(B)=\mathcal U_R\left(R_{(i,\:j)}\right)=\frac1{|J|}\tag2.$$</span></p>
<blockquote>
<p>Let <span class="math-container">$$g(x):=h(x)\left.\begin{cases}\displaystyle\frac fq(x)&amp;\text{, if }q(x)&gt;0\\0&amp;\text{, otherwise}\end{cases}\right\}\;\;\;\text{for }x\in E.$$</span> Now the naive estimator for <span class="math-container">$I$</span> is <span class="math-container">$$\frac1n\sum_{i=1}^ng(X_i)\xrightarrow{n\to\infty} I\;\;\;\text{almost surely}\tag3.$$</span> However, if I implement this (which corresponds to replacing <code>m_camera-&gt;film-&gt;WriteImage(pbrt::Float{ 1 } / m_sampler-&gt;samplesPerPixel);</code> in the last line of <code>importance_sampling_integrator::render()</code> by <code>m_camera-&gt;film-&gt;WriteImage(pbrt::Float{ 1 } / m_n);</code>), I end up with a black image. This is not surprising, since due to the very small support of <span class="math-container">$h$</span> compared to the whole image, only very few terms contribute to the sum in <span class="math-container">$(3)$</span>.</p>
</blockquote>
<p>So, it seems like that one needs to consider only the visits of <span class="math-container">$(X_n)_{n\in\mathbb N}$</span> to the set <span class="math-container">$B$</span>, i.e. consider the process <span class="math-container">$$Y_k:=X_{\tau_k}\;\;\;\text{for }k\in\mathbb N$$</span> instead, where <span class="math-container">$\tau_0:=0$</span>, <span class="math-container">$$\tau_k:=\inf\left\{n&gt;\tau_{k-1}:X_n\in B\right\}\;\;\;\text{for }k\in\mathbb N.$$</span> It's easy to see that <span class="math-container">$(Y_k)_{k\in\mathbb N}$</span> is independent with <span class="math-container">$$Y_n\sim (q\lambda)\left[\;\cdot\mid B\right]\;\;\;\text{for all }k\in\mathbb N\tag4.$$</span> Moreover, <span class="math-container">$$\frac1k\sum_{i=1}^kg(Y_i)\xrightarrow{k\to\infty}(q\lambda)\left[g\mid B\right]=|J|I\tag5$$</span> by <span class="math-container">$(2)$</span>.</p>
<blockquote>
<p>Now what the code does is independently drawing <span class="math-container">$X_1,\ldots,X_n\sim q\lambda$</span>, where <span class="math-container">$$n=n_0|J|\tag6$$</span> for some <span class="math-container">$n_0\in\mathbb N$</span> (called <code>mutationsPerPixel</code> in PBRT).</p>
</blockquote>
<p>We may consider the number <span class="math-container">$$N_n:=\sum_{i=1}^n1_B(X_i)$$</span> of visits to <span class="math-container">$B$</span> up to time <span class="math-container">$n$</span> and note that <span class="math-container">$$\operatorname E\left[N_n\right]=\frac n{|J|}\tag7$$</span> and <span class="math-container">$$\frac{N_n}n\xrightarrow{n\to\infty}\frac1{|J|}\;\;\;\text{almost surely}.\tag8$$</span></p>
<blockquote>
<p>Now PBRT (and the code I've provided above as well) uses the estimator <span class="math-container">$$\frac1{n_0}\sum_{i=1}^ng(X_i)=\frac1{\operatorname E\left[N_n\right]}\sum_{i=1}^{N_n}g(Y_i),$$</span> but I absolutely don't understand why this works. From <span class="math-container">$(5)$</span> we should obtain <span class="math-container">$$\frac1{\operatorname E\left[N_n\right]}\sum_{i=1}^{N_n}g(Y_i)=\frac{|J|}n\sum_{i=1}^ng(X_i)\xrightarrow{n\to\infty}|J|I\tag9$$</span> almost surely, but the left-hand side of <span class="math-container">$(9)$</span>, dividied by <span class="math-container">$|J|$</span>, is once again the pracitcally not working estimator <span class="math-container">$(3)$</span>.</p>
<p>What am I missing?</p>
</blockquote>
<p><strong>EDIT</strong>: Just to be completely sure I've built the image array by myself using the (supposed to be correct) estimator <span class="math-container">$(5)$</span>:</p>
<pre class="lang-cpp prettyprint-override"><code>void render(pbrt::Scene const&amp; scene)
{
    auto const light_distribution = pbrt::ComputeLightPowerDistribution(scene);

    std::unordered_map&lt;pbrt::Light const*, std::size_t&gt; light_to_index;
    for (std::size_t i = 0; i &lt; scene.lights.size(); ++i)
        light_to_index[scene.lights[i].get()] = i;

    std::vector&lt;vertex&gt; z(m_t),
        y(m_s);

    std::size_t const a = m_camera-&gt;film-&gt;GetSampleBounds().pMax.y,
        b = m_camera-&gt;film-&gt;GetSampleBounds().pMax.x;
    std::vector&lt;pbrt::Spectrum&gt; image(a * b);
    std::vector&lt;std::size_t&gt; n(a * b);

    pbrt::MemoryArena arena;
    for (std::size_t i = 0; i &lt; m_n; ++i)
    {
        pbrt::Point2f raster_point;
        auto const contribution = sample_path(scene, *m_camera, *light_distribution, light_to_index,
            arena, m_k, m_s, *m_sampler, raster_point, z.data(), y.data());
        arena.Reset();

        std::size_t const index = static_cast&lt;std::size_t&gt;(raster_point.y) * b + static_cast&lt;std::size_t&gt;(raster_point.x);

        image[index] += contribution.value_or(0);
        ++n[index];
    }

    for (std::size_t i = 0; i &lt; n.size(); ++i)
    {
        if (n[i] &gt; 0)
            image[i] = image[i] / (n[i] /** a * b*/);
    }
    m_camera-&gt;film-&gt;SetImage(image.data());
    m_camera-&gt;film-&gt;WriteImage();
}
</code></pre>
<p>At the end, I should devide each pixel value by <span class="math-container">$|J|=ab$</span>. However, if I uncomment the devision in the code, I end up with a black image again. So, my guess right now is that the values provided by <code>BxDF::Sample_f()</code> are not what I thought they are. Maybe they are &quot;normalized&quot; and maybe this is what they mean by &quot;<em>Each Metropolis iteration within &lt;&gt; has splatted contributions with weighted unit luminance</em>&quot;.</p>

CC BY-SA 4.0
2020-03-05T10:26:37.107Z
Are you certain they have what you refer to as $g$ in their code/formulation? It could be that they have $\frac{hf}{q'}$ where $q'$ is the probability without the first vertex (for which it is $\frac{1}{|J|}$). If that is the case then what you wrote and what they have would be equivalent, since what you have is really: $\frac{1}{spp}\sum \frac{h(X_i)f(X_i)}{q'(X_i)}$. $q'$ would be the probability of picking the remaining dimensions of the sample (without the factor coming from picking a uniformly a sample on the film) - that is $q(X_i) = \frac{q'(X_i)}{|J|}$.
lightxbulb
CC BY-SA 4.0
2020-03-05T10:47:02.367Z
With regards to your new edit - how does `sample_path` compute the probability? If it doesn't include the $\frac{1}{|J|}$ probability multiplication (the contribution being divided by it), then what happens is exactly what I outlined above.
lightxbulb
CC BY-SA 4.0
2020-03-05T10:48:52.467Z
@lightxbulb That's a good point. Well, they don't compute $f$ and $q$ separately, but only the fraction $\frac fq$ (which is what I called `contribution` and they refer to a $\beta$ in the book). The relevant $β_1⋯β_k$ here would be the one for the second vertex $z_1$ on the camera subpath. This $\beta$ is always equal to $1$. Formally, it should be $$\beta_1:=\frac{W_e(z_1\to z_0)}{p_0(z_0,\omega_{z_0\to z_1})},$$ where $p_0(z_0,\omega_{z_0\to z_1})$ is the density of choosing the first ray $r_1=(z_0,\omega_{z_0\to z_1})$ on the camera subpath with respect to the path throughput measure.
0xbadf00d
CC BY-SA 4.0
2020-03-05T10:49:03.240Z
@lightxbulb Now, if I understood them correctly, they defined $W_e(z_0\to z_1)$ (for a perspective camera) to be equal to this density; see equation 16.3 here: http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/The_Path-Space_Measurement_Equation.html#SamplingCameras). Maybe the normalization of $W_e(z_0\to z_1)$ is the source of the error. What do you think?
0xbadf00d
CC BY-SA 4.0
2020-03-05T10:53:28.363Z
Then they simply do not have this $\frac{1}{|J|}$ in the probability, since they have taken it outside. This means that their contribution is divided by $q'$, instead of $q$, which explains the $\frac{1}{spp}$. They basically simply took out the raster probability out of the sum, and canceled it out. Note that this makes sense, especially since this makes it consistent with the other pixel estimators. In general if you had pixels with varying area, you would instead have $\frac{A_j}{\sum_k A_k}$ for example. Simply put - you're missing the $\frac{1}{|J|}$ in your probability in your code.
lightxbulb
CC BY-SA 4.0
2020-03-05T12:51:40.713Z
Any progress on that - did you find whether they do not have this probability in their code? Basically, was my assumption correct, or does this arise from somewhere else?
lightxbulb
CC BY-SA 4.0
2020-03-18T16:04:50.720Z
@lightxbulb Sorry, I couldn't return to this problem for a while. The source of the error was indeed the normalization of the importance function. So, multiplying the importance function by the area solves the problem.
0xbadf00d
CC BY-SA 4.0
2020-03-19T17:02:10.737Z
I would suggest you formulate an answer to your question yourself, since you're more familiar with the PBRT details you were referring to. It may be useful for someone in the future.
lightxbulb
<raytracing><global-illumination><pbr><monte-carlo><pbrt>
Scaling of the final image in Metropolis Light Transport
0xbadf00d
# Scaling of the final image in Metropolis Light Transport

(Asked by: username_0 on 2020-02-19)

I don't understand why the PBR implementation of Metorpolis Light Transport scales the final image by `b / mutationsPerPixel`.

The authors write:

> *Each Metropolis iteration within "Run nChains Markov chains in parallel" has splatted contributions with weighted unit luminance to the Film so that the final average film luminance before Film::WriteImage() is exactly equal to mutationsPerPixel.*

(see <http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/Metropolis_Light_Transport.html> at the very end of the page.)

For simplicity, assume that we are considering a fixed path length $k\in\mathbb N$ and a single fixed $(s,t)$-strategy. Formally, let

* $M$ denote the scene surface set, $\mathcal B(M)$ denote the Borel $\sigma$-algebra on $M$ and $\sigma\_M$ denote the surface measure on $\mathcal B(M)$
* $E:=M^{\{0,\:\ldots\:,\:,k\}}$, $\mathcal E:={\mathcal B(M)}^{\otimes\{0,\:\ldots\:,\:,k\}}$ and $\lambda:=\sigma\_M^{\otimes\{0,\:\ldots\:,\:,k\}}$
* $f:E\to[0,\infty)^3$ denote the measurement contribution function
* $q$ be the probability density on $(E,\mathcal E,\lambda)$ corresponding to the $(s,t)$-strategy satisfying $\{q=0\}\subseteq\{f=0\}$

Once more, for simplicity, assume that the generated $(E,\mathcal E)$-valued chain $(X\_n)\_{n\in\mathbb N}$ is independent and $$X\_n\sim q\lambda\;\;\;\text{for all }n\in\mathbb N.$$ If it's easier to understand for you, you might want to take a look at the `importance_sampling_integrator::render()` method in [this code](https://coliru.stacked-crooked.com/view?id=7f55a67c5e1dab10), where I've implemented these simplifications.

Now let's consider the measurement of a single pixel value $$I:=\int hf\:{\rm d}\lambda,$$ where $h:E\to[0,\infty)$ is the image reconstruction filter corresponding to this pixel. Assume (as PBRT does) that $h$ is a Box filter with radius 1/2. So, if $\psi:E\to R$ is the canonical mapping from the path space $E$ to the raster space $R=[0,a)\times[0,b)$, where $a\in\mathbb N$ and $b\in\mathbb N$ are the vertical and horizontal resolution of the image, respectively, and the pixel we've fixed is pixel $(i,j)\in J:=\{0,\ldots,a-1\}\times\{0,\ldots,b-1\}$ then $h$ is simply the indicator function of the set $$B:=\left\{x\in E:\psi(x)\in\underbrace{[i,i+1)\times[j,j+1)}\_{=:\:R\_{(i,\:j)}}\right\},$$ i.e. $h=1\_B$. Now let $$U\_n:=\psi(X\_n)\;\;\;\text{for }n\in\mathbb N$$ and note that, by construction of $q$, $$U\_n\sim\mathcal U\_R\;\;\;\text{for all }n\in\mathbb N\tag1,$$ where $\mathcal U\_R$ denotes the uniform distribution on $R$. This immediately yields that $$(q\lambda)(B)=\mathcal U\_R\left(R\_{(i,\:j)}\right)=\frac1{|J|}\tag2.$$

> Let $$g(x):=h(x)\left.\begin{cases}\displaystyle\frac fq(x)&\text{, if }q(x)>0\\0&\text{, otherwise}\end{cases}\right\}\;\;\;\text{for }x\in E.$$ Now the naive estimator for $I$ is $$\frac1n\sum\_{i=1}^ng(X\_i)\xrightarrow{n\to\infty} I\;\;\;\text{almost surely}\tag3.$$ However, if I implement this (which corresponds to replacing `m_camera->film->WriteImage(pbrt::Float{ 1 } / m_sampler->samplesPerPixel);` in the last line of `importance_sampling_integrator::render()` by `m_camera->film->WriteImage(pbrt::Float{ 1 } / m_n);`), I end up with a black image. This is not surprising, since due to the very small support of $h$ compared to the whole image, only very few terms contribute to the sum in $(3)$.

So, it seems like that one needs to consider only the visits of $(X\_n)\_{n\in\mathbb N}$ to the set $B$, i.e. consider the process $$Y\_k:=X\_{\tau\_k}\;\;\;\text{for }k\in\mathbb N$$ instead, where $\tau\_0:=0$, $$\tau\_k:=\inf\left\{n>\tau\_{k-1}:X\_n\in B\right\}\;\;\;\text{for }k\in\mathbb N.$$ It's easy to see that $(Y\_k)\_{k\in\mathbb N}$ is independent with $$Y\_n\sim (q\lambda)\left[\;\cdot\mid B\right]\;\;\;\text{for all }k\in\mathbb N\tag4.$$ Moreover, $$\frac1k\sum\_{i=1}^kg(Y\_i)\xrightarrow{k\to\infty}(q\lambda)\left[g\mid B\right]=|J|I\tag5$$ by $(2)$.

> Now what the code does is independently drawing $X\_1,\ldots,X\_n\sim q\lambda$, where $$n=n\_0|J|\tag6$$ for some $n\_0\in\mathbb N$ (called `mutationsPerPixel` in PBRT).

We may consider the number $$N\_n:=\sum\_{i=1}^n1\_B(X\_i)$$ of visits to $B$ up to time $n$ and note that $$\operatorname E\left[N\_n\right]=\frac n{|J|}\tag7$$ and $$\frac{N\_n}n\xrightarrow{n\to\infty}\frac1{|J|}\;\;\;\text{almost surely}.\tag8$$

> Now PBRT (and the code I've provided above as well) uses the estimator $$\frac1{n\_0}\sum\_{i=1}^ng(X\_i)=\frac1{\operatorname E\left[N\_n\right]}\sum\_{i=1}^{N\_n}g(Y\_i),$$ but I absolutely don't understand why this works. From $(5)$ we should obtain $$\frac1{\operatorname E\left[N\_n\right]}\sum\_{i=1}^{N\_n}g(Y\_i)=\frac{|J|}n\sum\_{i=1}^ng(X\_i)\xrightarrow{n\to\infty}|J|I\tag9$$ almost surely, but the left-hand side of $(9)$, dividied by $|J|$, is once again the pracitcally not working estimator $(3)$.
>
> What am I missing?

**EDIT**: Just to be completely sure I've built the image array by myself using the (supposed to be correct) estimator $(5)$:

```cpp
void render(pbrt::Scene const& scene)
{
    auto const light_distribution = pbrt::ComputeLightPowerDistribution(scene);

    std::unordered_map<pbrt::Light const*, std::size_t> light_to_index;
    for (std::size_t i = 0; i < scene.lights.size(); ++i)
        light_to_index[scene.lights[i].get()] = i;

    std::vector<vertex> z(m_t),
        y(m_s);

    std::size_t const a = m_camera->film->GetSampleBounds().pMax.y,
        b = m_camera->film->GetSampleBounds().pMax.x;
    std::vector<pbrt::Spectrum> image(a * b);
    std::vector<std::size_t> n(a * b);

    pbrt::MemoryArena arena;
    for (std::size_t i = 0; i < m_n; ++i)
    {
        pbrt::Point2f raster_point;
        auto const contribution = sample_path(scene, *m_camera, *light_distribution, light_to_index,
            arena, m_k, m_s, *m_sampler, raster_point, z.data(), y.data());
        arena.Reset();

        std::size_t const index = static_cast<std::size_t>(raster_point.y) * b + static_cast<std::size_t>(raster_point.x);

        image[index] += contribution.value_or(0);
        ++n[index];
    }

    for (std::size_t i = 0; i < n.size(); ++i)
    {
        if (n[i] > 0)
            image[i] = image[i] / (n[i] /** a * b*/);
    }
    m_camera->film->SetImage(image.data());
    m_camera->film->WriteImage();
}

```

At the end, I should devide each pixel value by $|J|=ab$. However, if I uncomment the devision in the code, I end up with a black image again. So, my guess right now is that the values provided by `BxDF::Sample_f()` are not what I thought they are. Maybe they are "normalized" and maybe this is what they mean by "*Each Metropolis iteration within <> has splatted contributions with weighted unit luminance*".

**Tags:** raytracing, global-illumination, pbr, monte-carlo, pbrt

> **Comments:**
> **username_1**: Are you certain they have what you refer to as $g$ in their code/formulation? It could be that they have $\frac{hf}{q'}$ where $q'$ is the probability without the first vertex (for which it is $\frac{1}{|J|}$). If that is the case then what you wrote and what they have would be equivalent, since what you have is really: $\frac{1}{spp}\sum \frac{h(X_i)f(X_i)}{q'(X_i)}$. $q'$ would be the probability of picking the remaining dimensions of the sample (without the factor coming from picking a uniformly a sample on the film) - that is $q(X_i) = \frac{q'(X_i)}{|J|}$.  
> **username_1**: With regards to your new edit - how does `sample_path` compute the probability? If it doesn't include the $\frac{1}{|J|}$ probability multiplication (the contribution being divided by it), then what happens is exactly what I outlined above.  
> **username_0**: @username_1 That's a good point. Well, they don't compute $f$ and $q$ separately, but only the fraction $\frac fq$ (which is what I called `contribution` and they refer to a $\beta$ in the book). The relevant $β_1⋯β_k$ here would be the one for the second vertex $z_1$ on the camera subpath. This $\beta$ is always equal to $1$. Formally, it should be $$\beta_1:=\frac{W_e(z_1\to z_0)}{p_0(z_0,\omega_{z_0\to z_1})},$$ where $p_0(z_0,\omega_{z_0\to z_1})$ is the density of choosing the first ray $r_1=(z_0,\omega_{z_0\to z_1})$ on the camera subpath with respect to the path throughput measure.  
> **username_0**: @username_1 Now, if I understood them correctly, they defined $W_e(z_0\to z_1)$ (for a perspective camera) to be equal to this density; see equation 16.3 here: http://www.pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/The_Path-Space_Measurement_Equation.html#SamplingCameras). Maybe the normalization of $W_e(z_0\to z_1)$ is the source of the error. What do you think?  
> **username_1**: Then they simply do not have this $\frac{1}{|J|}$ in the probability, since they have taken it outside. This means that their contribution is divided by $q'$, instead of $q$, which explains the $\frac{1}{spp}$. They basically simply took out the raster probability out of the sum, and canceled it out. Note that this makes sense, especially since this makes it consistent with the other pixel estimators. In general if you had pixels with varying area, you would instead have $\frac{A_j}{\sum_k A_k}$ for example. Simply put - you're missing the $\frac{1}{|J|}$ in your probability in your code.  
> **username_1**: Any progress on that - did you find whether they do not have this probability in their code? Basically, was my assumption correct, or does this arise from somewhere else?  
> **username_0**: @username_1 Sorry, I couldn't return to this problem for a while. The source of the error was indeed the normalization of the importance function. So, multiplying the importance function by the area solves the problem.  
> **username_1**: I would suggest you formulate an answer to your question yourself, since you're more familiar with the PBRT details you were referring to. It may be useful for someone in the future.  

---

## 0 answers

