# Memo on Fresnel equations

April 29, 2013 12 Comments

Version : 1.4 – Living blog – First version was 29 April 2013

This post is a memo for me about misc thing I found related to the Fresnel equation as there is plenty of formula into the wild. I spend some times to gather these information and I was thinking it can interest others. This will not be really relevant to game rendering but still good to know. This could be useful when doing reference or when dealing with total internal reflection (frequent with multi layered BRDF). This is a memo, not a tutorial, so I won’t give basic explanation of many concepts like reflection/refraction, Snell’s law, index of refraction (IOR), total internal reflection (TIR), etc… At the end of the post I provide a Mathematica file with all the equations and graphs.

Notation for this post:

Conductor mean Metal and dielectric mean no-Metal material.

Light moves from a medium of a given IOR (incoming) into a second medium with IOR (transmitted).

Conductor have complex IOR with an imaginary part (note that’s t “transmitted” is a bad choice for conductor but I found it more identifiable than and , ).

## Fresnel Equation basis

All equations using an index of refraction can be replace with the same equation using a complex index of refraction .

Snell’s law dielectric-conductor interface :

Snell’s law dielectric-dielectric interface :

The calculations of the reflectance (What we are looking for when we want to calculate the percentage of reflection and transmission) depend on p- and s-polarization of the incident ray. and are the reflectivity for the two planes of polarization. is perpendicular (s = *German* senkrecht) and is parallel. The reflectance for unpolarized light is the average of and :

Following expressions use and or and depend on cases to simplify notation.

Fresnel equation for dielectric-conductor interface is [5]:

Fresnel equation for dielectric-dielectric interface is [2]:

Note as the expression is square “i” and “t” can be swap.

Dielectric Fresnel’s formulas are always derive from conductor Fresnel’s formulas by settings .

Several Fresnel’s formulas assume an Air-Dielectric/Conductor interface. Air IOR is 1.000293 and is approximate to 1. Thus using allow many simplification.

We define and the ratio of indices of refraction at the surface interface.

Fresnel’s formulas with , and can always be replace by Fresnel’s formulas with and .

All Fresnel’s formulas of the rest of the post are based on derivation of the Fresnel equation above. Arrangement is done to express and optimize Fresnel’s formulas depending on alias in this post using expression derives from Snell’s law:

## Fresnel Equation in practice

**Dielectric-Conductor interface **

From [1]:

using and with and as follow give the same result:

Derivation from conductor Fresnel equation can be found in [5] (p.111) and use

In practice there is some simplification possible:

resulting in hlsl code:

float3 FresnelDieletricConductor(float3 Eta, float3 Etak, float CosTheta) { float CosTheta2 = CosTheta * CosTheta; float SinTheta2 = 1 - CosTheta2; float3 Eta2 = Eta * Eta; float3 Etak2 = Etak * Etak; float3 t0 = Eta2 - Etak2 - SinTheta2; float3 a2plusb2 = sqrt(t0 * t0 + 4 * Eta2 * Etak2); float3 t1 = a2plusb2 + CosTheta2; float3 a = sqrt(0.5f * (a2plusb2 + t0)); float3 t2 = 2 * a * CosTheta; float3 Rs = (t1 - t2) / (t1 + t2); float3 t3 = CosTheta2 * a2plusb2 + SinTheta2 * SinTheta2; float3 t4 = t2 * SinTheta2; float3 Rp = Rs * (t3 - t4) / (t3 + t4); return 0.5 * (Rp + Rs); }

A more efficient approximation for Air-Conductor interface is available in [3]:

Calling the above expressions with and instead of and allow to approximate Dielectric-Conductor interface. Hsls code is:

float3 FresnelDieletricConductorApprox(float3 Eta, float3 Etak, float CosTheta) { float CosTheta2 = CosTheta * CosTheta; float3 TwoEtaCosTheta = 2 * Eta * CosTheta; float3 t0 = Eta * Eta + Etak * Etak; float3 t1 = t0 * CosTheta2; float3 Rs = (t0 - TwoEtaCosTheta + CosTheta2) / (t0 + TwoEtaCosTheta + CosTheta2); float3 Rp = (t1 - TwoEtaCosTheta + 1) / (t1 + TwoEtaCosTheta + 1); return 0.5* (Rp + Rs); }

For the approximation (Blue curve) is not good, for other case, the approximation is great:

This formulation is really specialized for conductor and should not be used for Dielectric-Dielectric interface.

**Dielectric-Dielectric interface**

From [2]:

(Note: For parallel case, “i” and “t” are swapped compare to equation in introduction to follow [2] notation. It doesn’t matter as the expression are square).

In hlsl this is:

