in shader, tutorial, Unity3D

CD-ROM Shader: Diffraction Grating – Part 1

Share Button

This post will guide you through the creation of a shader that reproduces the rainbow reflections that can be seen on CD-ROMs and DVDs. This tutorial is part of a longer series on physically based iridescence.

You can find the complete series here:

A link to download the Unity project used in this series is also provided at the end of the page.


In a previous tutorial, The Mathematics of Diffraction Grating, we have derived the equations that capture the very nature of the iridescent reflections that certain surfaces exhibit. Iridescence occurs on materials featuring a repeating surface pattern which size is comparable to the wavelength of the light they reflect.

The optical effects we are interested in reproducing ultimately depends on three factors: the angle of the light source with the surface normal (light direction), the angle of the viewer (view direction) and the distance between the repeating gaps.

We want our shader to add iridescent reflections on top of the normal effects that the Standard material usually comes with. For this reason, we will extend the lighting function of a Standard Surface shader. If you are unfamiliar with the procedure, Physically Based Rendering and Lighting Models provides a good introduction.

Creating a Surface Shader

The first step is to create a new shader.Since we want to extend a shader that already supports physically based lighting, we will start with a Standard Surface Shader.

The newly created CD-ROM shader needs a new property: the distance d used in the diffraction grating equation. Let’s add it to the Properties block, which should now look like this:

This will create a new slider in the Material Inspector. The _Distance property, however, still needs to be coupled with a variable in the CGPROGRAM section:

We are now ready to start.

Customising the Lighting Function

The first step we need to take is to replace the lighting function of the CD-ROM shader with a custom one. We can do this by altering the #pragma directive from:


This forces Unity to delegate the lighting calculation to a function called LightingDiffraction. It is important to understand that we want to extend the behaviour of this Surface shader, not override it. For this reason, out new lighting function will start by simply calling Unity’s Standard PBR lighting function:

As you can see from the snippet above, the new LightingDiffraction simply calls LightingStandard and returns its value. If we compile the shader now, we will see no difference in the way it renders materials.

Before continuing, however, we need to create an additional function to handle the Global Illumination. Since we are not interested in changing that behaviour, our new global illumination function will once be a proxy for Unity’s Standard PBR function:

Lastly, please note that since we are using LightingStandard and LightingDiffraction_GI directly, we will need to include UnityPBSLighting.cginc our shader.

Implementing the Diffraction Grating

This is the core of our shader. We are finally ready to implement the diffraction grating equations seen in The Mathematics of Diffraction Grating. In that post, we concluded that the viewer sees an iridescent reflection which is a sum of all the wavelengths w which satisfy the grating equation:

    \[\left | \sin{\theta_L} - \codt \sin{ \theta_V } \right |= \frac{n \cdot w}{d}\]

with n being an integer number greater than 0.

Given a certain pixel, the values for \theta_L (given by the light direction), \theta_V (given by the view direction) and d (the gap distance) are known. The unknown variables are w and n. The easiest thing to do is to loop over certain values of n, to see which wavelengths satisfy the grating equation.

When we know which wavelengths contribute to the final iridescent reflection, we calculate their associated colours and add them together. The Improving the Rainbow discussed several approached to convert wavelengths from the visible spectrum into colours. for this tutorial, we will use spectral_zucconi6 as it provides the best approximation with the cheapest computational cost.

Let’s see a possible implementation below:

In the snippet above we use values of n up to 8. For better results, you can go higher, although this should already account for a significant part of the iridescent reflection.

We now have one last thing left to do. Calculating sin_thetaL and sin_thetaV. That requires to introduce yet another concept: the tangent vector. We will see how to calculate that in the next part of this tutorial.


You can find the complete series here:

You can download the Unity package for the CD-ROM Shader effect on Patreon.

📧 Stay updated

A new tutorial is released every week.

💖 Support this blog

This websites exists thanks to the contribution of patrons on Patreon. If you think these posts have either helped or inspired you, please consider supporting this blog.


Write a Comment


  1. Hello Alan Zucconi:
    Thanks for your great tutorial.
    I have a question about the CG code.
    In the “For loop” of the code, you set n not bigger than 8,
    Can you explain the reason of that?
    Thank you very much.

    • Sure!
      I am looping through multiple of the wavelength to take into account all colours that are subjected to constructive interference.
      With values higher than 8 you won’t see any difference since the wavelengths are outside the visible spectrum!


  • The Nature of Light - Alan Zucconi December 26, 2017

    I am looping through multiple of the wavelength to take into account all colours that are subjected to constructive interference.
    With values higher than 8 you won’t see any difference since the wavelengths are outside the visible spectrum!