top of page

SHADER KARATE:

PROCEDURAL SHADERS IN GLSL

Toon shader (HSB levels and contour edges)

Toon

​

void main () 

{

           // Original Blinn diffuse intensity: vec3 Id = Ld * Kd * dot_prod;    

        vec3 Kd = vec3(texture(Fox_BaseColor, texture_coords));

    vec3 Is = vec3(0.0);    // Will be added to in lights loop.

    vec3 Id = vec3(0.0);

        vec3 color = vec3(0.0);

    vec3 surface_to_viewer_eye = normalize (-P);   // Vector toward camera for specular.

        vec3 foxIntensities = vec3(0.2, 0.4, 0.9);

        float edgeDot = dot(N, surface_to_viewer_eye ); // Dot product between surface normal and 

                                //  direction to eye (for determining edges).

 

    // Diffuse and specular intensities per light

        for (int i=0; i<2; i++)

    {

        // Vectors here are appended with _eye as a reminder once in view space versus world space.

        // "Eye" is used instead of "camera" since reflectance models often phrased that way.

        vec3 light_position_eye = vec3 (view_mat * vec4 (light_positions_world[i], 1.0));

        vec3 distance_to_light_eye = light_position_eye - P;

        vec3 direction_to_light_eye = normalize (distance_to_light_eye);

        float dot_prod = max( 0.0, dot(direction_to_light_eye, N) );

    

        // Blinn specular

        vec3 half_way_eye = normalize (surface_to_viewer_eye + direction_to_light_eye);

        float dot_prod_specular = max (dot (half_way_eye, N), 0.0);

        float specular_factor = pow (dot_prod_specular, specular_exponent);

        Is += Ls * Ks * specular_factor; // final specular intensity

 

        //    the diffuse component.

        Id += Ld * Kd;

        Id *= 0.5;     // Averaging components of the two lights.

        

    } // End per light

 

        Id = capAtIntensities(Id, foxIntensities);

 

        if (abs(edgeDot) < 0.5)  // Close to zero, normal almost perpendicular to view.

        {

            color = vec3(0.0);    // Black edges

        }

        else

        {

            color = edgeDot * (Is + Id);

        }

 

    // Final color with no ambient.

    fragment_color = vec4 (color, 1.0);

​

​

​

   

Language:  C++ and GLSL 

Date:  Fall 2018

Class:  Rendering and Shading (Graduate)

​

I implemented 5 procedural shaders written in GLSL: 

  • Using shaping functions to create the impression of a William Turner painting (his 1817 work of Mount Vesuvius).

  • Mimicking W. H. Lizar’s Spectra-Hue Diagram (1834) using curve functions for controlling the band of colors by red, yellow, and blue and plotting those curves.

  • Creating and animating trigonometric shapes that look like flowers or snowflakes.

  • Layering noise to generate a marble-like texture.

  • Writing a "toon" shader that (1) segments the brightness/intensities of the diffuse-texture RGB as Hue-Saturation-Brightness (HSB) values into four different levels and (2) adding dark, contour edges based on where the surface normals are perpendicular to the viewing direction.

​

The reference images are in the PDF description, and several code snippets are provided.

Vesuvius.png

Mount Vesuvius

Lizars.png

Spectra-Hue Diagram

Snowflakes/Flowers

​

marble.png

Blue-Black Marble

​

Vesuvius

​

void main() 

{

    vec2 st = gl_FragCoord.xy / u_resolution;

    vec3 color = vec3( 0.0 );

    vec3 color1 = vec3( 0.0 );

    vec3 color2 = vec3( 0.0 );

    vec3 t_x = vec3( st.x );     // Percentage horizontally along X.

    vec3 t_y = vec3( st.y );     // Percentage along Y.

  

    // Compute new x values based on y, or y based on x. 

    // Can be different functions per RGB.

    t_y.r = smoothstep( 0.0, 0.9, st.x ) 

            * sin( st.y * PI );

    t_y.g = t_y.r * 0.8;    // Balances some red that appear on top.

    t_y.b = t_y.r * 0.9;    // Also balance. Helps maintain original defined colors.

 

    t_x.r = smoothstep( 0.2, 0.5, st.y ) 

            * sin( st.x * PI );

    t_x.g = t_x.r * 0.9;    // Tone down some greens!

    t_x.b = t_x.r * 0.8;    // Tone down some blue that also shows up.

 

 

    // Two passes to incorporate two colors.  The first two mixed together will

    //    be more prominent than the third.

    color1 = mix( color_gray, color_white, t_x );    // White orb in center framed with gray

    color2 = mix( color1, color_red, t_y );          // Bold red framing whites.

    color = mix( color1, color2, 0.8);               // Blend the two but with more emphasis on the red (color2).

 

    gl_FragColor = vec4(color, 1.0);

}

​

Spectra-Hue​

​

void main()

{

    vec2 st = gl_FragCoord.xy/u_resolution;

    vec3 color = vec3(0.0);

 

    vec3 yellowLine = vec3(0.0);

    vec3 yellowColor = vec3(0.0);

    vec3 blueLine = vec3(0.0);

    vec3 blueColor = vec3(0.0);

    vec3 redLine = vec3(0.0);

    vec3 redColor = vec3(0.0);

 

    // Function for each color yellow, blue, red in y and their percentage x based on y.

    // Each function is a sine.

    // verticalTrans + amp*sin(xRads*intervalScale - horizontalTrans)

 

    float yellow_y = bannerPos+0.2 + 0.5*( sin(st.x*PI*1.5 - 0.35));

    float yellow_pct = plot(st, yellow_y);  

 

    float blue_y = bannerPos+0.2 + 0.2*(sin(st.x*PI*1.4 - 1.5)) ;  

    float blue_pct = plot(st, blue_y);

 

    float red_y = bannerPos+0.15 + 0.3*(sin(st.x*PI*1.7 + 0.4));  

    float red_pct = plot(st, red_y);

 

 

    // We map x (0.0 - 1.0) to the hue (0.0 - 1.0)

    // And the the inverse of x to the brightness, darkening towards the right.

    //color = hsb2rgb( vec3(st.x, 1.0, 1.2-st.x) );

    //color = color * vec3( 1.0 - step(bannerPos, st.y) );

 

 

    // Yellow function/line

    //yellowColor.rg = vec2(yellow_y) * (1.0 - step(bannerPos, st.y));

    yellowColor.g = yellow_y * (1.0 - step(bannerPos, st.y));

    yellowLine = yellow_pct*vec3(1.0,1.0,0.0) + (1.0-yellow_pct)*color;

 

    blueColor.b = blue_y * (1.0 - step(bannerPos, st.y));

    blueLine = blue_pct*vec3(0.0,0.0,1.0) + (1.0-blue_pct)*color;

 

    redColor.r = red_y * (1.0 - step(bannerPos, st.y));

    redLine = red_pct*vec3(1.0,0.0,0.0) + (1.0-red_pct)*color;

 

    color = yellowColor + yellowLine*vec3( step(bannerPos, st.y))

           + blueColor + blueLine*vec3( step(bannerPos, st.y))

           + redColor + redLine*vec3( step(bannerPos, st.y));

 

    gl_FragColor = vec4(color,1.0);

 

}

​

​

Snowflakes

​

void main()

{

    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    vec3 color = vec3(0.0);

    vec2 pos = vec2(0.0);

    float r = 0.0;

    float a = 0.0; 

    float f = 0.0;

    float step = 0.0;

    int numSnowflakes = 5;

  

    // Generate some number of snowflakes and alter properties based on each one's loop-ID. 

    // A cycle of pulsing (based on u_time affecting radii) lasts about 7 seconds.

  

    step = 1.0 / float(numSnowflakes+1);

    for (int i=1; i<numSnowflakes+1; i++)

    {

        if (mod(float(i), 2.0) == 0.0)

        {

            pos = vec2( step*float(i), step*float(i) ) - st;

            a = atan(pos.y, pos.x);

            r = length(pos)*5.0 + abs( sin(PI*u_time*0.1*float(i)) );

 

            // cos(a*petalHardness) * sin(petalHardness)

            f = abs( cos(a*15.0*float(i)) 

                    * sin(a*float(numSnowflakes-i+1)) );

        }

        else

        {

            pos = vec2( 1.0-(step*float(i)), step*float(i) ) - st;

            a = atan(pos.x, pos.y);

            r = length(pos)*4.0 + (1.0 - abs( sin(PI*u_time*0.1*float(i))) );

 

            // cos(a*numPetals)*petalHardness

            f = abs(cos( a*2.5*float(i) )) * 0.8;

        }

 

        color.r += ( 1.0 - smoothstep(f, f+0.1, r) );

        color.b += ( 1.0 - smoothstep(f, f+(1.0-step*float(i)), r) );

        color.g += ( 1.0 - smoothstep(f, f+step*float(i), r) );

    }

    

    gl_FragColor = vec4(color, 1.0);

}

​

bottom of page