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