UPDATE v1.6: Cycles PBR thin film interference and metals

Edit: See here for a node-based implementation of the code, supporting GPU rendering! Comes with all the materials as well

UPDATE 1.6: Support for double layers. Very suitable for metal/dielectric/metal stacks to simulate car paints

I have implemented a calculation of thin-film interference, or iridiscence, of metals and dielectrics. These metals or dielectrics may be described in a physically-correct way by n and k values (see
https://en.wikipedia.org/wiki/Refractive_index#Complex_refractive_index ). The model assumes a user-defined base material (glass, metal, …), which may be coated with one or two additional layers that also can be metallic and/or absorbing. In these thin layers, light can interfere with itself, leading to rainbow-colored phenomena like oil spills on the street, soap bubbles, anti-reflection coatings, oxide formation on heated metals and color-changing car paints.
See the images below for examples rendered with the script as the base for glossy and glassy shaders (bubbles lit by and background part of free HDR image pack by www.hdrlabs.com, BMW model by Mike Pan).

The blend file has the following material presets (change the material of the test object to view)

These materials are examples of how the OSL Fresnel code can be used to imitate common interference phenomena in Blender.

Here’s the blend file: https://www.dropbox.com/s/k2lehy470xceuws/interference.blend?dl=0

1.1 How to use the script:

In a new Blender file, make sure you have set Cycles as the rendering engine. Then, on the Render tab of the properties window (where you also set render size), check the box that says ‘Open Shading Language’. You might need to turn off GPU computing to see the box. When you have created your object and added a material, make sure to ‘use nodes’, and then add (Shift+A) a script node. Select the ‘external’ option and open the .osl file provided below. Or, Create a new text block within Blender, paste the code there and select the ‘internal’ option. Then select the text block you just created.

1.2 Options of the script:


  • Top layer*:

  • thickness of layer 1, d_top (in micrometer)

  • Index of refraction IOR_top of layer 1. RGB datatype: a value for red ®, a value for green (G) and a value for blue (B). Default (1.6,1.6,1.6).

  • Extinction coefficient k_top of layer 1, related to the absorbance. RGB datatype: a value for R, a value for G and a value for B. Default (0,0,0) = nonabsorbing

  • Middle layer*:

  • layer 2 thickness d_middle (in micrometer)

  • IOR_middle of layer 2. RGB datatype: a value for R, a value for G and a value for B. Default (1.6,1.6,1.6).

  • k_middle of layer 2, related to the absorbance. RGB datatype: a value for R, a value for G and a value for B. Default (0,0,0) = nonabsorbing

  • Substrate:

  • IOR / n of the substrate (base layer). RGB datatype: a value for R, a value for G and a value for B. Default (1.45,1.45,1.45).

  • k of the substrate. RGB datatype: a value for R, a value for G and a value for B. Default (0,0,0) = nonabsorbing

  • Wavelength*: the wavelengths to use for the interference effect, for the R, G and B channels.

  • IOR_outside*: the index of refraction of the ‘outside world’. Typically 1, but under water would be 1.33, for example.

  • Whether these inputs are visible depends on the conditional compilation flags, discussed below.

1.3 Outputs:

  • F: reflected percentage of the light, per color (RGB), at the angle between the normal and the ray of light.
  • F0: reflected percentage of the light, per color (RGB), at normal incidence.
  • T: transmitted percentage of light, per color. Can be hooked up to a refractive shader.
  • T0: transmitted percentage of light at normal incidence.

By hooking these up to glossy and refractive shaders, metallic and glass-like materials can be made. Examples are present in the blend file. Negative values which don’t make sense are not prohibited, and may lead to funky results.

1.4 Conditional compilation:

The OSL script has statements that are used for conditional compilation to maximize speed. For example, say you only want to use it to calculate the interference due to a single layer on top of a base material. Then, there is no need for the code to calculate the second layer. You can simply set this layer thickness to 0, and it indeed will be as if it is not there. However, the code for the second layer does execute. To circumvent this, conditional compilation is used.

At the top of the code you can use #define statements to in- or exclude parts of the code. The provided example blend file has actually three identical copies of the OSL code, except for the amount of layers that it calculates. This is controlled by the flags for conditional compilation. The current flags that you can #define are these:


As you probably expect, SINGLELAYER includes just the code for a single layer on top of the base material. DOUBLELAYER includes code for two layers on top of the base material. If you don’t #define either, it will be just the base material (for example, for accurate metals). The NONVACUUM flag exposes the assumed index of refraction for the ambient medium. Typically that’s air, but maybe you have an underwater scene (n = 1.33) and you need to calculate what happens then. This allows you to change it. It has not been tested extensively by me!

EXPOSE_WAVELENGTH is a flag that allows the artist to set the wavelength for the R, G and B channels. At first, some values were hard-coded in the script, which of course can be easily changed by changing the code if you’re a programmer. However, I realize that often artists aren’t coders (or vice versa). Therefore, making the wavelength exposed in the shader node is an artist-friendly way to gain access to the internals. However, in many cases it’s just fine with the hard-coded values. Therefore, you can choose to expose the wavelengths to the outside, or to hide them altogether and maybe gain a little bit of speed.

2.1 Choosing values for n and k

The shader allows you to enter the values to use for the refractive index (n) and the extinction coefficient (k) for the red, green and blue channels. These values are often listed as a function of the wavelength of the light, for example at refractiveindex.info. Wavelengths that are perceived as red run from something like 625 nm to 670 nm, green is about 520 nm to 550 nm and blue is about 440 nm to 470 nm. Which wavelength you choose to use for red, green and blue depends on your own preference; there is no wrong choice, and the shader will behave physically correct regardless. Of course for different choices of n and k at slightly different wavelengths, the material is likely to change appearance slightly.

2.2 Optimized values of n and k for metals

So-called spectral renderers use the tabulated n and k values for each wavelength in the visible light spectrum to calculate what a material should look like. Cycles on the other hand only uses three color channels, one for red, one for green and one for blue. You might wonder what n and k values would best approximate the results of a spectral renderer. I have optimized the values of n and k for the metals that are included in the blend file to look as close as possible to the output from a spectral renderer, when the metal is illuminated with white light. These calibration curves can be found in this zip file: https://www.dropbox.com/s/uul8wbgso911svt/calibration_curves.zip?dl=0

Link to the script: http://www.pasteall.org/73796


Thank you.

Just another example:

Do you have to do anything to get this script working.

I copied the text - pasted it into the text editor, named it - then added the script node and tried to load it - however the code just keeps throwing an “invalid syntax” error on line 1.

nice looking

can you give the math model followed for this effect
to understand what the model is

happy cl

Video showing angle-dependent color (smooth glossy shader for speed):

Did you try the .blend file that I also put online?

Thanks :slight_smile:

Here’s a starter: https://en.wikipedia.org/wiki/Thin-film_interference

Not how I implemented it, though. I implemented this formula:

where r(p,s) is the complex reflection amplitude for light of a certain wavelength at a certain angle. This depends on the reflection coefficients of light at the interface with the film (r12), the reflection coefficient at the interface with the substrate (r23) and the thickness (d). See also, for example, L. Novotny & B. Hecht, Principles of Nano-optics, Ch. 2 & ch. 10.

Yes - it’s ok - muppet me hadn’t ticked the OSL checkbox on the render tab

edit: Just been having a play - very cool.

Glad to hear! :slight_smile:

any chance this could be use to do some other effects like sea shells surface ?

happy cl

Yes, I think you can get quite far, though I don’t have the exact behavior of mother of pearl firmly in my head.

but your OSL does only circular shapes I think so this might require some re shaping or deformations to look like mother pearl !

happy cl

You can modify the thickness d as a function of location. Set it to 0 to turn off interference, set it to some value to turn it on. Set it to a specific thickness to get a certain color at a certain angle. You could even use one of the colors (R, G or B) to drive another color map. I’m not sure how far you can push it, but it would be a start.

Edit: for the soap bubble animation I started out with a homogeneous thickness across the bubble, slowly decreasing the thickness at the top and increasing it at the bottom to simulate gravity. It’s just an example of what you could do

I did not learn a lot on OSL so cannot really begin to find a logical way to do it !

happy cl

You don’t need OSL to change the thickness, just plug something into the connector for d and you’re done :slight_smile: Look at my blend file, where I’ve added nodes to locally change the thickness in the last scene in the file.

I will try it tomorrow got other problems to solve for today! LOL

happy cl

do you know there is a thread for all OSL things
you should add your in that thread too

happy cl

Yes, I will do that, thanks! :slight_smile:

I also fixed a bug, so I’ve updated the OSL script. See first post for the update (blend file and script)

for the 2 files or only first one ?

I mean does it affect the other PBR metal too ?

happy bl