Very thanks for https://learnopengl.com/PBR/Theory. I have learn a lot from this page.
This is an intermediate article to record my progress in implementing real-time physcis based rendering and imaged based lighitng. I will introduce how do I change everything from Blinn-Phong workflow to PBR_MetallicRoughness workflow.
Visialization of a PBR material:
Comparison between brdf functions:
Cook-torrance BRDF and sampling theory:
Convert HDR image to cube map:
Free HDR image resources:
What did I do to shift the work flow from Blinn-Phong to PBR?
- Get a model with PBR textures
- Implemented normal mapping
- Use those textures in BlinnPhong and test if they look good
- Create PBR fragment shader, and releated material class
- Modify directional light and point light logic to fit PBR shading
- Use 25 sphere to test metalness & roughness
- Modify shadow function to fit PBR shading
Until here, basic PBR_MR is done. The next step is to add Image based lighting to create an illusion of global illumination.
First thing to do is to capture the scene. I did not capture the skybox directly like the tutorial said. Instead, I capture the whole scene before the actual rendering. To achieve this, I made two functions for two threads call:
- BeforeUpdate() // In application thread
- PreRenderFrame() // In render thread
I passed the scene that I wanted to be captured from application thread to the render thead, and the pre-render pass will render the scene to the capture frameBuffer, this frame buffer is a HDR cubemap(GL_TEXTURE_CUBEMAP, GL_RGB16, 2048 * 2048). Here is the result:
After I got the environment cubemap, I generated 2 cubemaps, 1 texture2D one by one:
- Irradiance map (32 * 32)
- Pre-filter cubemap (With LOD of 5, from 256*256 to 8*8)
- BRDF-Integration map (2048 * 2048)
The first two process the environment cubemap, generate a new version of cubemap to represent the environment information.
Irradiance map is used to estimate the whole environment influence to the diffuse term of the cook-torrance BRDF. Since irradiance map does not require too many detail in specific direction, it is usually in a very low resolution, in my case: 32 * 32.
Irradiance map (32 * 32)
Pre-filter cubemap is used to estimate the environment in certain direction, specifically, in the perfect reflection direction, that is the specualr term of the cook-torrance BRDF. In PBR, the roughness determines the reflectance of the material. So if a surface is smooth, then for this surface, it should use lower LOD of the pre-filter cubemap to get detail reflection. On the other hand, a rougher surface needs less detail, so it should use higher LOD of the pre-filter cubemap, which is more blury.
Pre-filter cubemap (LOD 0, 256 * 256)
Pre-filter cubemap (LOD 1, 128 * 128)
The last one, BRDF integration map, AKA, 2D lookup texture of roughness. Theoratically, the engine did not need to generate this map, since it is a parameter independent texture. For the sake of demoing, I genereate the texture in run-time.
BRDF integration map (2048 * 2048)
metalness & roughness comparasion among 25 spheres
roughness intensity changes from left to right: 0.25 to 0.05
A high resolution gun with high resolution texture, doesn’t look good
PBR teapot, check the environment reflection
Normal mapping and the environment reflection
To be honest, I did not understand some of the math inside, but it was fun to look into the math.
Also, my scene looks very low contrast and I will find a way to solve it in the future. What I plan to do next, is either improve the lighting or start to do defered shading & lighting.