Vase Shader Highlights Tutorial

Introduction Vertex Shader Fragment Shader Summary

Introduction

Swipe to move the 3D blue vase. Colored highlights glance off the vase as it moves. WebGL highlights display with GLSL shaders. Shaders access normals to process lighting. This very short tutorial discusses the vertex and fragment shaders used to highlight the vase. For more details regarding GLSL lighting please read the point light tutorial.

The original vase was modeled with the 3ds Max nurbs and lathe modifier, then exported as a DAE file. The free 3D Translator app converted vertex, texel, and normal values, into arrays, for use with WebGL.

Vertex Shader

The vertex shader follows. Attribute a_normal includes X,Y, and Z values representing a normal for each vertex. Per vertex normals represent the direction a vertex faces. Fragments of color render in the fragment shader, based on values obtained from vertex normals. Lighting glances off fragments based on a combination of light location and the direction a fragment faces. For example, if a fragment faces away from the light, the fragment displays dark. If a fragment faces directly toward the light, the fragment appears bright. Per vertex normals help determine which direction fragments face.

Uniform um3_nmatrix is a normal matrix. The normal matrix is a 3 x 3 matrix derived from the inverted and transposed model matrix. The model matrix includes rotation, translation (movement), scaling, or other transformations applied to the vase. The normal matrix includes rotation, translation (movement), scaling, or other transformations applied to the normal. The normal matrix helps the shader determine which direction this normal faces. For example, assume the vase rotated 1800 around. Apply the normal matrix to rotate the normal 1800 too.

Last multiply the normal attribute, a_normal, by the normal matrix, um3_nmatrix. Assign the result to varying v_normal_transform. The GPU (Graphics Processing Unit) interpolates values, then sends a varying with the exact same name, to the fragment shader.

Also the fragment shader will have access to the interpolated position of this particular vertex. Varying v_position receives the product of the current vertex coordinate multiplied by the model matrix. Varying v_position represents the current vertex with any interactive transformations applied to the vase.

attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_tex_coord0;

uniform mat4 um4_matrix;
uniform mat4 um4_pmatrix;
uniform mat3 um3_nmatrix;

varying vec2 v_tex_coord0;
varying vec3 v_normal_transform;
varying vec4 v_position;

void main(void) {

 v_position = um4_matrix * vec4(
  a_position, 1.0
 );

 gl_Position = um4_pmatrix * v_position;

 v_tex_coord0 = a_tex_coord0;
        
 // Vertex normal times
 // transposed inverted model matrix.
 // Send to the GPU for 
 // interpolation before
 // fragment shader processing
 v_normal_transform = um3_nmatrix * a_normal;
}

Fragment Shader

The following code includes the entire fragment shader. Notice the fragment shader includes varyings, v_normal_transform and v_position, with the same name and type as the vertex shader. Each varying represents values assigned in the vertex shader. However the GPU processes varyings for more precise values, before passing them to the fragment shader.

This fragment shader includes a number of constant vectors. A vector represents a line with direction. Vector c_light_location represents the direction of light rays. Vector constants c_light_dcolor, c_light_scolor modify the amount of diffuse and specular color applied. For more details regarding shader lighting please see the point light tutorial.

precision mediump float;

varying vec2 v_tex_coord0;
varying vec3 v_normal_transform;
varying vec4 v_position;

const float c_shine_amount = 16.0;
const vec3 c_ambient = vec3(0.2,0.2,0.4);

const vec3 c_light_location = vec3(1.0,1.0,0.5);
const vec3 c_light_dcolor = vec3(0.8,0.8,2.0);
const vec3 c_light_scolor = vec3(0.9,0.8,0.8);

uniform sampler2D u_sampler0;

void main(void) {
 
 // Difference between 
 // the light's position
 // and the current 
 // vertex position.  
 vec3 v3_light_vector = 
  normalize(
   c_light_location - v_position.xyz
  );
 
 // Normal times transposed 
 // inverted model matrix.
 vec3 v3_normalized_normal = 
  normalize(
   v_normal_transform
  );

 float f_specular_weight = 0.0;
 
 // Line between view point
 // and current vertex           
 vec3 v3_direction_eye = 
  normalize(
   -v_position.xyz
  );
 
 // Reflection of the 
 // opposite of the light vector
 // and the normal.
 vec3 v3_direction_reflection = 
  reflect(
   -v3_light_vector, 
   v3_normalized_normal
  );

 f_specular_weight = 
  pow(
   max(
    dot(
     v3_direction_reflection, 
     v3_direction_eye
    ), 
    0.0
  ), 
  c_shine_amount
 );       

 float f_diffuse_weight = 
  max(
   dot(
    v3_normalized_normal, 
    v3_light_vector
   ), 
   0.0
  );
  
 vec3 v3_light_weight = c_ambient
  + c_light_scolor * f_specular_weight
  + c_light_dcolor * f_diffuse_weight;

 vec4 color0 = 
  texture2D(
   u_sampler0, 
   v_tex_coord0
  );
 
 gl_FragColor = 
  vec4(
   color0.rgb * v3_light_weight, 
   color0.a
  );
  
}

Summary

Swipe to move the 3D blue vase. Colored highlights glance off the vase as it moves. WebGL highlights display with GLSL shaders. Shaders access normals to process lighting. This very short tutorial included the vertex and fragment shaders used to highlight the vase. For more details regarding GLSL lighting please read the point light tutorial.

The original vase was modeled the 3ds Max nurbs and lathe modifiers, then exported as a DAE file. The free 3D Translator app converted vertex, texel, and normal values, into arrays, for use with WebGL.

Copyright © 2015 Seven Thunder Software. All Rights Reserved.