<p>From all other implementations I have seen, you generally sample twice, once in the Y dimension to select a row and then once in the X dimension to selct the pixel from this row.</p>
<p>I am not sure if this is necissary, maybe you're one dimensional sampling is fine.</p>
<p>I think you also need to multiply your pdf so that it is relative to solid angle (so it's total sums to 4pi).</p>
<p>You should be able to do this by dividing by (2 * Pi * Pi * sinTheta).</p>
<p>PBRT is probably the best textbook to help you. You can find it online here:</p>
<p><a href="http://www.pbr-book.org/" rel="nofollow noreferrer">http://www.pbr-book.org/</a></p>
<p>The source code is also available. Here is the code for the environment light:</p>
<p><a href="https://github.com/mmp/pbrt-v3/blob/aaa552a4b9cbf9dccb71450f47b268e0ed6370e2/src/lights/infinite.cpp" rel="nofollow noreferrer">https://github.com/mmp/pbrt-v3/blob/aaa552a4b9cbf9dccb71450f47b268e0ed6370e2/src/lights/infinite.cpp</a></p>
<p>and the code for the Distribution2D sampler:</p>
<p><a href="https://github.com/mmp/pbrt-v3/blob/aaa552a4b9cbf9dccb71450f47b268e0ed6370e2/src/core/sampling.h" rel="nofollow noreferrer">https://github.com/mmp/pbrt-v3/blob/aaa552a4b9cbf9dccb71450f47b268e0ed6370e2/src/core/sampling.h</a></p>

CC BY-SA 4.0
2021-05-01T13:18:22.733Z
yeah, just reading that textbook and implementing the same way helped. thanks.
Pachiko
CC BY-SA 4.0
2021-04-15T10:51:05.813Z
2021-04-15T10:51:05.813Z
Peter
<p>beginner here.</p>
<p>I am implementing importance sampling for environment maps in Scotty3D, a CMU CS project (but I am not a student).</p>
<p>The task can be found <a href="https://cmu-graphics.github.io/Scotty3D/pathtracer/environment_lighting" rel="nofollow noreferrer">here</a></p>
<p>I have a problem with the sample's pdf, it is unusually small and that results in the object being rendered white even if its albedo is not white.</p>
<p>Here is how I construct the CDF:</p>
<pre><code>// Calculate Lsintheta across the image. Accumulate to normalize the pdf to 1 later.
for (size_t j = 0; j &lt; h; j++) {
    for (size_t i = 0; i &lt; w; i++) {
        // 0.5f at the pixel center. Else we never sample top and bottom rows
        float Lsintheta = image.at(i, j).luma() * sin(PI_F*(j + 0.5f)/h);
        total += Lsintheta;
        cdf.push_back(total); // Joint Cdf. P(theta, phi)
    }
}
</code></pre>
<p>So <code>total</code> would have all the pixels' luminosities. <code>cdf</code> would actually be the cumulative luminosity.</p>
<p>Next is the sampling and calculating the actual pdf.</p>
<pre><code>float Xi = RNG::unit(); // [0, 1]
auto lower = std::lower_bound(cdf.begin(), cdf.end(), Xi*total); // cdf is not normalized yet. Scale the RV
auto index = std::distance(cdf.begin(), lower);
out_pdf = *lower;
if (index &gt; 0) out_pdf -= cdf[index - 1]; // pdf[index] = cdf[index] - cdf[index - 1]
out_pdf /= total;
</code></pre>
<p><code>Xi</code> is a uniformly sampled real number from 0 to 1. Then it is multiplied by the <code>total</code> luminosity in the image to get the sampled accumulated luminosity. Use <code>std::lower_bound</code> to get the nearest accumulated luminosity greater than or equal to it. Find the index of this nearest accumulated luminosity using <code>std::distance</code>. Then minus with the previous accumulated luminosity to get the actual luminosity in this pixel. Divide by <code>total</code> to get the <code>pdf</code>.</p>
<p>This doesn't work, but if I just do <code>out_pdf = lower/total</code> it can at least show the right color.
What am I doing/understanding wrong?</p>

CC BY-SA 4.0
2021-09-13T18:47:54.423Z
The code here looks fine. When you say it "doesn't work", what do you mean? What happens?
Nathan Reed
CC BY-SA 4.0
2021-09-14T06:25:00.850Z
@NathanReed IIRC, it was the color being white regardless of the object's albedo. I think the pdf calculated was wrong (too small).
Pachiko
CC BY-SA 4.0
2021-09-14T19:14:02.397Z
Hmm. Well, `out_pdf = lower/total` looks simply wrong AFAICT, as that is a CDF not a PDF. Peter made the point about having it sum up to 4π instead of 1, which would mean the pdf values were too small by about a factor of 12. Could that account for the errors?
Nathan Reed
<c++>
Environment map importance sampling (beginner)
Pachiko
# Environment map importance sampling (beginner)

(Asked by: username_0 on 2021-04-15)

beginner here.

I am implementing importance sampling for environment maps in Scotty3D, a CMU CS project (but I am not a student).

The task can be found [here](https://cmu-graphics.github.io/Scotty3D/pathtracer/environment_lighting)

I have a problem with the sample's pdf, it is unusually small and that results in the object being rendered white even if its albedo is not white.

Here is how I construct the CDF:

```
// Calculate Lsintheta across the image. Accumulate to normalize the pdf to 1 later.
for (size_t j = 0; j < h; j++) {
    for (size_t i = 0; i < w; i++) {
        // 0.5f at the pixel center. Else we never sample top and bottom rows
        float Lsintheta = image.at(i, j).luma() * sin(PI_F*(j + 0.5f)/h);
        total += Lsintheta;
        cdf.push_back(total); // Joint Cdf. P(theta, phi)
    }
}

```

So `total` would have all the pixels' luminosities. `cdf` would actually be the cumulative luminosity.

Next is the sampling and calculating the actual pdf.

```
float Xi = RNG::unit(); // [0, 1]
auto lower = std::lower_bound(cdf.begin(), cdf.end(), Xi*total); // cdf is not normalized yet. Scale the RV
auto index = std::distance(cdf.begin(), lower);
out_pdf = *lower;
if (index > 0) out_pdf -= cdf[index - 1]; // pdf[index] = cdf[index] - cdf[index - 1]
out_pdf /= total;

```

`Xi` is a uniformly sampled real number from 0 to 1. Then it is multiplied by the `total` luminosity in the image to get the sampled accumulated luminosity. Use `std::lower_bound` to get the nearest accumulated luminosity greater than or equal to it. Find the index of this nearest accumulated luminosity using `std::distance`. Then minus with the previous accumulated luminosity to get the actual luminosity in this pixel. Divide by `total` to get the `pdf`.

This doesn't work, but if I just do `out_pdf = lower/total` it can at least show the right color.
What am I doing/understanding wrong?

**Tags:** c++

> **Comments:**
> **username_1**: The code here looks fine. When you say it "doesn't work", what do you mean? What happens?  
> **username_0**: @username_1 IIRC, it was the color being white regardless of the object's albedo. I think the pdf calculated was wrong (too small).  
> **username_1**: Hmm. Well, `out_pdf = lower/total` looks simply wrong AFAICT, as that is a CDF not a PDF. username_2 made the point about having it sum up to 4π instead of 1, which would mean the pdf values were too small by about a factor of 12. Could that account for the errors?  

---

## 1 answers

### Answer by username_2 on 2021-04-15. Score: 1

From all other implementations I have seen, you generally sample twice, once in the Y dimension to select a row and then once in the X dimension to selct the pixel from this row.

I am not sure if this is necissary, maybe you're one dimensional sampling is fine.

I think you also need to multiply your pdf so that it is relative to solid angle (so it's total sums to 4pi).

You should be able to do this by dividing by (2 \* Pi \* Pi \* sinTheta).

PBRT is probably the best textbook to help you. You can find it online here:

<http://www.pbr-book.org/>

The source code is also available. Here is the code for the environment light:

<https://github.com/mmp/pbrt-v3/blob/aaa552a4b9cbf9dccb71450f47b268e0ed6370e2/src/lights/infinite.cpp>

and the code for the Distribution2D sampler:

<https://github.com/mmp/pbrt-v3/blob/aaa552a4b9cbf9dccb71450f47b268e0ed6370e2/src/core/sampling.h>

> **Comments:**
> **username_0**: yeah, just reading that textbook and implementing the same way helped. thanks.  

---

