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();
}