The shader program illustrates several new features of the shader langauge:

The language supports structs, similar to C but not with the full complexity.
GLSL structs can contain arrays but not other structs. The structure definitions for
materialProperties and lightProperties define new types that can then
be used to declare variables just like the built-in types.

The language supports arrays, including arrays of structs. But array indexing
might be limited to constants and for-loop variables.

Structs can be used for uniforms, but not for attributes or varyings.
Arrays can be used for uniforms and varyings, but not for attributes.

Functions are defined and used just as in C (but recursion is not allowed).

Note that the calculations in the lighting function are vector calculations,
working with values of type vec3. This allows the function to do the computations
for the R, G, and B components of the color all at once.

Lighting calculations are done in view coordinates. They use vertex coordinates that
have been transformed by the modelview matrix. Light positions are assumed to be in view
coordinates already. That is, they have already been transformed by the appropriate modelview
matrix. In standard OpenGL, light positions are automatically transformed by the current
modelview matrix when the position is specified, and the position is stored in view coordinates.
In this example, light positions are not transformed, which means that they are given
directly in view coordinates. Another way of saying this is that the light positions are given
relative to the viewer, and the lights keep the same position even as the world is being
rotated.

Normal vectors also have to be transformed to view coordinates before they can be used
in the lighting computation. However, normal vectors are not transformed by the modelview
matrix. Instead, they are transformed by the inverse transpose of the upper-left 3-by-3
submatrix of the modelview matrix. (This can be shown mathematically
and it can be shown geometrically for simple
cases, but I don't know a good geometric argument for the general case.) The normalTransform
matrix is computed in the JavaScript program and sent to the shader program by saying:

This program uses arrays and structs of uniforms. Unfortunately, a uniform location on
the JavaScript side can only refer to one of the built-in shader language types. This means
that to work with a uniform variable that is a struct, you need a separate location for
each item in the struct. And to work with a uniform variable that is an array, you need
a separate location for each element of the array. In the sample programs, I build objects
and arrays of uniform locations that mirror the structure of the variables in the shader
program. At the same time, I assign default values to all uniforms that represent light
and material properties:

Note how the names for the uniforms in the shader program are the full names of
individual items in the values, such as material.diffuse and
light[3].position.

Hierarchical graphics in 3D

The second of today's examples, world2.html, uses hierarchical graphics.
We will spend some time today and Wednesday looking at this example in more detail, including
the definitions of the classes SimpleObject3D and ComplexObject which are used
to build the data structure that represents the content of the world.

The data structure is known as a scene graph. The scene graph represents the content
of the world. To render the world, you have to traverse the data structure and render each
object that you encounter.

Transformations are also part of the scene graph, and you have to manage those transformations
as you traverse the graph.

Eventually, we would like to have viewers and lights as part of a scene graph, but there
are certain complications...