Page 74 - Computer Graphics Handout
P. 74
divide_tetra(v[0], v[1], v[2], v[3], n);
There are two more problems that we must address before we have a useful three-dimensional program. The first is how to deal
with color. If we use just a single color as in our first example, we won’t be able to see any of the three-dimensional structure.
Alternately, we could use the approach of our last example of letting the color of each fragment be determined by where the point
is located in three dimensions. But we would prefer to use a small number of colors and color the face of each triangle with one of
these colors. We can set this scheme by choosing some base colors in the application, such as
typedef vec3 color3;
color3 base_colors[4] = {color3(1.0, 0.0, 0.0), color3(0.0, 1.0, 0.0),
color3(0.0, 0.0, 1.0), color3(0.0, 0.0, 0.0)};
and then assigning colors to each point as it is generated. We set a color index as we generate the triangles
int colorindex;
void tetra(point3 a, point3 b, point3 c, point3 d)
{
colorindex = 0;
triangle(a,b,c);
colorindex = 1;
triangle(a,c,d);
colorindex = 2;
triangle(a,d,b);
colorindex = 3;
triangle(b,d,c);
}
and then form a color array with a color for each point:
color3 colors[NumVertices];
int i = 0; // number of vertices
void triangle(point3 a, point3 b, point3 c)
/* specify one triangle */
{
colors[i] = base_colors[colorindex];
points[i] = a;
i++;
colors[i] = base_colors[colorindex];
points[i] = b;
i++;
colors[i] = base_colors[colorindex];
points[i] = c;
i++;
}
We send these colors to the GPU along with their associated vertices in a buffer object. Inside of the buffer object, we’ll place the
vertex data at the start of the buffer’s memory and then follow it with the color data. To do this, however, we’ll need to first allocate
a buffer large enough to contain all of the data, and then load data into the buffer in two operations using the OpenGL function
glBufferSubData. The function glBufferSubData allows us to update parts of an existing buffer object with new data. The first
parameter specifies which array in the buffer we want to update. The second parameter specifies which byte in the buffer to start
writing data at, and the third parameter specifies how many bytes to read from the memory pointer, which is passed in using the
fourth parameter. Consider the code:
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Allocate a buffer of uninitialized data of the correct size
glBufferData(GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors),
NULL, GL_STATIC_DRAW);
// Load the separate arrays of data
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(points), points );
glBufferSubData(GL_ARRAY_BUFFER, sizeof(points),
sizeof(colors), colors );
In the above example, the first call updates the bytes in the range [0, sizeof(points)-1]. Since we need to write the data for the colors
immediately after in the buffer, we start at the byte immediately following the vertex data, which is just the length (in bytes) of the
74

