Wiki
Clarifications for Angel's Interactive Computer Graphics

Chapter 6 (Worksheet 4)
Page last edited by Jeppe Revall Frisvad (jerf) 27/09-2017

In the beginning of Sec. 6.6, please note that the z-coordinate is mistakenly negated in the four lines of code setting the tetrahedron vertex positions. This issue is not in the text preceding the code. The negated z-coordinates reverse the winding order of the triangles. Tetrahedron vertices with proper winding order are:

var va = vec4(0.0, 0.0, 1.0, 1);
var vb = vec4(0.0, 0.942809, -0.333333, 1);
var vc = vec4(-0.816497, -0.471405, -0.333333, 1);
var vd = vec4(0.816497, -0.471405, -0.333333, 1);

Toward the end of Sec. 6.6, the triangle function should push a, b, and c rather than pushing the vertex position a thrice. In addition, the index variable is not really necessary, as we can use pointsArray.length to get the number of vertices. The corrected function is:

function triangle(a, b, c) {
  pointsArray.push(a);
  pointsArray.push(b);
  pointsArray.push(c);
}

In Sec. 6.7.1, note that a point light and a directional light are considered the same except for the homogeneous coordinate being 1 for a point light and 0 for a directional light. This means that the code examples would tacitly assume that the direction toward the light (l) is stored for a directional light rather than the direction of the light (le). As illustrated in Figure 6.15, the latter (le) would typically be used to specify a directional light, but the former (l = -le) is more convenient in the code examples, as negation is then unnecessary.

In Sec. 6.8.1, the following line has a small typo

var d = Math.max(dot(normal, lightPostiion), 0.0);

and should of course be

var d = Math.max(dot(normal, lightPosition), 0.0);

This is not too important as we strongly recommend that the dot product is calculated in the vertex shader.

In Sec. 6.8.3, one should note that the light source is considered to be a point light in the following line

vec3 L = normalize(lightPosition - vPosition).xyz;

The full code that also deals with a directional light is available in the online code example:

vec3 pos = (modelViewMatrix * vPosition).xyz; // pos is vertex position in eye coordinates
vec3 L; // vector from vertex position to light source
if(lightPosition.w == 0.0) L = normalize(lightPosition.xyz); // check for directional light
else L = normalize(lightPosition.xyz - pos);

Please note that the negation of pos appearing in the code listed in the textbook is a mistake. One should also note that since pos is in eye space, lightPosition should also be in eye space. Thus the view matrix should be used to transform lightPosition to eye space on the CPU side for this code to work accurately. Unfortunately, this is not done in the example code associated with the textbook. A better implementation is then

vec3 pos = (modelViewMatrix * vPosition).xyz;
vec3 light = (viewMatrix * lightPosition).xyz;
vec3 L = lightPosition.w == 0.0 ? normalize(light) : normalize(light - pos);

Transformation of normal vectors to eye space is discussed in the Sec. 6.8.3 as an afterthought. In this context, it should be mentioned that there is a function for calculating the normal matrix N (the inverse transpose of the upper left 3x3 part of the model-view matrix). Thus, it is important to note that in the JavaScript part of the code, you can calculate the normal matrix and upload it to the vertex shader as follows:

var N = normalMatrix(ctm, true);
gl.uniformMatrix3fv(gl.getUniformLocation(program, "normalMatrix"), false, flatten(N));

where ctm is the current transformation matrix.

In the vertex shader listed in Sec. 6.8.3, the names of the variables Kd and Ks are misnomers. Consulting Sec. 6.3.2 and 6.3.3, we observe that Kd and Ks are really the cosine terms appearing in the lighting calculations while the variables diffuseProduct and specularProduct contain kdLd and ksLs, respectively.

In Sec. 6.9, the triangle function is inaccurate as the homogeneous coordinate of the normals should be set to zero. Consulting the online code example, we observe that a corrected version of this function is

function triangle(a, b, c) {
  pointsArray.push(a);
  pointsArray.push(b);
  pointsArray.push(c);

  normalsArray.push(vec4(a[0], a[1], a[2], 0.0));
  normalsArray.push(vec4(b[0], b[1], b[2], 0.0));
  normalsArray.push(vec4(c[0], c[1], c[2], 0.0));
}

In Sec. 6.10, after listing the vertex and fragment shaders, the authors of the textbook make the following important statement:

"Note that we normalize vectors in the fragment shader rather than in the vertex
shaders. If we were to normalize a variable such as the normals in the vertex shader,
that would not guarantee that the interpolated normals produced by the rasterizer
would have the unit magnitude needed for the lighting computation."

However, they unfortunately forget to normalize the vectors N, L, and E in the fragment shader. This mistake is also in the online code examples, and this is the reason why their shadedSphere4 example exhibits Gouraud shading even though lighting calculations were moved to the fragment shader. Please remember to normalize all the varying vectors in the fragment shader when implementing Phong shading.

Support: +45 45 25 74 43