Rendering with index buffers - Fish-In-A-Suit/Conquest GitHub Wiki
The downside with DrawArrays is that you have to specify each vertex of a triangle in your model. A quad may have 4 corners but when a quad is divided in 2 triangles it has 6! With “glDrawElements” we can remove those duplicate vertex definitions. In return we’ll have to tell OpenGL which vertex to use for each triangle by using indices.
To help define how OpenGL should render vertices, use index buffers (they define the order in which OpenGL connects vertices in order to make triangles which can be rendered). Rendering with index buffers prevents duplication of vertices, as shown in the image below. This majorly affects performance, especially when it comes to models whose points are shared by several different vertices.
The process which enables using one vertex over and over is called indexing and is achieved with an index buffer.
Using indexing is very simple. First, you need to create an additional buffer, which you fill with the right indices. The code is the same as before, but now, the target in the context is an ELEMENT_ARRAY_BUFFER, not an ARRAY_BUFFER (this makes sense... a target in the OpenGL context specifies how input data will be used/modified by OpenGL. Since we're performing indexed rather than non-indexed rendering, it makes sense that OpenGL will use vertices differently):
private void bindIndicesBuffer(int[] indices) {
int indicesVboID = glGenBuffers();
vbos.add(indicesVboID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesVboID);
IntBuffer indicesBuffer = storeDataInIntBuffer(indices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);
}
An integer array of indices is passed to the method bindIndicesBuffer. First, a vbo called indicesVboID is created and then added to the ArrayList vbos (for keeping track of it, just optional). Then, the indicesVboID is bound to the target GL_ELEMENT_ARRAY_BUFFER.
To actually populate indicesVboID, you first need to store the array of indices inside an IntBuffer (in my code referenced as indicesBuffer). Then, send indicesBuffer using the method glBufferData to the currently bound buffer in context (which is indicesVboID). This is the method which is responsible for populating an IntBuffer with indices:
private IntBuffer storeDataInIntBuffer(int[] data) {
IntBuffer indicesBuffer = BufferUtils.createIntBuffer(data.length);
indicesBuffer.put(data);
indicesBuffer.flip();
return indicesBuffer;
}
Prior to implementing indexed rendering, the float array of vertices would look like:
float[] positions = {
// Left bottom triangle
-0.5f, 0.5f, 0f,
-0.5f, -0.5f, 0f,
0.5f, -0.5f, 0f,
// Right top triangle
0.5f, -0.5f, 0f,
0.5f, 0.5f, 0f,
-0.5f, 0.5f, 0f
};
But now, since we've removed the need for vertex duplication, only 4 vertices are needed to draw a quad:
float[] vertices = {
-0.5f, 0.5f, 0f, // Left top, V0
-0.5f, -0.5f, 0f, // Left bottom, V1
0.5f, -0.5f, 0f, // Right bottom, V2
0.5f, 0.5f, 0f // Right left, V3
};
Now, however, OpenGL has no way of knowing in which way to connect up the vertices. To specify that, you need to create an int array of indices (0 corresponds to V0, etc ... also note that the indices specify the order of vertices in a counter-clockwise order!):
int[] indices = {
// Left bottom triangle
0, 1, 2,
// Right top triangle
2, 3, 0
};
The method which is now used for rendering is glDrawElements(int mode, int count, int type, long indicesOffset)
- mode: the kind of primitives (basic shapes) being constructed. One of GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_TRIANGLE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY and GL_PATCHES are accepted.
- count: specifies the number of elements to be rendered (make sure to specify the vertexCount here, which should refer to the number of indices of a particular mesh)
- type: one of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT, depending on the data type of indices
- indicesOffset: Specifies an offset of the first index in the array in the data store of the buffer currently bound to the GL_ELEMENT_ARRAY_BUFFER target... 0 if you want the data to be read from the start.