As we stated in 3D Geometry and Attribute Modeling 101, there are three important modeling tasks to address when we design a scene:
You usually create such a surface over a rectangular domain in the xy-plane, something like:
for (double x=xmin ; x<=xmax ; x+=dx) for (double y=ymin ; y<=ymax ; y+=dy) { cryph::AffPoint p(x, y, f(x,y)); cryph::AffVector normal(gradientOfF(x,y)); // store p and normal in their respective VBOs }The surface above was generated from z = f(x, y) = cos(x2 + y2).
Recalling your calculus days, you implement gradientOfF(x,y) = ∇(f(x,y) - z) = ((∂f/∂x)(x,y), (∂f/∂y)(x,y), -1).
In the case of our surface above, ∇(f(x,y) - z) = ((∂f/∂x)(x,y), (∂f/∂y)(x,y), -1) = (-2xsin(x2 + y2), -2ysin(x2 + y2), -1).
Sometimes height surfaces are only known discretely. The most common example could be characterized as a 2D array representing a grid covering some region of the xy-plane in which we know the height only at each point of the array. This structure is easily mapped to a collection of OpenGL triangle strips. The normal at each vertex could be computed either as the average of all triangle normals sharing the vertex, or as the normal to a single triangle. As an example of the latter, the normal at vertex [r][c] might come from the triangle with vertices [r][c], [r+1][c] and [r][c+1], with proper care being taken for boundary cases.
Some model attributes need to be per-vertex; others are per-primitive. We are focusing on attributes related to the lighting model here, and they are most commonly done as per-primitive. Hence that will be our approach here.