# Improving Fresnel term attenuation in a PBR engine

Physically based rendering is now used by a majority of 3D engines in video game industry. I wont go over the core of a PBR engine (BRDF, material model…), because there is already a lot of very good references all over the internet (see ref. below), I will just focus on one particular term: the Fresnel.

Glossary:

• cSpec is the minimal amount of spec a 0 incidence angle
• H is the half way vector, so H = normalize(View+Light)
• F = 1-cos ($\theta$), where $\theta$ is the angle between the direction from which the incident light is coming and the BRDF half way vector (H).

Most engine use the Shlick Fresnel approximation:
Fresnel = cSpec + (1-cSpec) * pow(F, 5)
It’s a very fast approximation, found about 20 years ago, it’s good, but when you use it with normalized Blinn-Phong or GGX BRDF for example, it tends to make the flat and rough surfaces, like a ground with a sunset light setup, looks a bit “milky”, is there a way to improve this?

Real world analysis:
Let’s take a look at what’s going on in the world we try to simulate, here is the curve resulting from the MERL (ref.5) database. The red dot represent the classical Schlick approximation for Fresnel term, other lines are results of observations for a bunch of di-electric material.

Hypothesis:

Fresnel term becomes higher at grazing angle, but it seems to be modulated by the roughness of the surface. With rougher surfaces it seems the Fresnel peak is lower but appear earlier, considering the incidence angle of light and normal.
Let’s see how it is handled by modern game 3D engine:

Lagarde (Frostbite):
cSpec + ( max( Gloss, cSpec) – cSpec ) * pow(F, 5)

red is for glossier surfaces, blue for rougher ones

Lazarov (Call of duty):
cSpec + (1-cSpec) * pow(F, 5) / ( 4 – 3 * Gloss )

My formula:
cSpec + ( (1-cSpec) * pow(F,6) * ( 1 + pow ( Gloss, 2 ) – (F)  ) )

3D view of the curve with x the angle, y the roughness and z the fresnel result

Optimizations:
The cost of this formula can be cheaper than Lazarov, here is how we compute it

On modern GPU, the fastest way to compute pow(x,6) is like this,and it s the same amount of operation as a pow(x,5)

const float xPow2 = x*x;

const float xPow5 = xPow2 *xPow2 * x;
const float xPow6 = xPow2 *xPow2 *xPow2 ; // As expensive as pow5


So our formula HLSL code looks like this:

float ComputeFresnelAtt(const float dotLH, const float F0, const float Glossiness)
{
const float F = 1.0f - dotLH;
const float FPow2 = F * F;
const float FPow6 = FPow2 * FPow2 * FPow2 ;
return F0 + ( 1 - F0 ) * FPow6 * ( 1 - F + Glossiness * Glossiness);
}


Results:
Results look great, our artists really love it, I will provide screenshots in a near future

References

(2) http://www.manufato.com/?p=902

(3) http://content.gpwiki.org/D3DBook:(Lighting)_Cook-Torrance

(4) https://seblagarde.wordpress.com/2011/08/17/hello-world/

(5) http://www.merl.com/brdf/

(6) https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Specular_Highlights_at_Silhouettes