A soap bubble OSL shader for Blender

The next step in our yourney to develop useful OSL shaders is a soap bubble shader.

The color patterns in soap bubbles and oil films are caused by a phenomenon called thin film interference. Our goal is to recreate those color patterns in a more or less physically accurate way.

Unlike the scales and hexagon shaders we developed earlier, this shader does not simply generate a color pattern but produces colors that are dependent on the angle of incidence. Because the incidence vector I is already provided in OSL as are many vector operations, this irridescence shader is surprisingly simple to implement.

surface irridescence (
 float nmedium = 1, // approximate refractive index of air
 float nfilm   = 1.3, // approximate refractive index of water
 float d       = 1000, // 1000 nm = 1 micron
        output color Color = 0
 // condition for constructive interference:
 // 2 * nfilm * d * cos(t2) == (m-0.5)*lambda
 // d and lambda in nm
 float eta = nmedium/nfilm;
        // note that N should be the perturbed normal
 vector T = normalize(refract(I,N,eta));
        // no need to divide by (len(-I) * len(T)) as these are normalized
 float cost2 = dot(-I , T);
 float opd = 2*nfilm*d*cost2;

 int mmin = int(ceil(opd/750+0.5));
 int mmax = int(floor(opd/350+0.5));
        // if mmax < mmin the film is too thin to show an effect
 int m = (mmin + mmax)/2;

 if (m > 0){
     float lambda = opd / (m - 0.5);
            color c = wavelength_color(lambda);
            Color =  c;

In the code shown above the trick is that we calculate the length of the optical path opd first and then (in line 18) calculate the minimum and maximum number of wavelengths (plus a half to correct for a phase shift, check the Wikipedia article to see why) that fit in this path. The minimum number of wavelengths is calculated by dividing by the wavelength of the longest waelength we can see (red, 750 nm), the maximum by dividing by the shortest wavelength (blue, 350 nm). If the film is too thin, mmin will be smaller tban zero.

The next step is to pick any integer that lies between those extremes (line 21) and calculate the wavelength lambda that corresponds with this integer (line 24). The final trick is converting this wavelength to a RGB-color with OSLs built-in wavelength_color() function.

Example node setup

The shader produces 'just' colors so it is best to combine plug it into a glossy shader and combine it with a general glossy shader too make things resemble a soap bubble. In the node setup shown below we've thrown in some noise to perturb the normal so we get the characteristic color swirls we see in real life soap bubbles.

Future steps

The next article in this series will probably cover weave patterns.


  1. This really is a fantastic shader (and blog too). This site is super helpful. Please keep this going.

  2. This comment has been removed by the author.

  3. Thanks for posting this great shader. I am experimenting with Renderman and would like to incorporate the bubble into a scene. Do you know how it might work using the Renderman nodes? Just curious.

  4. @Mark,

    I think about any OSL node should work with Renderman for Blender. Check this article for my first experiments: http://www.swineworld.org/2015/07/renderman-for-blender-vs-osl.html