<p>Shoot me... After the fixes I posted above, I started debugging Mitsuba to see whether it does anything differently on a large scale. Of course it didn't; my code is completely correct, the dimness is due to this macro def:</p>

<pre><code>#define EPSILON   1e-5
</code></pre>

<p>This is used in divisions to avoid dividing with zero. Unfortunately, it accumulates to a large value during the calculations. Changing it to 1e-9 fixes everything:</p>

<p><a href="https://i.sstatic.net/URn4F.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/URn4F.jpg" alt="mis_final"></a></p>

<p>For future generations I leave the source code up (just remember to change this value).</p>

<p><strong>UPDATE:</strong></p>

<p>I found another mistake in my calculations, but it isn't apparent in this scene. Microsurface-based BRDFs are usually sampled according to the microfacet normal distribution function (D(h, a), in this case GGX).</p>

<p>Don't forget however, that you also have a shadowing-masking function (G(l, v, a)) which has to be evaluated according to the chosen microfacet distribution. This means that I can't just mix-and-match f.e. GGX sampling and the <em>Smith-Schlick</em> evaluation; I have to use the <em>Smith-GGX</em> solution:</p>

<pre><code>float G_Smith_GGX(float ndotl, float ndotv, float roughness)
{
    float a = roughness * roughness;
    float a2 = a * a;

    float lambda_l = sqrt(a2 + (1.0 - a2) * ndotl * ndotl);
    float lambda_v = sqrt(a2 + (1.0 - a2) * ndotv * ndotv);

    return (2.0 * ndotl * ndotv) / (ndotv * lambda_l + ndotl * lambda_v);
}
</code></pre>

<p>Which explains the UE4 approach, as:</p>

<pre><code>vec3 F = F_Schlick(F0, ldoth);
float G = G_Smith_GGX(ndotl, ndotv, roughness);

// PDF = (D(h) * dot(n, h)) / (4 * dot(v, h))
return (F * G * vdoth) / (ndotv * ndoth + EPSILON);
</code></pre>

<p>See <a href="https://schuttejoe.github.io/post/ggximportancesamplingpart1/" rel="nofollow noreferrer">this article</a> for the derivation.</p>

CC BY-SA 4.0
2020-02-04T00:50:55.270Z
Changing this value shouldn't really affect the behavior of your algorithm though. I think you should look into how you handle PDFs of zero. In fact, changing the epsilon in Mitsuba doesn't break MIS so your problem probably lies somewhere else.
Hubble
CC BY-SA 4.0
2020-02-04T11:43:37.907Z
simply put, I don't (have to) handle them :) Since I'm using the power heuristic, the MIS weight could be Inf or NaN only when both PDFs are zero (which is unlikely). Also I don't use Dirac deltas (yet).
Asylum
CC BY-SA 4.0
2020-02-27T13:45:34.340Z
Epsilon values are a fact of life under a floating-point regime, but I think any geometric problem should be able to *justify* a value based on the propagation of some measurement uncertainty in a system. Why 1e-9? Even something based on FLT_EPSILON (2^-23), or even HALF_EPSILON (2^-10), as a kind of intrinsic 'uncertainty', should yield something physically consistent when measurement error is propagated through a physical system.
Brett Hale
CC BY-SA 4.0
2020-02-27T13:50:34.940Z
I know that may come across as incredibly tedious, but I've been going through a lot of floating-point code and trying to quantify errors and failures in the domain of graphics. I mean, even the typical implementation of the inner product is subject to catastrophic cancellation... Otherwise, +1 for the care and feeding of your physically-based renderer!
Brett Hale
CC BY-SA 4.0
2020-03-05T14:17:20.673Z
I can't choose an arbitrarly small epsilon. For example the Smith-GGX function above tends to return zero for example at the silhouette of a sphere, which then messes up everything. Same thing happens with very small epsilon values. 1e-9 is "so far so good".
Asylum
CC BY-SA 4.0
2020-02-03T15:22:22.857Z
2020-02-27T11:35:18.773Z
2020-02-27T11:35:18.773Z
Asylum
<p>(possible duplicate)</p>

