Diffraction Grating Shader

Hi all,

I’ve completed my diffraction grating shader, and to keep things organized here in BA, I chose to post it here also.

/*
 * Diffraction Grating Reflection
 *
 * by Miguel Porces
 * 2014
 *
 * random generator by Brecht
 *
 */



#include "stdosl.h"

void rng_seed(output int rng, int seed)
{
    int chash = seed;
    if (chash == 0) chash = 1;
    rng = chash * 891694213;
}

float rng_uniform(output int rng)
{
    float res = rng / float(2137483647) * 0.5 + 0.5;
    rng *= 891694213;
    return res;
}

float randomInterval(float minval, float maxval,point ipt)
{
    int rng;
    float f=0;
    float delta=maxval-minval;
    f = fmod(cellnoise(ipt*12345.0), 1.0);
    rng_seed(rng, int(f * 2137483647));

    return delta*rng_uniform(rng)+minval;
}

void rotateVector(output vector v, vector axis, float angle)
{
    float sf=0, cf=0;
    sincos(angle, sf, cf);
    v=(normalize(cross(axis,v))*sf)+(v*cf);
}


shader Diffraction
(
color Color=1.0,
float Distance=1200,
float Roughness=0.0,
float Rotation=0.0,
normal Normal = N,
normal Tangent = normal(0.0,0.0,0.0),
output closure color BRDF=reflection(N)
)

{
    float Rg=clamp(Roughness,0,1);
    
    /* setup Tangent direction */
    normal Tg=Tangent;
    if (Tangent==normal(0.0,0.0,0.0))
    {
        normal Zaxis=normal(0.0,0.0,1.0);
        Zaxis = transform("object", "world", Zaxis);
        Tg=normalize(cross(Zaxis,Normal));
    }
    rotateVector(Tg, Normal, Rotation*M_2PI);
    
    /* setup loop */
    color cl;
    BRDF=0;
    
    vector rht=cross(I,Normal);
    float dtr=dot(Tg,rht);
    float sTheta=length(rht) * dtr;
    
    float wave=randomInterval(380, 780,P);
    float wldst=wave/Distance;

    int ordmin=int(floor(Distance * (-1+sTheta)/wave))-1;
    int ordmax=int(ceil(Distance * (1-sTheta)/wave));
 
    float orddif;
    
    orddif=abs(ordmax-ordmin-1);

    /* sample wavelenght */
    for(int order=ordmin; order<=ordmax;order++){
        if(order!=0){
            /* calculate angle */
            float wfac=order*wldst+sTheta;
            float angle= asin(wfac);


            /*  rotate normal  */
            normal NN=Normal;
            rotateVector(NN, Tg, angle);

            cl=wavelength_color(wave)*(Color)/orddif;

            if(Rg==0.0)
            {
                BRDF += (cl * reflection(NN));
            } else {
                BRDF += (cl * microfacet_beckmann(NN,Rg));
            }
        }
    }
}

Hope you like it :wink:

2 Likes

I love these physically correct special situation shaders! Thank you for your work.

Hi Secrop,

Very nice job…and thks for sharing.
But how do use it, please? like the Playground.blend?

here some examples…


DiffractionExamples.blend (909 KB)
there’s more that can be done… There’s just one thing to remember, you need allways to mix with a glossy, because the script jumps the 0 order. (with this ‘If not 0’ loop removed, there’s no need for a mix with a glossy, and the highlights will be a bit colored.)
I’ve did it so, because you may want to control the 0 order differently…

1 Like

Very good Secrop, thanks you so much!

I love it !

I’ve started one, but I never had time to finish it. yours is really GOOD !

I thank you for liking it.
I still have doubt’s about the best way to optimize it, and if the wavelength color need’s some kind of ColorLambda)=Lumisosity(lambda)*wavelength(lambda)…

But it’s functional, and can be usefull. :slight_smile:

