Vase Shader Highlights Tutorial
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.
Vase 3D Model
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.
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.
The original vase was modeled the 3ds Max
nurbs and lathe modifiers, then exported as a DAE file.
The free WebGL Translator
app converted vertex,
texel, and normal values, into arrays, for use with WebGL.