The following is a transcription of one of my independent projects while I was a student at SCAD. The original page is still available here. With RPS 17 and beyond, most of these features are standard, and I haven't continued development as I no longer have a RenderMan license at home. I hope this information is useful!
At first, I was eager to use uniform sampling to quickly get some results from my BRDF implementation. To do this, I used a gather() loop simply to generate random ray directions, then I evaluated the BRDF with those random ray directions as l. Many of these samples go through the entire BRDF calculation only to be discarded because they affect a normal of the microfacet structure not visible to the view direction for one reason or another. An interesting side effect of this is that for very rough evaluations, I could get decent renders with reasonable sample counts. As I approached mirror reflection, however, the number of samples required skyrocketed. This is expected, since the area of the reflection is so much smaller for mirror reflections, and I'm still just shooting rays all over the place and most of them are being discarded. Obviously a better method is needed.
To extend these BRDF models from simple punctual light support, the BRDF must be sampled. The first approach is a simple uniform sampling of the entire hemisphere above the surface. To do this, I used a gather() loop to generate random ray directions, which I considered as my light direction l. Many of these samples go through the entire BRDF calculation only to be discarded because they affect a normal of the microfacet structure not visible to the view direction for one reason or another. An interesting side effect of this is that for very rough surfaces, I could achieve smooth renders with reasonable sample counts. As I approached mirror reflection, the number of samples required skyrocketed. This is expected, since the area of reflection is so much smaller for mirror reflections, but I am just shooting rays all over the hemisphere and almost all are being discarded by the BRDF. Obviously a better approach is needed.
To more accurately sample a BRDF, we must effectively sample the distribution term by generating random ray directions that are relevant to the BRDF, as opposed to just firing them at random over the whole hemisphere. This is considered importance sampling the BRDF. The sampling equations are more mathematically involved than I will go into here, but are described in the various papers in great detail. Essentially we generate random microfacet normals (equivalent to the half-vector in the description above) that satisfy the BRDF, and then do the reflection calculation on these normals and the view direction to generate the light directions l. These samples tell the shader 'where to look' so we are not spending precious time sampling areas of the hemisphere that will not contribute to the reflection at that point. Each of these samples is then weighted by a Probability Density Function (PDF), which accounts for visibility and the concentration of facets with that particular half-vector normal. Averaging these samples provides a resulting reflectance with less variance than uniform sampling, and is much, much faster.
MULTIPLE IMPORTANCE SAMPLING
One strategy is to sample the BRDF and generate vectors to sample the environment/objects/lights. Another approach is to sample the lights/ environment and provide that information to the surface and evaluate the BRDF. This is particularly useful when lights are very small or very bright. If we only sample the material, we may miss these lights entirely due to the randomness of the ray directions. Worse, we may miss them in one frame and hit them in the next, causing ugly flickering (a problem I am quite familiar with from other projects).
An environment map is importance sampled by determining which areas of the environment map are brightest and sending the associated ray directions back to the material to use as sample directions. Similarly with very small lights, sending the material information about the position of the light makes sure enough samples go in that direction.
In many cases this is a good strategy, but in the case of large light sources, a large number of samples is required, and it can often be more efficient to just sample the material. Combining these two strategies is the basis of Multiple Importance Sampling. In the scope of this project, I used RenderMan's built in physically-plausible lights, which are capable of generating samples to send to the material, as well as applying their light contribution to samples from the material. RenderMan's new RPS 16 integrators take care of the weighing between the light and material samples.