ACADEMIC RESEARCH AND PUBLICATIONS
during Graduate and Undergraduate Studies
Doctoral Candidate in Computer Science
Visual Computing Division, Digital Production Arts
Clemson University | School of Computing
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.
Mount Vesuvius
Spectra-Hue Diagram
Snowflakes/Flowers
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);
}