From my understanding, only the light that wasn’t reflected from the surface (reflected specularly) should enter the material where it reflects diffusely. This split (amount reflected and the amount entering) should be governed by fresnel equations (based on IOR and angle). It’s being accounted for in directSpecular (as part of BRDF_GGX), but not when directDiffuse is being calculated.
What I think should be happening is something more like this:
Is there a misunderstanding in my thinking? Or is this split being taken into account in some other part of the code? If it’s not and my thinking is correct, then this is not conserving energy as more light is reflected than it’s entering.
EDIT: I’m guessing it has something to do with the microsurface distribution of normals … I wonder if I can use dotNL above to calculate F. Inside BRDF_GGX dotVH is used since only microfacets facing H will reflect light to the viewer. I guess microsurface distribution has to affect how much light enters the material, which I’m not accounting above either.
You are correct, diffuse BRDF should include some fresnel factor.
Disney BRDF specifies the equation for this but is generally more performance-expensive, with no significant quality gains (compared to lambert). Although, I cannot say for sure that three.js doesn’t use it for this reason.
It’s pretty simple to extend the physical material to use disney diffuse BRDF.
Just multiply the value from this function to irradiance and diffuseColor.
Although, even after this, you cannot assume that energy will be perfectly conserved. You can read more about this in section 4.5 and 4.7 of the amazing Physically based rendering in Filament The above function is also taken from section 4.5.
Thank you so much, this is very helpful (so I wasn’t going crazy)!
I’ll see if I have any other questions. I was thinking of implementing Oren-Nayar model for the diffuse part and see what results I get. But I first wanted to be sure I’m even sending the right portion of irradiance into the diffuse part.