Notes on physically based shading & shaders Part 1


Disclaimer : 

I by no means claim to know everything about these topics, but I think I know some stuff from the artist’s perspective.
So I thought I would share some of this knowledge, and maybe some of you find it useful. 🙂

headlight_lookdev_closeupVW Jetta closeup look-dev render.
The dust flares up when light hits it from behind, this was achieved using some SSS back-scattering.

In some bare-bone renderers or even game engines you don’t have a magical ‘v-ray material’ or mila/mia material libraries that gets you 90% of the way there, doing everything for you under the hood.
Ever noticed how when you increase the reflection/refraction amounts in a vray/mia/aistandard material, the diffuse color amount gets dimmed down accordingly. This is energy conservation working in a shader.

Knowing the fundamentals of how this is actually working will allow you to build your own complex shader networks to better simulate a real-world material and always maintain predictable results..

The key here is predictability , yeah you might build this massive shader network that gets you the look you want, but
will it look the same in all lighting conditions? This is why it’s important to make sure you are working as physically correct
as possible.

1. Energy Conservation

In most cases your renderer comes with monolithic shaders(VrayMTL , Mia/Mila, AiStandard) that sorts this out for you.
So you never really have to worry about this. But it is good to know in some cases..

Energy conservation in a shader is just like energy conservation in any other form of physics but to do with light energy.
Simply put. A shader / shader-network must never bounce or reflect more light out, than is coming in.

Unless of course you want to make a shader that is actually emitting light..

To illustrate more clearly you can think of the total energy of all of the components that build up a shader, the diffuse, reflection, sss, refraction etc.. must always add up to be equal to or less than the total incoming light energy.
So the different values between all of these BRDF components have to be balanced out accordingly so they add up together to equal the total energy coming in.

It is nothing more than some simple arithmetic that goes down and does this. Lets say the total light coming into a shader is an intensity of “1”.. an equation for an energy conserving shader would look like this:

Diffuse + Reflection + Refraction <= 1(incoming light)

But what about Fresnel ?

Augustin_FresnelThis Dapper Chap is ‘Augustin-Jean Fresnel’

How do we automatically balance all of these different components out in the shader?
And how do we do this if we have to add in more physically correct fresnel term to correctly balance reflection over diffuse and refraction.
For those of you who don’t know what the fresnel term is, read this, but more on fresnel later.

All we have to do is make all the different components take into account the others, reflection always goes on top,
then diffuse and refraction amount are weighted based on the overall refraction amount.
So a more physically correct BRDF shader that takes into account fresnel  would look more like this:

(Diffuse * (1 – (fresnelTerm * specularMap * SpecularValue)*(1-RefractionStrength))
Refraction * (1- (fresnelTerm * specularMap * specularValue)*(RefractionStrength)

Reflection * (fresnelTerm * specularMap)
1(incoming light)

The above equation might look a little confusing so lets see what this would look like if we had to piece it together in a shader network.

If I had to create the below shader network in maya for real, a lot of the operations and connections would be hidden.So I just made this pseudo shader network with preview-swatches at the end of every operation to better illustrate what is going on. Please click on the image to open the full resolution version.


So this is pretty much what is going on behind the scenes in an energy conserving shader.
It is quite interesting to note, in earlier versions of Arnold, there was a mistake with the energy conservation
where the weighting on the diffuse component wasn’t taking into account any specular maps plugged in.
So if you had a spec map plugged into your shader, the diffuse was only being multiplied by the invert
fresnel curve and not the result of the ‘spec map * fresnel’. This caused a nasty dark/black rim on all
fresnel materials if they had a spec map plugged in.

This concludes the first part on physically based shaders. Next post I will explain fresnel in a lot more detail
and different levels of correctly implementing it from the most simplistic to the most complex and physically
correct way.

Hope you enjoyed reading this 🙂
In part 2 I will go into fresnel in a lot more detail.