This implements the approach to fixing the energy loss of GGX at high roughness that was presented in "Revisiting Physically Based Shading at Imageworks" at SIGGRAPH 2017.
Essentially, an additional lobe is added which is designed to have an albedo that exactly matches the missing energy. As a result, the combined shader passes the typical furnace test, meaning that hemispherical reflection is 1 for any incoming direction.
Compared to the Multiscattering GGX closure which solves the same problem, this approach does not reproduce the exact directional distribution of the reflection, but is simpler to compute, doesn't require stochastic evaluation and doesn't have noise problems.
For correctly handling colored closures, I implemented the power series lookup tables that are suggested in Stephen Hill's blog. I'm not sure whether his approach is the same since the post about implementation details isn't out yet, but my approach works so I guess so.
- Grazing reflections at high roughnesses - originally, my precomputation code produced a table that worked perfectly fine, but didn't match the plots in the two sources at grazing angles. Now, with the power series code, I get a matching result, but rendered images don't preserve energy at those angles anymore. And on top of that, I don't even know why there is any difference in the first place...
- Glass - should be doable, but is a bit more complex. Definitely worth doing since the energy loss is most noticeable there.
- LUT resolution - I went for 128x128 for now, but the original presentation suggests 32 is enough.
- Integration with Principled BSDF - just copy-pasting it in wouldn't be hard, but I'm fairly sure that there are further improvements that could be made using this albedo information (see e.g. the "Microfacet Dielectrics" section of the presentation). Since this would be a new option, it would be a rare opportunity to overhaul things without breaking compatibility, so I don't want to rush it. Also, it's connected to the planned metallic fresnel option too...
- EEVEE integration - no major problems as far as I can see, just needs to be done.
- Naming - I went for "Energy-conserving GGX" for now, but I don't really like the name. Internally I named it "Kelemen Lobe" after the author of the paper where the lobe was initially published.
- The table size #define is really long and makes the code unreadable, maybe we should just hardcode it?