<p>Think of this way: when integrating uniformly over the hemisphere, it's like you are importance-sampling with a constant pdf of <span class="math-container">$1/2\pi$</span>.</p>
<p>The multiplication by <span class="math-container">$2\pi$</span> at the end, then, can be seen as the division by the pdf, factored out of the sum because it's constant.</p>
<p>The constant pdf is <span class="math-container">$1/2\pi$</span> (as opposed to some other number) because it needs to be normalized to integrate to 1 over the hemisphere, which it does because the area of the hemisphere is <span class="math-container">$2\pi$</span>.</p>
<p>Your code looks reasonable from what I can see, but I would define the function <code>pdf</code> to include the normalization factor of <span class="math-container">$1/\pi$</span> for the cosine pdf:</p>
<pre><code>float cosinePDF(Vec3f sampleDir) { return sampleDir.z / PI; }
</code></pre>

CC BY-SA 4.0
2021-11-03T16:57:26.213Z
That's good explanation, that we actually divide by PDF in both cases, but it is constant value in case of uniform sampling, so we actually don't multiply by integration area even when we solve usual integrals, it is dividing by constant PDF. Thank you!
Emil Kabirov
CC BY-SA 4.0
2021-11-02T18:36:36.713Z
2021-11-02T18:43:23.050Z
2021-11-02T18:43:23.050Z
Nathan Reed
<p>I read some articles about BRDF integration with and without importance sampling and I don't understand one thing in equations.</p>
<ul>
<li>If we integrate a BRDF over hemisphere with uniform sample direction distribution, we evaluate BRDF at all generated directions, sum all values, divide the sum by N (number of samples) <strong>and then multiply the result by 2π, because we need to multiply the result by integration area, and 2π is solid angle of hemisphere</strong></li>
<li>If we integrate a BRDF over hemisphere using importance sampling, we generate sample directions from inverted CDF (Cumulative Distribution Function), evaluate BRDF at these samples dividing each value by PDF (Probability Distribution Function) of a corresponding sample, then sum values and divide by N <strong>and that's it, no 2π multiplication</strong></li>
</ul>
<p>I confirmed it numerically integrating Cook-Torrance BRDF and simple Lambertian cosine BRDF, <em><strong>but I don't get why 2π multiplication disappears when we use importance sampling from math perspective?</strong></em></p>
<p>Below is example with just cosine function integration. In importance sampling cosine function is also used as its own PDF. Also is my integration code for both cases correct?</p>
<pre><code>float f(Vec3f sampleDir) { return sampleDir.z; }
float pdf(Vec3f sampleDir) { return sampleDir.z; } // cosine function is also its PDF

void uniformIntegrationCos()
{
    double sum = 0.0;
    const int N = 1000;
    for (int i = 0; i &lt; N; ++i)
    {
        Vec3f sampleDir = randomHemisphere(i, N); // fibonacci hemisphere spiral

        float value = f(sampleDir);
        float weight = sampleDir.z; // NdotL == pdf(sample)
        sum += value * weight / PI / N; // divide by PI to normalize cosine-weighted PDF

    }
    const float HEMISPHERE_SOLID_ANGLE = 2.f * PI; // integration area
    sum *= HEMISPHERE_SOLID_ANGLE;

    std::cout &lt;&lt; &quot;Sum = &quot; &lt;&lt; sum &lt;&lt; std::endl;
}

void importanceIntegrationCos()
{
    double sum = 0.0;
    const int N = 1000;
    for (int i = 0; i &lt; N; ++i)
    {
        // generate sample direction using inversed CDF of cosine BRDF
        Vec2f r = randomHammersley(i, N);
        float phi = 2.f * PI * r.x;
        float sinTheta = sqrt(r.y);
        float cosTheta = sqrtf(1.f - sinTheta * sinTheta);

        Vec3f sampleDir;
        sampleDir.x = cos(phi) * sinTheta;
        sampleDir.y = sin(phi) * sinTheta;
        sampleDir.z = cosTheta;

        float value = f(sampleDir);
        sum += value / N; // BRDF is divided by it's PDF, so no NdotL weight and no PDF normalization by dividing by PI
    }

    //const float HEMISPHERE_SOLID_ANGLE = 2.f * PI; // integration area
    //sum *= HEMISPHERE_SOLID_ANGLE; // Not needed, why ???

    std::cout &lt;&lt; &quot;Sum = &quot; &lt;&lt; sum &lt;&lt; std::endl;
}

void main()
{
    uniformIntegrationCos();
    importanceIntegrationCos();
}
</code></pre>

