Fresnel with Accurate Roughness

  • June 16, 2016
  • Blender 2.7x
  • Render: Cycles
  • Creator: angavrilov
  • License: CC-0
You must be logged in to download.


Fresnel equations describe how the fraction of light reflected by a surface changes depending on the incident angle. Blender has Fresnel and Layer Weight nodes that can compute this. However, they have two deficiencies:

  1. Metals have complex IOR (with the imaginary part also known as extinction coefficient), but both nodes only have one real input. It's impossible to reproduce the true metal Fresnel curve using that.
  2. Neither node has any way to adjust the effect for roughness.

The second point is actually the most difficult one to resolve. Roughness is mathematically simulated using so called microfacet model, which basically assumes that the surface consists of infinitely small perfectly flat faces, with normals randomly perturbed from the average according to a statistical distribution. The fresnel function represents reflection from a flat surface, and thus should be applied to the microfacet normal being sampled; the practical result of this is that the contribution of the fresnel effect actually depends on incoming light direction.

Node shaders don't have any access to that information, so I thought that the best thing you can do is try to emulate the appearance of the microfacet model in uniformly lit environment, where the same amount of light is coming from any direction. Given that the whole thing is an approximation, it is then appropriate to sidestep the first problem by using the Schlick's approximation approach, which simply lets you choose minimal reflectivity, and interpolates a curve between it and 100%.

The file contains the following node groups:

  • Fresnel-Sharp: Implements Schlick's approximation, tweaked to reproduce the 1.45 IOR curve with only 0.6-0.8% error.

This group should be basically equivalent in its results to the approach suggested by some tutorials of using a constant IOR, subtracting a second Fresnel node using Incoming as normal, and then scaling the result to the desired Min Reflectivity.

The group inputs and outputs reflectivity values as colors so that it can be easily used for adjusting the specular color of metals. There is no additional cost from this for dielectrics, since the Mix RGB node is the most convenient thing to use internally in any case.

Note: This node group is redundant in 2.78, because Cycles is now able to simplify Fresnel-GGX into its exact equivalent if Roughness is constant 0.

  • Fresnel-GGX: Builds on the tweaked approximation, and adds math and correction curves to account for roughness.

The group was constructed using reference images rendered by Blender patched to include the Fresnel coefficient (1.45 IOR) into the microfacet code used by the GGX Glossy node, and approximates the result with 1.5-2.5% error.

There are also a few immediately usable examples of trivial PBR material groups:

  • GlossySurface-GGX: Wraps another shader, adding colorless fresnel glossy reflection to it using a combination of Fresnel-GGX and Glossy. Intended as a building block for dielectric shaders.

  • Metallic-GGX: Combines Fresnel-GGX and Glossy into a primitive PBR metal material.

  • Dielectric-GGX: Combines Diffuse and GlossySurface-GGX into a simple PBR dielectric.

  • Dielectric-IOR-GGX: Like above, but computes Min Glossy from an IOR input.

As a bonus, there's also a HighlightRangeDev group, which highlights acceptable and unacceptable deviations of a value from a goal using different colors, and was used in tuning the Fresnel node groups.


  • MZiemys profile picture

    This is really cool. Is there a way to add changeable IOR value to this?

    Written June 23, 2016
  • angavrilov profile picture

    No, the approximation is based on effectively using a mix between flat minimum value and a fixed curve starting at zero. I think the use of linear mix means that the effects of roughness should remain correct for any min reflectance value, if you imagine that the microfacet fresnel uses the same linear mix internally.

    I also notice that there seem to be tutorials that suggest building 'artist friendly' fresnel by using fixed IOR and rescaling the resulting curve to match a 'specularity' value, and the result of that is no different from what happens here.

    If you need it, you can easily compute the correct minimum reflectance value from IOR: it equals the square of (ior-1)/(ior+1), e.g. for 1.45 it's (0.45/2.45)^2 = 0.0337.

    Written June 23, 2016
  • MetinSeven profile picture

    Interesting indeed. Thanks for sharing, appreciated.

    Written June 23, 2016