To render our geometry and take advantage of our normals, we require a shader that implements the lighting equation. This is a more sophisticated application of GLSL. Here is a summary of GLSL that covers everything we'll need to know.
The uniforms configure the transformation matrices and the lighting parameters. The attributes configure the vertices. The varyings define the values to be interpolated by the rasterizer. Pay close attention to their types.
precision mediump float
uniform
mat4
projectionMatrixuniform
mat4
viewMatrixuniform
mat4
modelMatrixuniform
vec4
lightPositionattribute
vec4
vertexPositionattribute
vec3
vertexNormalvarying
vec3
fragmentNormalvarying
vec3
fragmentLightvarying
vec3
fragmentViewThe vertex shader uses the scene parameters defined by the attributes and uniforms to calculate $n$, $l$, and $v$, the inputs to the lighting model. Note, lighting calculations are done in eye space because this allows us to infer that the viewer sits at the origin.
normalize
(mat3
(modelViewMatrix) · vertexNormal)normalize
(vec3
(q − p))normalize
(vec3
(−p))gl_Position
← projectionMatrix · modelViewMatrix · vertexPositionThe lighting equation itself is implemented in the fragment shader. Here are the inputs.
precision mediump float
varying
vec3
fragmentNormalvarying
vec3
fragmentLightvarying
vec3
fragmentViewuniform
vec3
modelColoruniform
vec3
lightColorAnd here is the calculation. We arbitrarily choose a constant specular exponent of 10, but this could easily be another uniform.
normalize
(fragmentNormal)normalize
(fragmentLight)normalize
(fragmentView)normalize
(l + v)max
(l · n, 0)max
(h · n, 0)10gl_FragColor
← vec4
(fragmentColor, 1.0)As usual, don't forget to use gl.getUniformLocation
, gl.uniformMatrix4f
, gl.uniform3f
, and gl.uniform4f
in the application code to query the locations of each of these uniforms and give them a value.