PBR Part 2: Image Based Lighting Intro
Before I start my blog post, I want to clarify the purpose of this series. It is more about me documenting my process of learning and implementing PBR, rather than what PBR is. These blog posts cover the resources I have found, and I try to clarify (or elaborate on) some things in these articles/papers that I believe were vague or not well-enough explained.
In my last post, I covered the math behind the Microfacet BRDF, which handles direct lighting. But now, we will tackle the indirect lighting, using Image-Based Lighting (IBL). Here is our checklist as a reminder of what we need:
3. Image-Based lighting (Indirect Lighting)
a. Irradiance Map (Diffuse)
i. Spherical Harmonics
b. Pre-filtered Mip-Mapped Radiance Environment Map, or PMREM (Specular)
i. Radiance Integral ---(see ii for inbetween phase)---> Split-Sum Approximation
ii. Importance Sampling* (new finding)
So, the goal here is for the environment map (in this case, the cube map that makes up the Skybox) to be a natural source of light, just like the real world. After all, objects should not be lit the same in daytime as in nighttime. Here is what we start with:
For the Indirect Diffuse, we need what is called an Irradiance Map. To get this, the cube map (shown above) is sample hundreds of thousands of times to calculate the light from each direction. This is where Spherical Harmonics comes in. Simply put, Spherical Harmonics are a series of functions on a sphere used to solve some differential equations We will calculate an irradiance map with only 9 SH coefficients. We are going to take a shortcut in regards to this by using an external program, but it's still important to understand the concept. Here is a great blog post on SH for Beginners.
So, as mentioned, we don't have to do SH manually. Long ago, there was an open source tool made available by AMD called CubeMapGen. This tool allowed one to generate and modify DDS cubemaps, with several great filter and sampler settings. It has since been deprecated, but a man by the name of Sebastien Lagarde has made a working recreation. You can find it on his blog here. So, here's how we use it to make an irradiance map:
1. Click "Load CubeMap(.dds)"
2. Load in the above cube map image (or any of you choice)
3. On the right, click the box labeled "Irradiance Cubemap". When clicked, no other option or filter is taken into account.
4. Click "Filter Cubemap"
5. What you will get for output is an Irradiance map!
6. This is not a step, but it is worth noting that, at the end of the documentation is an explanation on how spherical harmonics were implemented withing the program.
Once we have it, we can sample it in the pixel shader like so, using the surface normal and multiplying by the diffuse albedo:
float3 indirectDiffuseLighting = irradianceMap.SampleLevel(SamplerAnisotropic, normalWS, 0) * diffuseAlbedo;
Now, when we get to indirect specular lighting, we still need another map. We need to use both maps in the same methods to do indirect lighting, so I'll hold off on posting code until the explanation is complete.
So, the second map needed is call a Pre-filtered Mip-Mapped Radiance Environment Map. Say that five times fast! No need, "PMREM" will do just fine. This will pre-calculate radiance, encode it in a CubeMap, and that map can be sampled using the normal of the pixel. Could we use CubeMapGen to once again come to our rescue? Yes and no. When looking at the options, once can see that it only supports PMREMS for Phong and Blinn lighting models. The lighting model being used in my engine is the Cook-Torrence Microfacet model. So, this time, the math will have to be done manually in the shaders. This is fine, since it's still a concept worth knowing in and out. In fact, the lighting model I am using is very common in the video game industry, so I am not alone in needing to know how to get my PMREM.
The process of getting this next map is not very simple and has quite a few steps. I think, to make this more digestible, it should have its own blog post. So, within the next two days, I will go through that process and post code samples, as well as a link to my Github. Stay tuned!