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
   61   62   63   64   65   66   67   68   69   70   71