Abhijit Joshi

ENGINEER. PROGRAMMER. ARTIST.

OpenGL Tutorial 006: Using Vertex Arrays & Indices

As we saw in the previous tutorial, using vertex arrays can significantly increase the efficiency of rendering complex geometries. But sometimes, several vertices are used more than once for drawing primitives. For example, consider drawing a wireframe representation of a cube.

A cube has 8 vertices and as you can see, each vertex is used three times when drawing a wireframe model.

Using an array of indices provides an elegant solution, where we define only 8 vertices and then define an indices array that tells OpenGL the order in which primitive elements (lines, in this case) using the vertices should be drawn.


#include <GLFW/glfw3.h>    // GLFW
#include <OpenGL/glu.h>    // for using gluLookAt( ... )
#include <cmath>           // for using sin() and cos()

static void key_callback(GLFWwindow* window, int key, int scancode,
                         int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

// OpenGL calls to draw a cube with a particular "zoom" factor and 
// to view it from a specified direction in space

void drawCube(const float *vertices, const GLuint *indices,
              const float scale, const int time)
{
        // select background color to be black
        float R = 0, G = 0, B = 0, alpha = 0;
        glClearColor(R, G, B, alpha);

        // clear all pixels in the window with the color selected above
        glClear(GL_COLOR_BUFFER_BIT);

        // enable use of vertex coordinate information from the array
        glEnableClientState(GL_VERTEX_ARRAY);

        glVertexPointer(3,                // number of coordinates per vertex (X,Y,Z)
                        GL_FLOAT,         // type of numbers
                        sizeof(float)*7,  // stride - gap between each set of (X,Y,Z)
                        &vertices[0]);    // offset - location of initial (X,Y,Z)

        // enable use of vertex color information from the array
        glEnableClientState(GL_COLOR_ARRAY);

        glColorPointer(4,                 // number of color values per vertex (R,G,B,A)
                       GL_FLOAT,          // type of values
                       sizeof(float)*7,   // stride - gap between each set of (R,G,B,A)
                       &vertices[3]);     // offset - location of initial (R,G,B,A)

        // enable use of indexing information
        glEnableClientState(GL_INDEX_ARRAY);
        glIndexPointer(GL_UNSIGNED_INT, 0, indices);

        // replace current transformation matrix with the identity matrix
        glLoadIdentity();

        // set clipping planes in the X-Y-Z coordinate system
        float xmin = -scale, xmax = scale;
        float ymin = -scale, ymax = scale;
        float zmin = -20*scale, zmax = 20*scale;
        glOrtho(xmin, xmax, ymin, ymax, zmin, zmax);

        // position the camera at a certain point and check how the view looks

        float angle = time*0.003;

        gluLookAt (cos(angle), sin(angle), 1.0, // camera position at (x1,y1,z1)
                          0.0,  0.0, 0.0,       // camera looks towards point (x2,y2,z2)
                          0.0,  0.0, 1.0);      // the "up" vector (x3,y3,z3)

        // increase line width (the default value is 1)
        glLineWidth(3);

        // draw command
        glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, indices);
}

int main(void)
{
    //--------------------------------
    //   Create a WINDOW using GLFW
    //--------------------------------

    GLFWwindow *window;

    // initialize the library
    if(!glfwInit())
    {
        return -1;
    }

    // window size for displaying graphics
    int WIDTH  = 600;
    int HEIGHT = 600;

    // set the window's display mode
    window = glfwCreateWindow(WIDTH, HEIGHT, "Triangle", NULL, NULL);
    if(!window)
    {
        glfwTerminate();
        return -1;
    }

    // make the windows context current
    glfwMakeContextCurrent(window);

    // enable quitting the graphics by pressing ESC
    glfwSetKeyCallback(window, key_callback);

    // vertex data for drawing a cube (8 vertices)
    const float vertices[] =
    {
    //   X     Y     Z   R  G  B  A
      -0.5, -0.5, -0.5,  1, 0, 0, 1, // vertex 0
       0.5, -0.5, -0.5,  0, 1, 0, 1, // vertex 1
       0.5,  0.5, -0.5,  1, 0, 1, 1, // vertex 2
      -0.5,  0.5, -0.5,  1, 1, 0, 1, // vertex 3
      -0.5, -0.5,  0.5,  1, 0, 0, 1, // vertex 4
       0.5, -0.5,  0.5,  1, 0, 0, 1, // vertex 5
       0.5,  0.5,  0.5,  0, 1, 1, 1, // vertex 6
      -0.5,  0.5,  0.5,  1, 0, 1, 1  // vertex 7
    };

    // indices data for drawing the 12 edges
    const GLuint indices[] =
    {
        0, 1,  // edge 0
        1, 2,  // edge 1
        2, 3,  // edge 2
        3, 0,  // edge 3
        4, 5,  // edge 4
        5, 6,  // edge 5
        6, 7,  // edge 6
        7, 4,  // edge 7
        0, 4,  // edge 8
        1, 5,  // edge 9
        2, 6,  // edge 10
        3, 7   // edge 11
    };

    //---------------------------------------
    // Loop until the user closes the window
    //---------------------------------------

    float scale = 3, delta = 0.01;
    int time = 0;
    while(!glfwWindowShouldClose(window))
    {
        // draw the cube
        drawCube(vertices, indices, scale, time);

        // swap front and back buffers
        glfwSwapBuffers(window);

        // poll for and processs events
        glfwPollEvents();

        // adjust scale
        scale += delta;
        if ( (scale > 4) || (scale < 1) ) delta = -delta;

        // increment time
        time++;
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

To compile this code, use the following Makefile:

NAME = useIndices

all:
    g++ -I /usr/local/include/ -L /usr/local/lib ${NAME}.cpp -o cpp.x -framework OpenGL -framework Cocoa -framework IOKit -lglfw

clean:
    rm *.x