My apologies to all of you that have downloaded the script… But I discovered a small imprecision in it.

In the line #83, where the ordmin is declared, where it was:int ordmin=int(floor(Distance * (-1+sTheta)/wave));

should be:int ordmin=int(floor(Distance * (-1+sTheta)/wave))-1;

This small error, was skipping one of the orders that are still visible. First I thought the floor() would include this order, but it wasn’t. :o

I’ve already edited the first post, to include the change.

This is excellence! Thanks for sharing!!! I’ve already been trying it in a render test in a project, it works really great!!! Can we find a way to pay you and use it in our projects? Please let me know!!

You’re welcome.
Feel free to use it in your projects and there’s no need for any payment (you can mention me in your credits if you want, but I don’t demand it).

1 Like

Sorry to necro this thread, but I just wanted to mention that @Secrop’s script is broken in Blender 4.0 due to the deprecation of the older microfacet closures. Now microfacet_beckmann (and ggx and the others) is rolled into the generic microfacet() closure which requires a few extra parameters:

/*
 * Diffraction Grating Reflection
 *
 * by Miguel Porces
 * 2014
 *
 * random generator by Brecht
 *
 */



#include "stdosl.h"

void rng_seed(output int rng, int seed)
{
    int chash = seed;
    if (chash == 0) chash = 1;
    rng = chash * 891694213;
}

float rng_uniform(output int rng)
{
    float res = rng / float(2137483647) * 0.5 + 0.5;
    rng *= 891694213;
    return res;
}

float randomInterval(float minval, float maxval,point ipt)
{
    int rng;
    float f=0;
    float delta=maxval-minval;
    f = fmod(cellnoise(ipt*12345.0), 1.0);
    rng_seed(rng, int(f * 2137483647));

    return delta*rng_uniform(rng)+minval;
}

void rotateVector(output vector v, vector axis, float angle)
{
    float sf=0, cf=0;
    sincos(angle, sf, cf);
    v=(normalize(cross(axis,v))*sf)+(v*cf);
}


shader Diffraction
(
color Color=1.0,
float Distance=1200,
float Roughness=0.0,
float Rotation=0.0,
float IOR = 1.4,
int Refract = 0,
normal Normal = N,
normal Tangent = normal(0.0,0.0,0.0),
output closure color BRDF=reflection(N)
)

{
    float Rg=clamp(Roughness,0,1);
    
    /* setup Tangent direction */
    normal Tg=Tangent;
    if (Tangent==normal(0.0,0.0,0.0))
    {
        normal Zaxis=normal(0.0,0.0,1.0);
        Zaxis = transform("object", "world", Zaxis);
        Tg=normalize(cross(Zaxis,Normal));
    }
    rotateVector(Tg, Normal, Rotation*M_2PI);
    
    /* setup loop */
    color cl;
    BRDF=0;
    
    vector rht=cross(I,Normal);
    float dtr=dot(Tg,rht);
    float sTheta=length(rht) * dtr;
    
    float wave=randomInterval(380, 780,P);
    float wldst=wave/Distance;

    int ordmin=int(floor(Distance * (-1+sTheta)/wave))-1;
    int ordmax=int(ceil(Distance * (1-sTheta)/wave));
 
    float orddif;
    
    orddif=abs(ordmax-ordmin-1);

    /* sample wavelenght */
    for(int order=ordmin; order<=ordmax;order++){
        if(order!=0){
            /* calculate angle */
            float wfac=order*wldst+sTheta;
            float angle= asin(wfac);


            /*  rotate normal  */
            normal NN=Normal;
            rotateVector(NN, Tg, angle);

            cl=wavelength_color(wave)*(Color)/orddif;

            if(Rg==0.0)
            {
                BRDF += (cl * reflection(NN));
            } else {
                BRDF += (cl * microfacet("beckmann",NN,Rg,IOR,Refract));
            }
        }
    }
}

2 Likes