Vertex Program Implementation of the Diffraction Shader

The new nVidia graphics cards support per vertex shading programs. These are programs that take care of the transformation and the lighting of each "vertex" of a model. Each "vertex" typically consists of the position of the vertices, the color or the texture coordinates of each point of your model. But you can also supply your own data. In addition to the "per vertex" data you can also supply your program with a set of constants. Typically, the different viewing matrices, the position of the light sources and of course your own data specific to your program. A vertex program is a set of assembly-like instructions that operate on vectors. For example, a dot product is computed in a single instruction. There are only 17 instructions and your program cannot exceed 128 instructions on the current hardware. Any good old hacker should rejoice, this is the time again to write compact assembly code for an exotic CPU. At least it got me excited. For fun I decided to port my diffraction shader to this new architecture, specifically the CD shader.

First I had to simplify my model somewhat. Cosines, sines and exponentials are not part of the 17 instructions, but can be programmed in about ten instructions, so I had to limit their usage. Also the programming language does not support "for loops" and unfortunately my diffraction shader has a for loop whose size depends on the angles involved. So I simplified the shader and kept only what is essential to the basic look of the diffraction pattern. I ended up with the following formula:
 

        Shader = 2^{-((u*rx)^2+(v*ry)^2)/w^2} sum_{k=0}^5 Ramp (|u|*dx/k)

where

      (u,v,w) = vector halfway between the light direction and the eye direction in local tangent space (T,B,N) : H = uT + vB + wN.
      (rx,ry) = controls the anisotropy of the principal highlight
      dx = spacing between the pits of the CD
      Ramp(x) = a function which returns a color for a given x between 0 and 1. The Ramp should look like a rainbow. Here is the implementation I used

                        Ramp.R(x) = blend(C*(x-0.25))    Ramp.G(x) = blend(C*(x-0.5))     and Ramp.B(x) = blend(C*(x-0.75))

             where

                        blend(x) = x*x > 1 ? 0 : 1-x*x
                        and C=3.2

To compute the power of two I use a well known implementation in 9 instructions. To compute the sum I simply unroll the code 6 times, this is not particularly elegant but do not see any other option since there is no for loop. All the other terms are fairly straightforward to compute.

I also wrote a Fresnel like reflection shader so that the CD would relfect more light at glancing angles. This was a straightforward modification from a shader that I found in the nVidia SDK. I will post the demo once I get the permission to do so.