I am implementing the BRDF from the 2017 paper "Production Friendly Microfacet Sheen BRDF". This BRDF is modelled as a coat layer stacked on top of a base specular. In order to do so in an energy conserving way, the authors precompute a LUT containing the directional albedo R(v)
(a.k.a. hemispherical-directional reflectance, or essentially an analytical white furnace test) which they use to scale the base specular to avoid energy gain (Fr = Fr_Base * (1 - E_Sheen) + Fr_Sheen
).
$$ R(\mathbf{v}) = \int_{\mathbf{l}\in\Omega} f(\mathbf{l}, \mathbf{v})(\mathbf{n}\cdot\mathbf{l}) \,d\mathbf{l} $$
The result of the above integration for a GGX BRDF in the fully reflective case (Fresnel = 1.0) looks like this when plotted as a 2D function of viewing angle (X axis) and roughness (Y axis):
While not energy preserving (there's energy loss at high roughnesses due to assuming single scattering) this BRDF is energy conserving, as it doesn't reflect more energy than it receives (i.e. values never go higher than 1).
This does not seem to be the case for the "Charlie" Sheen BRDF. A non-insignificant portion of the result (marked in magenta) returns values well above 1, corresponding to a low roughness response viewed at a grazing angle.
I am really confused about this, given this BRDF was introduced in "Revisiting Physically Based Shading at Imageworks", a wonderful presentation with a strong focus on energy conservation/preservation. In fact, using this LUT in practice yields correct enough results, a close match to what was presented in the slides. However this doesn't feel right, and it seems to go against anything I've been able to find on the matter.
Real-Time Rendering, 4th Edition p.313 states "As a distribution function, the BRDF can have arbitrarily high values in certain directions [...] the requirement for a BRDF to be energy conserving is that R(l)
be no greater than one for all possible values of l
" (note that R(l) == R(v)
if the BRDF is reciprocal).
The "Charlie" Sheen BRDF is widely adopted in a number of rendering systems such as Khronos's glTF, Enterprise PBR, Filament, and others. The results they share don't match what I've shown above, which strenghtens my suspicions that I must be getting something crossed.
Above (left) is Enterprise PBR's result (upscaled from 16x16 for comparison reasons). They don't seem to provide code for the integration, only a numerical fit approximation. It seems to match Filament's result (right) which they encode in the blue channel of their environment BRDF (DFG). They do provide source code for the integration in CubemapIBL.cpp, however I cannot seem to find any significant differences between our implementations. I've added my implementation to a ShaderToy for reference purposes: https://www.shadertoy.com/view/4ctGRM
My questions are, did I make some mistake in my implementation? Is it possible that this BRDF is not energy conserving? Or am I failing to understand some concept here? This has got me really stumped, any insight would be greatly appreciated!