# Energy conservation in RE\_Direct\_Physical

**URL:** https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837
**Category:** Questions
**Tags:** shaders, physical-material
**Created:** [January 19, 2022, 11:11am UTC](https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837 "2022-01-19T11:11:33Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![Retro](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/retro/32/9294_2.png) [@Retro](https://discourse.threejs.org/u/Retro)
#### Post date: [January 19, 2022, 11:11am UTC](https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837/1 "2022-01-19T11:11:33Z")

</div>

I’ve been digging deep into the rendering equations used for the physical material ([lights\_physical\_pars\_fragment.glsl.js](https://github.com/mrdoob/three.js/blob/1a241ef10048770d56e06d6cd6a64c76cc720f95/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js)).

What I don’t understand is why the same irradiance is used for both the specular and diffuse reflection in RE\_Direct\_Physical.

```javascript
reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );
reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );

```

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:

```javascript
vec3 F = F_Schlick( f0, f90, dotNL );
reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );
reflectedLight.directDiffuse += (1 - F) * irradiance * BRDF_Lambert( material.diffuseColor );

```

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.

---

<div class="post-metadata">

### Author: ![repalash](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/repalash/32/18241_2.png) [@repalash](https://discourse.threejs.org/u/repalash)
#### Post date: [January 19, 2022, 12:51pm UTC](https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837/2 "2022-01-19T12:51:08Z")

</div>

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.

```javascript
float fd_Burley(float NoV, float NoL, float LoH, float roughness) {
    float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
    float lightScatter = F_Schlick(NoL, 1.0, f90);
    float viewScatter = F_Schlick(NoV, 1.0, f90);
    return lightScatter * viewScatter * (1.0 / PI);
}

```

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](https://google.github.io/filament/Filament.md.html#listing_diffusebrdf) The above function is also taken from section 4.5.

---

<div class="post-metadata">

### Author: ![Retro](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/retro/32/9294_2.png) [@Retro](https://discourse.threejs.org/u/Retro)
#### Post date: [January 19, 2022, 1:54pm UTC](https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837/3 "2022-01-19T13:54:20Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)
#### Post date: [January 19, 2022, 4:17pm UTC](https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837/4 "2022-01-19T16:17:17Z")

</div>

Related issue at GitHub:

> <https://github.com/mrdoob/three.js/issues/8354>
>
> \##### Description of the problem
> 
> From Frostbite 3 paper on page 9:
> 
> "Burley… \[Bur12\] has presented another diffuse model built on real world surface observations, see Equation 5. While this model is empirical, it allows us to reproduce the main features of the MERL
> database’s materials. For this reason and because of its simplicity, we have chosen to use this model
> in Frostbite. This diffuse term takes into account the roughness of the material and creates some
> retro-reflection at grazing angles."
> 
> https://seblagarde.files.wordpress.com/2015/07/course\_notes\_moving\_frostbite\_to\_pbr\_v32.pdf
> 
> For improved accuracy, we should likely replace the simple Lambertian diffuse model with Burley 2012 in our Standard material.
> \##### Three.js version
> \- \[x\] Dev
> \- \[\] r74
> \- \[\] ...
> \##### Browser
> \- \[x\] All of them
> \- \[\] Chrome
> \- \[\] Firefox
> \- \[\] Internet Explorer
> \##### OS
> \- \[x\] All of them
> \- \[\] Windows
> \- \[\] Linux
> \- \[\] Android
> \- \[\] IOS
> \##### Hardware Requirements (graphics card, VR Device, ...)

Please don’t be confused since the issue is closed but the feature was never implemented.

---

<div class="post-metadata">

### Author: ![looeee](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/looeee/32/59_2.png) [@looeee](https://discourse.threejs.org/u/looeee)
#### Post date: [January 20, 2022, 2:23am UTC](https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837/5 "2022-01-20T02:23:50Z")

</div>

@Mugen87 do you know why that issue was closed without any comment as to why? Seems like it would be a good enhancement to look into at some point.

---

<div class="post-metadata">

### Author: ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)
#### Post date: [January 20, 2022, 7:14am UTC](https://discourse.threejs.org/t/energy-conservation-in-re-direct-physical/33837/6 "2022-01-20T07:14:45Z")

</div>

> [@looeee](#):
>
> do you know why that issue was closed without any comment as to why?

I’m afraid I don’t.