<p>I'm trying to implement multiple importance sampling, as described in the <a href="http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Direct_Lighting.html#fragment-SampleBSDFwithmultipleimportancesampling-0" rel="nofollow noreferrer">PBRT book</a> (no bouncing yet, I want to make MIS work first).</p>

<p>What my problem is, that while the BRDF sampling looks okay, the light sampling is much dimmer (yet according to the pictures in the book they should look the same in intensity).</p>

<p><a href="https://i.sstatic.net/W36nQm.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/W36nQm.jpg" alt="sampling the BRDF"></a>
<a href="https://i.sstatic.net/2zQz2m.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/2zQz2m.jpg" alt="sampling the light"></a></p>

<p>I uploaded the <em>(updated)</em> <a href="https://www.dropbox.com/s/bkprhax5dgwpmu2/FOR_STACKEXCHANGE_1.zip?dl=0" rel="nofollow noreferrer">compilable code</a> (VS 2015). The relevant parts can be found in <em>multipleimportance.frag</em> (SampleLightsExplicit) and <em>pbr_common.head</em> (ConeSampleSphere).</p>

<p><em>(note that I don't apply the MIS weights yet)</em></p>

<pre><code>for (int k = 0; k &lt; numLights; ++k) {
    SceneObject light = objects.data[k];

    center = vec3(light.toworld[3].xyz);
    radius = light.toworld[0][0];

    // importance sample light (xyz = direction, w = PDF)
    sample1 = ConeSampleSphere(p, center, radius, pixel, seed + float(k));

    // cast shadow ray
    vis = Visibility(p, sample1.xyz, k);
    ndotl = vis * max(0.0, dot(sample1.xyz, n));

    // evaluate BRDF
    fd = vec3(0.0); //obj.color.rgb * ONE_OVER_PI;
    fs = CookTorrance_General(sample1.xyz, v, n, vec3(0.04), roughness);

    sum += (light.color.rgb * (fd + fs) * ndotl) / sample1.w;
}
</code></pre>

<p>My question is why light sampling looks so dim? Did I misunderstand something?</p>

<p><strong>UPDATE</strong></p>

<p>Looking through the code I noticed some inconsistencies which probably originated from a typo in one of the older SIGGRAPH course notes (can't tell which one tho).</p>

<p>The PDF of the GGX distribution is:</p>

<pre><code>PDF = D(h) * dot(n, h)
</code></pre>

<p>While some implementations (including UE4) incorrectly write it as</p>

<pre><code>PDF = (D(h) * dot(n, h)) / (4 * dot(v, h))
</code></pre>

<p>which essentially means that they include the Cook-Torrance denominator into this variable (<em>ndotl</em> cancels out). Dividing with the correct PDF cancels out <em>D</em> in the Cook-Torrance model, which I forgot to account for, while the <em>1 / (4 * dot(v, h))</em> term goes into the <em>Vis_Schlick</em> function.</p>

<p>So regarding intensity the second picture is the "correct" one. The dimness is still a problem tho. Changing the Fresnel <em>F0</em> to 0.11 (as it supposedly is in the book) and applying the MIS weights, the picture looks a lot more promising, except for the topmost plate which should almost explicitly reflect the light sources:</p>

<p><a href="https://i.sstatic.net/lFnVt.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/lFnVt.jpg" alt="multiple importance sampling"></a></p>

<p>So this is <em>almost okay</em> now. I'm gonna look deeper into the MIS part to see why it makes those highlights disappear.</p>

CC BY-SA 4.0
2020-01-29T20:08:02.670Z
Are you accounting for the probability of choosing a light?
Hubble
CC BY-SA 4.0
2020-01-30T13:04:19.827Z
I'm not 'choosing' a light (in the above code), I accumulate the contribution of all lights. Also, this is reproable if I use just one light only.
Asylum
CC BY-SA 4.0
2020-02-01T06:44:35.740Z
I saw your code, you are not dividing by the `bsdf_pdf` and missing the `ndotl` factor while computing your bsdf sample. Also post the code for both the calculations here. Not everyone's gonna download the whole project.
gallickgunner
CC BY-SA 4.0
2020-02-01T10:32:48.423Z
because the denominator of the Cook-Torrance BRDF cancels it out. I found some other errors in the code tho, I update the post with my findings.
Asylum
<opengl><pathtracing>
Sampling the light vs. sampling the BRDF: difference in intensity
Asylum
# Sampling the light vs. sampling the BRDF: difference in intensity

(Asked by: username_0 on 2020-01-29)

(possible duplicate)

I'm trying to implement multiple importance sampling, as described in the [PBRT book](http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Direct_Lighting.html#fragment-SampleBSDFwithmultipleimportancesampling-0) (no bouncing yet, I want to make MIS work first).

What my problem is, that while the BRDF sampling looks okay, the light sampling is much dimmer (yet according to the pictures in the book they should look the same in intensity).

[![sampling the BRDF](https://i.sstatic.net/W36nQm.jpg)](https://i.sstatic.net/W36nQm.jpg)
[![sampling the light](https://i.sstatic.net/2zQz2m.jpg)](https://i.sstatic.net/2zQz2m.jpg)

I uploaded the *(updated)* [compilable code](https://www.dropbox.com/s/bkprhax5dgwpmu2/FOR_STACKEXCHANGE_1.zip?dl=0) (VS 2015). The relevant parts can be found in *multipleimportance.frag* (SampleLightsExplicit) and *pbr\_common.head* (ConeSampleSphere).

*(note that I don't apply the MIS weights yet)*

```
for (int k = 0; k < numLights; ++k) {
    SceneObject light = objects.data[k];

    center = vec3(light.toworld[3].xyz);
    radius = light.toworld[0][0];

    // importance sample light (xyz = direction, w = PDF)
    sample1 = ConeSampleSphere(p, center, radius, pixel, seed + float(k));

    // cast shadow ray
    vis = Visibility(p, sample1.xyz, k);
    ndotl = vis * max(0.0, dot(sample1.xyz, n));

    // evaluate BRDF
    fd = vec3(0.0); //obj.color.rgb * ONE_OVER_PI;
    fs = CookTorrance_General(sample1.xyz, v, n, vec3(0.04), roughness);

    sum += (light.color.rgb * (fd + fs) * ndotl) / sample1.w;
}

```

My question is why light sampling looks so dim? Did I misunderstand something?

**UPDATE**

Looking through the code I noticed some inconsistencies which probably originated from a typo in one of the older SIGGRAPH course notes (can't tell which one tho).

The PDF of the GGX distribution is:

```
PDF = D(h) * dot(n, h)

```

While some implementations (including UE4) incorrectly write it as

```
PDF = (D(h) * dot(n, h)) / (4 * dot(v, h))

```

which essentially means that they include the Cook-Torrance denominator into this variable (*ndotl* cancels out). Dividing with the correct PDF cancels out *D* in the Cook-Torrance model, which I forgot to account for, while the *1 / (4 \* dot(v, h))* term goes into the *Vis\_Schlick* function.

So regarding intensity the second picture is the "correct" one. The dimness is still a problem tho. Changing the Fresnel *F0* to 0.11 (as it supposedly is in the book) and applying the MIS weights, the picture looks a lot more promising, except for the topmost plate which should almost explicitly reflect the light sources:

[![multiple importance sampling](https://i.sstatic.net/lFnVt.jpg)](https://i.sstatic.net/lFnVt.jpg)

So this is *almost okay* now. I'm gonna look deeper into the MIS part to see why it makes those highlights disappear.

**Tags:** opengl, pathtracing

> **Comments:**
> **username_1**: Are you accounting for the probability of choosing a light?  
> **username_0**: I'm not 'choosing' a light (in the above code), I accumulate the contribution of all lights. Also, this is reproable if I use just one light only.  
> **username_2**: I saw your code, you are not dividing by the `bsdf_pdf` and missing the `ndotl` factor while computing your bsdf sample. Also post the code for both the calculations here. Not everyone's gonna download the whole project.  
> **username_0**: because the denominator of the Cook-Torrance BRDF cancels it out. I found some other errors in the code tho, I update the post with my findings.  

---

## 1 answers

### Answer by username_0 on 2020-02-03. Score: 2 | Accepted answer

Shoot me... After the fixes I posted above, I started debugging Mitsuba to see whether it does anything differently on a large scale. Of course it didn't; my code is completely correct, the dimness is due to this macro def:

```
#define EPSILON   1e-5

```

This is used in divisions to avoid dividing with zero. Unfortunately, it accumulates to a large value during the calculations. Changing it to 1e-9 fixes everything:

[![mis_final](https://i.sstatic.net/URn4F.jpg)](https://i.sstatic.net/URn4F.jpg)

For future generations I leave the source code up (just remember to change this value).

**UPDATE:**

I found another mistake in my calculations, but it isn't apparent in this scene. Microsurface-based BRDFs are usually sampled according to the microfacet normal distribution function (D(h, a), in this case GGX).

Don't forget however, that you also have a shadowing-masking function (G(l, v, a)) which has to be evaluated according to the chosen microfacet distribution. This means that I can't just mix-and-match f.e. GGX sampling and the *Smith-Schlick* evaluation; I have to use the *Smith-GGX* solution:

```
float G_Smith_GGX(float ndotl, float ndotv, float roughness)
{
    float a = roughness * roughness;
    float a2 = a * a;

    float lambda_l = sqrt(a2 + (1.0 - a2) * ndotl * ndotl);
    float lambda_v = sqrt(a2 + (1.0 - a2) * ndotv * ndotv);

    return (2.0 * ndotl * ndotv) / (ndotv * lambda_l + ndotl * lambda_v);
}

```

Which explains the UE4 approach, as:

```
vec3 F = F_Schlick(F0, ldoth);
float G = G_Smith_GGX(ndotl, ndotv, roughness);

// PDF = (D(h) * dot(n, h)) / (4 * dot(v, h))
return (F * G * vdoth) / (ndotv * ndoth + EPSILON);

```

See [this article](https://schuttejoe.github.io/post/ggximportancesamplingpart1/) for the derivation.

> **Comments:**
> **username_1**: Changing this value shouldn't really affect the behavior of your algorithm though. I think you should look into how you handle PDFs of zero. In fact, changing the epsilon in Mitsuba doesn't break MIS so your problem probably lies somewhere else.  
> **username_0**: simply put, I don't (have to) handle them :) Since I'm using the power heuristic, the MIS weight could be Inf or NaN only when both PDFs are zero (which is unlikely). Also I don't use Dirac deltas (yet).  
> **username_3**: Epsilon values are a fact of life under a floating-point regime, but I think any geometric problem should be able to *justify* a value based on the propagation of some measurement uncertainty in a system. Why 1e-9? Even something based on FLT_EPSILON (2^-23), or even HALF_EPSILON (2^-10), as a kind of intrinsic 'uncertainty', should yield something physically consistent when measurement error is propagated through a physical system.  
> **username_3**: I know that may come across as incredibly tedious, but I've been going through a lot of floating-point code and trying to quantify errors and failures in the domain of graphics. I mean, even the typical implementation of the inner product is subject to catastrophic cancellation... Otherwise, +1 for the care and feeding of your physically-based renderer!  
> **username_0**: I can't choose an arbitrarly small epsilon. For example the Smith-GGX function above tends to return zero for example at the silhouette of a sphere, which then messes up everything. Same thing happens with very small epsilon values. 1e-9 is "so far so good".  

---