CC BY-SA 4.0
2021-11-02T09:12:41.127Z
It's change of variables in integration, a different Jacobian arises which leads to a different term.
lightxbulb
CC BY-SA 4.0
2021-11-02T10:09:45.473Z
@lightxbulb but my code above is correct for both cases, right?
Emil Kabirov
CC BY-SA 4.0
2021-11-02T10:11:37.157Z
I didn't read your code and I don't plan to. It was just an answer to why $2\pi$ 'disappears'.
lightxbulb
<brdf><monte-carlo><importance-sampling><ibl><integral>
Integrating BRDF using Importance Sampling
Emil Kabirov
# Integrating BRDF using Importance Sampling

(Asked by: username_0 on 2021-11-02)

I read some articles about BRDF integration with and without importance sampling and I don't understand one thing in equations.

* If we integrate a BRDF over hemisphere with uniform sample direction distribution, we evaluate BRDF at all generated directions, sum all values, divide the sum by N (number of samples) **and then multiply the result by 2π, because we need to multiply the result by integration area, and 2π is solid angle of hemisphere**
* If we integrate a BRDF over hemisphere using importance sampling, we generate sample directions from inverted CDF (Cumulative Distribution Function), evaluate BRDF at these samples dividing each value by PDF (Probability Distribution Function) of a corresponding sample, then sum values and divide by N **and that's it, no 2π multiplication**

I confirmed it numerically integrating Cook-Torrance BRDF and simple Lambertian cosine BRDF, ***but I don't get why 2π multiplication disappears when we use importance sampling from math perspective?***

Below is example with just cosine function integration. In importance sampling cosine function is also used as its own PDF. Also is my integration code for both cases correct?

```
float f(Vec3f sampleDir) { return sampleDir.z; }
float pdf(Vec3f sampleDir) { return sampleDir.z; } // cosine function is also its PDF

void uniformIntegrationCos()
{
    double sum = 0.0;
    const int N = 1000;
    for (int i = 0; i < N; ++i)
    {
        Vec3f sampleDir = randomHemisphere(i, N); // fibonacci hemisphere spiral

        float value = f(sampleDir);
        float weight = sampleDir.z; // NdotL == pdf(sample)
        sum += value * weight / PI / N; // divide by PI to normalize cosine-weighted PDF

    }
    const float HEMISPHERE_SOLID_ANGLE = 2.f * PI; // integration area
    sum *= HEMISPHERE_SOLID_ANGLE;

    std::cout << "Sum = " << sum << std::endl;
}

void importanceIntegrationCos()
{
    double sum = 0.0;
    const int N = 1000;
    for (int i = 0; i < N; ++i)
    {
        // generate sample direction using inversed CDF of cosine BRDF
        Vec2f r = randomHammersley(i, N);
        float phi = 2.f * PI * r.x;
        float sinTheta = sqrt(r.y);
        float cosTheta = sqrtf(1.f - sinTheta * sinTheta);

        Vec3f sampleDir;
        sampleDir.x = cos(phi) * sinTheta;
        sampleDir.y = sin(phi) * sinTheta;
        sampleDir.z = cosTheta;

        float value = f(sampleDir);
        sum += value / N; // BRDF is divided by it's PDF, so no NdotL weight and no PDF normalization by dividing by PI
    }

    //const float HEMISPHERE_SOLID_ANGLE = 2.f * PI; // integration area
    //sum *= HEMISPHERE_SOLID_ANGLE; // Not needed, why ???

    std::cout << "Sum = " << sum << std::endl;
}

void main()
{
    uniformIntegrationCos();
    importanceIntegrationCos();
}

```

**Tags:** brdf, monte-carlo, importance-sampling, ibl, integral

> **Comments:**
> **username_1**: It's change of variables in integration, a different Jacobian arises which leads to a different term.  
> **username_0**: @username_1 but my code above is correct for both cases, right?  
> **username_1**: I didn't read your code and I don't plan to. It was just an answer to why $2\pi$ 'disappears'.  

---

## 1 answers

### Answer by username_2 on 2021-11-02. Score: 2 | Accepted answer

Think of this way: when integrating uniformly over the hemisphere, it's like you are importance-sampling with a constant pdf of $1/2\pi$.

The multiplication by $2\pi$ at the end, then, can be seen as the division by the pdf, factored out of the sum because it's constant.

The constant pdf is $1/2\pi$ (as opposed to some other number) because it needs to be normalized to integrate to 1 over the hemisphere, which it does because the area of the hemisphere is $2\pi$.

Your code looks reasonable from what I can see, but I would define the function `pdf` to include the normalization factor of $1/\pi$ for the cosine pdf:

```
float cosinePDF(Vec3f sampleDir) { return sampleDir.z / PI; }

```

> **Comments:**
> **username_0**: That's good explanation, that we actually divide by PDF in both cases, but it is constant value in case of uniform sampling, so we actually don't multiply by integration area even when we solve usual integrals, it is dividing by constant PDF. Thank you!  

---

