Some tutorials on importance sampling specular terms that are out in the wild have what I found to be an information gap: the step from the PDF to the actual sampling function is missing. Hopefully this step-by-step guide can help out one or two other confused readers. I checked all integrals with Maxima and Wolfram Alpha. If you just have a free account at Wolfram Alpha, you might run into issues with exceeding the computation time, which is why I will also write down the commands to do everything in Maxima. If your result doesn’t look like the one noted here, try simplifying it first (in the Maxima menu Simplify -> Simplifiy Expression) or put it into Wolfram Alpha and check the Alternate form section!
The PDF for a normalized Phong BRDF with the specular power notation is:
We generate two functions to sample theta and phi independently. To do this, we first integrate along the domain of :
integrate((n+1)/(2 * %pi) * cos(t)^n * sin(t), p, 0, 2 * %pi)
This is the marginal density function of . Note that the extra at the end is there because we are dealing with differential solid angles in spherical coordinates. We can retreive the PDF for with the conditional probability , the conditional density function:
For isotropic NDFs this is always the case, no matter which PDF you will integrate over the domain of , so all you need to calculate for the next PDF is the function for .
Now that we have two functions for each variable, we can integrate both to generate a CDF each. The case of is trivial:
integrate(1/(2 * %pi), p, 0, s)
If we set to a random variable and solve for , we get:
Again, sampling around the azimuthal angle of an isotropic NDF is always the same because the material is rotationally invariant along this angle, so you don’t need to derive this formula again.
We now repeat the process and create a CDF for :
integrate((n+1) * cos(t)^n * sin(t), t, 0, s)
Just like in the case of , we set to a random variable again and solve for :
solve(1 - cos(s)^(n+1) = x, s)
Therefore, a GLSL shader which generates important directions for a Phong NDF from random, uniform values looks like this:
Note that since the expression is also a random variable in the same range.
You can for instance use a low-discrepancy sequence as a source for uniformly distributed random values xi and generate important samples which aren’t clumped.
Trowbridge-Reitz aka GGX
The GGX normal distribution function is part of a microfacet model, which gives you the probability of micro-surface normals oriented along a certain direction. The term has to be normalized though before integrating over the hemisphere to account for the projected micro-surface area:
The NDF itself is defined as:
Just like above, we start out with the PDF for GGX:
As in the case of Phong, we create two functions for and . First let’s create :
integrate((a^2 * cos(t) * sin(t))/(%pi * ((a^2−1) * cos(t)^2+1)^2), p, 0, 2 * %pi)
The integration for is the same as above, so we skip it and instead now create the CDF for :
integrate((2 * a^2 * cos(t) * sin(t))/((a^2−1) * cos(t)^2+1)^2, t, 0, s)
Setting the CDF to a random variable and solving for yields:
solve(2 * a^2 * (1/((2 * a^4−4 * a^2+2) * cos(s)^2+2 * a^2−2)−1/(2 * a^4−2 * a^2)) = x, s)
A simple GLSL function to generate important directions looks like this:
The ultimate goal of mathematics is to eliminate any need for intelligent thought.
Other NDFs can be used to create important samples with the exact same procedure: given a hemispherical PDF (i.e. the specular part of the BRDF), create two independent PDFs for and , integrate both from to (i.e. create a CDF for each), set the result to and solve for .
- Walter et al., Microfacet Models for Refraction through Rough Surfaces
- Wikipedia, Constructions of low-discrepancy sequences