float FresnelDielectricDielectric(float Eta, float CosTheta) { float SinTheta2 = 1 - CosTheta * CosTheta; float t0 = sqrt(1 - (SinTheta2 / (Eta * Eta))); float t1 = Eta * t0; float t2 = Eta * CosTheta; float rs = (CosTheta - t1) / (CosTheta + t1); float rp = (t0 - t2) / (t0 + t2); return 0.5 * (rs * rs + rp * rp); }

An equivalent expression can be derive from the [1] formulation by setting . Source [7]:

If is imaginary, this indicates a total internal reflection and .

Note that’s here we get the reflectance directly. In hlsl this is:

float FresnelDielectricDielectric(float Eta, float CosTheta) { float c = CosTheta; float temp = Eta* Eta + c * c - 1; if (temp < 0) return 1; float g = sqrt(temp); return 0.5 * Square((g - c) / (g + c)) * (1 + Square(( (g + c) * c - 1) / ((g - c) * c+ 1))); }

## Fresnel Schlick’s approximation

The Fresnel Schlick’s approximation [9] is widely used and knows (Call FresnelSchlick later) but its properties are not.

There is two possible versions of the approximation.

The first Schlick’s approximation use , the Fresnel reflectance at 0° (reflectance at normal incidence), also name specular color in PBR game :

in hlsl:

float3 FresnelSchlick(float3 SpecularColor, float3 E,float3 H) { return SpecularColor + (1.0f - SpecularColor) * pow(1.0f - saturate(dot(E, H)), 5); }

For conductor:

For dielectric:

In game we always define specular color as with for air as we always want to deal with air-dielectric/conductor interface. But if we need to interact with another interface like water-dielectric, we need to store another specular color. In case of specular color define for air-dielectric/conductor interface, IOR can be retrieving with:

However for complex IOR there is no way to retrieve the correct and . So in case of multi layer BRDF prepare to move to parameters with complex IOR instead of specular color.

The FresnelSchlick (Blue) with air-dielectric interface is only accurate in the range . Outside this range the relative error grows [8]:

The accurate range still covers most use material in game (water at 1.33 still ok), so no need to worry too much about this.

The other form of the Schlick approximation is without . This is exactly the same as the first one, just replace R(0) by one of the equality provided above:

Example for dielectric-dielectric:

Example for dielectric-conductor which is the formulation provide in Lazanyi et al [8].

The FresnelSchlick (Blue) for air-conductor don’t match exactly the Fresnel conductor equation (Red):

There is a noticeable “dip” just before going to white in case of several metals. Lazanyi et al [8] try to provide a better FresnelSchlick approximation for Metals. They try to add a compensation term to take into account the error introduce by the FresnelSchlick. The term is a rational approximation of the error between Fresnel equation and FresnelSchlick equation. Here is an example with , . Blue curve is the error term, red is the rational approximation with and :

They suggest to use but the value is more problematic. To understand why, examine p (Red) and s-polarized (Yellow) curves of Fresnel equation for an air-dielectric interface (Left) and an air-conductor interface (Right):

The left figure show that’s the “dip” come from the perpendicular curve. For dielectric, the perpendicular curve crosses the 0 line at the Brewster’s angle: .

The reflectance does not reach zero for a conductor as it does for a dielectric, but the angle for which it is a minimum is called the pseudo Brewster. However there is no simple solution to retrieve it. That’s why the is difficult to get right as it should be related to this pseudo-Brewster’s angle which define the local minimun of the error term.

Authors suggest that for most metal local minimun of the error term is somewhere between 0.1 and 0.15. I write a small “solver” in Mathematica to retrieve a good value. It retrieves the local minimum by brute force, then solves the error compensation equation with it. For the author’s case of and I get . Note that the calculated value is not the optimal value it just match the local minimum. Look at the Mathematica file if interested. The equation provided in the paper follow the second Schlick’s approximation form, so I reuse it here. Following graph show the result approximation (Blue):

for , and

Result is good, equation is cheap but only work for a particular case and need to be tune for each value (Remember that’s a metal IOR is chromatic and the process need to be done 3 times), so not really useful in practice. Anyway the visual impact of this “dip” is negligible and we can avoid it [8].

When mean we go to a less dense medium there is TIR occuring at critical angle. . FresnelSchlick don’t handle TIR. This can be fix by using instead of in the FresnelSchlick when . Here is a graph showing original Fresnel, FresnelSchlick (both curve an almost merged in brown) and FresnelSchlick with (Blue) that I will call FresnelSchlickTIR for and (water-air interface):

In hlsl this gives:

