Page 66 - Computer Graphics Handout
P. 66
2.7.4 Program Structure
Every program we write will have a similar structure to our gasket program. We will always use the GLUT toolkit. The main function
will then consist of calls to GLUT functions to set up our window(s) and to make sure that the local environment supports the
required display properties. The main will also name the required callbacks and callback functions. Every program must have a
display callback, and most will have other callbacks to set up interaction. The init function will set up user options, usually through
OpenGL functions in the GL library. Although these options could be set in main, it is clearer to keep GLUT functions separate
fromOpenGL functions. In the majority of programs, the graphics output will be generated in the display callback.
Every application, no matter how simple, must provide both a vertex shader and a fragment shader. Setting up the shaders requires
a number of steps, including reading the shader code from files, compiling the code, and linking the shaders with the application.
These steps are almost identical for most applications. Hence, we will put this code into a function init Shaders. These operations
require a handful of OpenGL functions that have little to do with graphics. Consequently, we place the details of these functions in
Appendix A.
2.8 THE GASKET PROGRAM
We can now complete our gasket program. We have already created the points and put them in an array. Now we have to get these
data to our GPU and render them. We start by creating a vertex-array object that will allow us to bundle data associated with a
vertex array. Use of multiple vertex-array objects will make it easy to switch among different vertex arrays.We use glGenVertexArray
to find an unused name for the buffer. The first time the function glBindVertexArray is executed for a given name, the object is
created. Subsequent calls to this function make the named object active. For this example, we need only a single vertex array buffer
that we set up as follows:
GLuint abuffer;
glGenVertexArrays(1, &abuffer);
glBindVertexArray(abuffer);
Next, we create a buffer object on the GPU and place our data in that object.We need three functions that we can call after we have
generated our points:
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points),
points, GL_STATIC_DRAW);
First, we use glGenBuffers to give us an unused identifier for our buffer object that is put into the variable buffer. The function
glBindBuffer creates the buffer with the identifier from glGenBuffers. The type GL_ARRAY_BUFFER indicates that the data in the
buffer will be vertex attribute data rather than some one of the other storage types that we will encounter later. Finally, with
glBufferData, we allocate sufficient memory on the GPU for our data and provide a pointer to the array holding the data. Once data
is in GPU memory, we might, as in this example, simply display it once. But in more realistic applications we might alter the data,
redisplay it many times, and even read data back from the GPU to the CPU. Modern GPUs can alter how they store data to increase
efficiency depending on the type of application. The final parameter in glBufferData gives a hint of how the application plans to use
the data. In our case, we are sending it once and displaying it so the choice of GL_STATIC_DRAW is appropriate. The code to compute
the points and create the buffer object can be part of initialization.
2.8.1 Rendering the Points
When we want to display our points, we can use the function
glDrawArrays(GL_POINTS, 0, N);
which causes N data to be rendered starting with the first point. The value of the first parameter, GL_POINTS, tells the GPU we want
the data to be used to display distinct points rather than other primitives such as lines or polygons that could be described by the
same data. Thus, a simple display callback is
void mydisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_POINTS, 0, N);
66

