sunLoadingImage
whowedImag
decoration left 1
decoration left 2
transhome
transprojects
transgallery
transarticles
decoration rigth
English

Show/Hide search bar
black cat logo variable logo
[17 Jan 2013]

Usage of Vertex Array Objects, Vertex Buffer Objects and Element Buffer Objects in OpenGL

Vertex Buffer Objects (VBO) are used to save information about vertices that will be used as source of data for attributes of a vertex shader. To render an object you have to create and fill Vertex Buffer Object for each type of attribute (position, color, normal, etc.) present in the vertex shader, indicate which vertex buffer corresponds to which attribute in the vertex shader, indicate how to interpret data in created VBOs and then you can execute draw call.
Vertex Array Objects (VAO) save information about which Vertex Buffer Object is connected to which attribute of the vertex shader, and information about how attributes of the vertex shader should interpret data in connected vertex buffers. In OpenGL 3+ in order to render any object you should bind VAO.
Element Buffer Objects (EBO) are used as source of indices during rendering of indexed primitives.
Following example shows how to initialize VAO, two VBOs and EBO:
const int floatsPerPosition = 3;
const int floatsPerColor = 4;
const int numVertices = 3;
const int sizeOfPositions = sizeof(float) * numVertices * floatsPerPosition;
const int sizeOfColors = sizeof(float) * numVertices * floatsPerColor;
const int numIndices = 3;
const int sizeOfIndices = sizeof(int) * numIndices;
// Positions of vertices on CPU
float positions[numVertices][floatsPerPosition] =
{
   {0, 0, 0},
   {1, 1, 1},
   {1, 0, 1}
};

// Colors of vertices on CPU
float colors[numVertices][floatsPerColor] =
{
   {0, 0, 0, 1},
   {1, 1, 1, 1},
   {1, 0, 1, 1}
};

// Indexes on CPU
int indices[numVertices] =
{
   0, 1, 2
};

////////////////////////////////////////////////////////////////////////

GLuint vao, vbo1, vbo2, ebo; // Identifiers of OpenGL objects

glGenVertexArrays(1, &vao); // Create new VAO
// Binded VAO will store connections between VBOs and attributes
glBindVertexArray(vao);

glGenBuffers(1, &vbo1); // Create new VBO
glBindBuffer(GL_ARRAY_BUFFER, vbo1); // Bind vbo1 as current vertex buffer
// initialize vertex buffer, allocate memory, fill it with data
glBufferData(GL_ARRAY_BUFFER, sizeOfPositions, positions, GL_STATIC_DRAW);
// indicate that current VBO should be used with vertex attribute with index 0
glEnableVertexAttribArray(0);
// indicate how vertex attribute 0 should interpret data in connected VBO
glVertexAttribPointer(0, floatsPerPosition, GL_FLOAT, GL_FALSE, 0, 0);

glGenBuffers(1, &vbo2); // Create new VBO
glBindBuffer(GL_ARRAY_BUFFER, vbo2); // Bind vbo2 as current vertex buffer
// initialize vertex buffer, allocate memory, fill it with data
glBufferData(GL_ARRAY_BUFFER, sizeOfColors, colors, GL_STATIC_DRAW);
// indicate that current VBO should be used with vertex attribute with index 1
glEnableVertexAttribArray(1);
// indicate how vertex attribute 1 should interpret data in connected VBO
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, 0, 0);

// Create new buffer that will be used to store indices
glGenBuffers(1, &ebo);
// Bind index buffer to corresponding target
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// ititialize index buffer, allocate memory, fill it with data
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeOfIndices, indices, GL_STATIC_DRAW);

// reset bindings for VAO, VBO and EBO
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Function glBufferData() allocates new region of memory for a buffer. That's why this functions should be used only during initialization of the buffer, but not during modification of buffer's contents. To update contents of the buffer you can use glBufferSubData() or glMapBufferRange() functions. Usage of glMapBufferRange() may be better in terms of performance, because in such case you don't need to allocate memory yourself (just use pointer provided by OpenGL). On some operating systems glMapBufferRange() may fail if called when application loses control over graphics memory.
To render the objects you have to bind corresponding VAO and exucute draw call:
// After initialization VAO contains all required information about which VBO corresponds to which attibute in the vertex shader, and how to interpret data in VBOs. So simply bind VAO and it will automatically initialize all states required for rendering.
glBindVertexArray(vao);
// Non-indexed draw call
glDrawArrays(GL_TRIANGLES, 0, numVertices);

/////////////////////////////////////////////////////////////////

// bind index buffer if you want to render indexed data
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// indexed draw call
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, NULL);
Previous examples show how to use separate Vertex Buffer Objects for each attribute of the vertex shader, but it's possible to store data in a single VBO. Data in buffers can be grouped by attributes (data for first attribute for all vertices, then data for second attribute for all vertices) or grouped by vertices, so called interleaved attributes (data for first vertex, then data for second vertex and so on).
Following example shows how to use interleaved attributes. glVertexAttribPointer() accepts relative offsets of attributes. Stride is equal to size of all attributes for single vertex.
const int floatsPerPosition = 3;
const int floatsPerColor = 4;
const int numVertices = 2;
const int positionsOffset = 0 * sizeof(float);
const int colorsOffset = floatsPerPosition * sizeof(float);
const int sizeOfVertex = (floatsPerPosition + floatsPerColor) * sizeof(float);

// Vertex buffer on CPU. Positions are interleaved with colors.
float data[numVertices * (floatsPerPosition * floatsPerColor)] =
{
   1, 1, 1, // Position of first vertex
   1, 1, 1, 1// Color of first vertex
   0, 0, 0, // Position of second vertex
   0, 0, 0, 1 // Color of second vertex
};

// stride is equal to sizeOfVertex and initial offset = positionsOffset
glVertexAttribPointer(0, floatsPerPosition, GL_FLOAT, GL_FALSE, sizeOfVertex,
                       (void*)(positionsOffset));
// stride is equal to sizeOfVertex, and initial offset = colorsOffset
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, sizeOfVertex,
                       (void*)(colorsOffset));
And this example shows how to use single buffer with data grouped by attributes. glVertexAttribPointer() accepts offsets to start of positions and colors. Stride is equal to 0, because there is no additional gap between consequtive positions or colors.
const int floatsPerPosition = 3;
const int floatsPerColor = 4;
const int numVertices = 2;
const int positionsStart = 0 * sizeof(float);
const int colorsStart = numVertices * floatsPerPosition * sizeof(float);

// Vertex buffer data on CPU. Positions for all vertices, then colors for all verticers.
float data[numVertices * (floatsPerPosition * floatsPerColor)] = {
   0, 0, 0, // Position of first vertex. Start of positions.
   1, 1, 1, // Position of second vertex
   0, 0, 0, 1, // Color of first vertex. Start of colors.
   1, 1, 1, 1 // Color of second vertex
};

// stride is equal to 0, and initial offset = positionsStart
glVertexAttribPointer(0, floatsPerPosition, GL_FLOAT,
                       GL_FALSE, 0, (void*)(positionsStart));
// stride is equal to 0, and initial offset = colorsStart
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT,
                       GL_FALSE, 0, (void*)(colorsStart));



Sun and Black Cat- Igor Dykhta (igor dykhta email) 2007-2014