float FresnelSchlickTIR(float nt, float ni, float3 n, float3 i) { float R0 = (nt - ni) / (nt + ni); R0 *= R0; float CosX = dot(n, i); if (ni > nt) { float inv_eta = ni / nt; float SinT2 = inv_eta * inv_eta * (1.0f - CosX * CosX); if (SinT2 > 1.0f) { return 1.0f; // TIR } CosX = sqrt(1.0f - SinT2); } return R0 + (1.0f - R0) * pow(1.0 - CosX, 5.0); }

With so much instruction the interest for FresnelSchlick start to decreases.

For an optimized FresnelSchlick formulation see Spherical Gaussian approximation for Blinn-Phong, Phong and Fresnel.

## Reflect/Refract vector

Reflect and refract vector are derive from Snell ‘s law [4]:

Reflect:

float3 reflect( float3 i, float3 n ) { return i - 2.0 * n * dot(n, i); }

Refract vector is calculated as:

The derivation for refraction vector can be found in [4].

float3 refract( float3 i, float3 n, float inv_eta ) { float cosi = dot(-i, n); float cost2 = 1.0f - inv_eta * inv_eta * (1.0f - cosi*cosi); float3 t = inv_eta * i + ((inv_eta * cosi - sqrt(abs(cost2))) * n); return t * (float3)(cost2 > 0); }

Optimized version of refract can be done for specific IOR, see Water drop 3a – Physically based wet surfaces.

## Mathematica

All the content of this post has been check in Mathematica. Here are the Mathematica files, one pdf: Fresnel and the notebook : Fresnel_nb (Right click, save, rename “.pdf” to “.nb” as wordpress don’t support zip files).

## Reference

[1] Shirley, “Physically based lighting calculations for computer graphics”, http://www.cs.virginia.edu/~jdl/bib/globillum/shirley_thesis.pdf

[2] Wikipedia, “Fresnel equation”, http://en.wikipedia.org/wiki/Fresnel_equations

[3] Pharr, Humphreys, “Physically based rendering book”, http://www.pbrt.org/

[4]Greve, “Reflections and Refractions in Ray Tracing”, http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006–degreve–reflection_refraction.pdf

[5] Möller, “Optics” book, http://www.springer.com/physics/optics+%26+lasers/book/978-0-387-26168-3

[6] Siegel, Howell, “Thermal radiation hit transfer, 3rd edition” book, http://www.amazon.com/Thermal-Radiation-Heat-Transfer-5th/dp/1439805334

[7] Larsen, “Shading a Surface using BRDFs”, http://www2.imm.dtu.dk/~jab/shaderday/ShadingASurfaceUsingBRDFs.ppt

[8] Lazanyi, Szirmay-Kalos, “Fresnel term approximations for Metals” http://sirkan.iit.bme.hu/~szirmay/fresnel.pdf

[9] Schlick, “An inexpensive BRDF model for physically based rendering”, http://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf

V1.1 : Minor change in Reflect/refract vector section

Nice! Good to see all these data gathered in one place!

Tiny detail concerning the Schilck approximation: the pow(x,5) can be translated to x2=x*x; x5=x2*x2*x; This was always faster on gen3 in the use cases I have encountered. :)

Thank you !

For the small optimisation, I discuss it and other in another post : https://seblagarde.wordpress.com/2012/06/03/spherical-gaussien-approximation-for-blinn-phong-phong-and-fresnel/

v1.2 : Add 3 hlsl code for Dielectric-Conductor interface and Dieletric-Dieltricinterface

v1.3: Add a statement for the last form of exact fresnel equation:

If is imaginary, this indicates a total internal reflection and

And update the schlick approximation with the other formulation without R(0):

Example for dielectric-dielectric:

Example for dielectric-conductor which is the formulation provide in Lazanyi et al [8].

Thank you for the great memo, it is really really helpful! I just would like to confirm is it ok to reference your code in our own projects or is there a copyright restriction or other similar restrictions?

Hi,

Thank you for the comment.

Feel free to use whatever you want on this blog, no copyright :)

Cheers

Great!! Thank you so much!! :)

Hi Sébastien,

it appears there are some issues with Fresnel equations for dielectric-conductor and dielectric-dielectric interfaces that you’ve presented. Here’s my version based on Siegel’s Thermal Radiation Heat Transfer.

For conductors:

,

.

For dielectrics:

,

.

They don’t match yours. Perhaps it’s worth double checking.

Cheers,

Evgenii

Here’s the first set of equations (for conductors) further simplified to match the equations you posted. I posted the older version and I can’t edit the comment, sorry about that.

,

.

Feel free to edit my previous comment and nuke this one. :-)

Hi,

You are right, thanks a lot for correcting me! Made a mistake when rewriting equation.

So perpendicular case was right, only the parallel case was wrong.

Also note that the swapped sign doesn’t matter as the expression is square. But better to be more correct from the beginning :)

I have update the equation in version 1.4, thank you very much to let me know. Other part of the post shouldn’t be affected.

Great, thanks for the update! I find your post to be a very useful reference – it’s nice to have all the equations in one place. :-)

Cheers,

Evgenii