88

I'm new to OpenGL and Graphics Programming. I've been reading a textbook which has been really thorough and well-written so far.However, I've hit a point in the code that I'm not quite understanding and I'd like to make sense of these lines before I move on.

GLuint abuffer;

glGenVertexArrays(1, &abuffer);
glBindVertexArray(abuffer);

GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

The book explains that the first three lines are creating a vertex-array object, which is used to bundle associated data with a vertex array. The second line finds an unused name (I'm guessing an unsigned integer identifier stored in abuffer) and the third line creates the object / makes it active.

The book explains that the 4th-7th lines creating a buffer object to store our data, with the 5th line giving us an unused identifier (similar to line 2 for the vertex array object?), the 6th line creating the buffer, and the 7th line allocating sufficient memory on the CPU and creating a pointer to our data (points) for GL_STATIC_DRAW.

What does it mean for the object to be active? When would you subsequently use abuffer? What does it mean for a vertex array to bundle associated data, and when was the data associated with this vertex-array object?

I'm confused about the relationship between abuffer and buffer. I'm confused about what the vertex array's relationship with the buffer object is, and at what point that relationship is formed. I'm not sure whether they are, in fact related, but they are presented in the textbook one immediately after the other.

Any help would be appreciated. Thanks.

vallentin
  • 23,478
  • 6
  • 59
  • 81
DashAnimal
  • 935
  • 1
  • 7
  • 10

5 Answers5

168

From a low-level perspective, you can think of an array as having two parts to it:

  • Information about the size, shape, and type of the array (e.g., 32-bit floating point numbers, containing rows of vectors with four elements each).

  • The array data, which is little more than a big blob of bytes.

Even though the low-level concept has mostly stayed the same, the way you specify arrays has changed several times over the years.

OpenGL 3.0 / ARB_vertex_array_object

This is the way you probably should be doing things today. It is very rare to find people who can't run OpenGL 3.x and yet still have money to spend on your software.

A buffer object in OpenGL is a big blob of bits. Think of the "active" buffer as just a global variable, and there are a bunch of functions which use the active buffer instead of using a parameter. These global state variables are the ugly side of OpenGL (prior to direct state access, which is covered below).

GLuint buffer;

// Generate a name for a new buffer.
// e.g. buffer = 2
glGenBuffers(1, &buffer);

// Make the new buffer active, creating it if necessary.
// Kind of like:
// if (opengl->buffers[buffer] == null)
//     opengl->buffers[buffer] = new Buffer()
// opengl->current_array_buffer = opengl->buffers[buffer]
glBindBuffer(GL_ARRAY_BUFFER, buffer);

// Upload a bunch of data into the active array buffer
// Kind of like:
// opengl->current_array_buffer->data = new byte[sizeof(points)]
// memcpy(opengl->current_array_buffer->data, points, sizeof(points))
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

Now, your typical vertex shader takes vertexes as input, not a big blob of bits. So you need to specify how the blob of bits (the buffer) is decoded into vertexes. That is the job of the array. Likewise, there is an "active" array which you can think of as just a global variable:

GLuint array;
// Generate a name for a new array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Make the buffer the active array buffer.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Attach the active buffer to the active array,
// as an array of vectors with 4 floats each.
// Kind of like:
// opengl->current_vertex_array->attributes[attr] = {
//     type = GL_FLOAT,
//     size = 4,
//     data = opengl->current_array_buffer
// }
glVertexAttribPointer(attr, 4, GL_FLOAT, GL_FALSE, 0, 0);
// Enable the vertex attribute
glEnableVertexAttribArray(attr);

OpenGL 2.0 (the old way)

In OpenGL 2.x, there weren't vertex arrays and the data was just global. You still had to call glVertexAttribPointer() and glEnableVertexAttribArray(), but you had to call them every time that you used a buffer. In OpenGL 3.x, you just set up the array once.

Going back to OpenGL 1.5, you could actually use buffers, but you used a separate function to bind each kind of data. For example, glVertexPointer() was for vertex data, and glNormalPointer() was for normal data. Prior to OpenGL 1.5, there weren't buffers, but you could use pointers into your application memory.

OpenGL 4.3 / ARB_vertex_attrib_binding

In 4.3, or if you have the ARB_vertex_attrib_binding extension, you can specify the attribute format and the attribute data separately. This is nice because it lets you easily switch one vertex array between different buffers.

GLuint array;
// Generate a name for a new array array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Enable my attributes
glEnableVertexAttribArray(loc_attrib);
glEnableVertexAttribArray(normal_attrib);
glEnableVertexAttribArray(texcoord_attrib);
// Set up the formats for my attributes
glVertexAttribFormat(loc_attrib,      3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(normal_attrib,   3, GL_FLOAT, GL_FALSE, 12);
glVertexAttribFormat(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexAttribBinding(loc_attrib,      0);
glVertexAttribBinding(normal_attrib,   0);
glVertexAttribBinding(texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
// This replaces several calls to glVertexAttribPointer()
// Note: you don't need to bind the buffer first!  Nice!
glBindVertexBuffer(0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glBindVertexBuffer(0, buffer2, 0, 32);

OpenGL 4.5 / ARB_direct_state_access

In OpenGL 4.5, or if you have the ARB_direct_state_access extension, you no longer need to call glBindBuffer() or glBindVertexArray() just to set things up... you specify the arrays and buffers directly. You only need to bind the array at the end to draw it.

GLuint array;
// Generate a name for the array and create it.
// Note that glGenVertexArrays() won't work here.
glCreateVertexArrays(1, &array);
// Instead of binding it, we pass it to the functions below.

// Enable my attributes
glEnableVertexArrayAttrib(array, loc_attrib);
glEnableVertexArrayAttrib(array, normal_attrib);
glEnableVertexArrayAttrib(array, texcoord_attrib);
// Set up the formats for my attributes
glVertexArrayAttribFormat(array, loc_attrib,      3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(array, normal_attrib,   3, GL_FLOAT, GL_FALSE, 12);
glVertexArrayAttribFormat(array, texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexArrayAttribBinding(array, loc_attrib,      0);
glVertexArrayAttribBinding(array, normal_attrib,   0);
glVertexArrayAttribBinding(array, texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
glVertexArrayVertexBuffer(array, 0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glVertexArrayVertexBuffer(array, 0, buffer2, 0, 32);

// You still have to bind the array to draw.
glBindVertexArray(array);
glDrawArrays(...);

ARB_direct_state_access is nice for a lot of reasons. You can forget about binding arrays and buffers (except when you draw) so you don't have to think about hidden global variables that OpenGL is tracking for you. You can forget about the difference between "generating a name for an object" and "creating an object" because glCreateBuffer() and glCreateArray() do both at the same time.

Vulkan

Vulkan goes even farther and has you write code like the pseudocode I wrote above. So you'll see something like:

// This defines part of a "vertex array", sort of
VkVertexInputAttributeDescription attrib[3];
attrib[0].location = 0; // Feed data into shader input #0
attrib[0].binding = 0;  // Get data from buffer bound to slot #0
attrib[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attrib[0].offset = 0;
// repeat for attrib[1], attrib[2]
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 7
    **+1** for the description of GL's selector/latch mechanism (*e.g.* the buffer object a vertex pointer applies to is taken from the bound object rather than a passed parameter). This API design can be worked around using [`GL_EXT_direct_state_access`](http://www.opengl.org/registry/specs/EXT/direct_state_access.txt), but that does rule out running your code on Mesa, Intel or Apple drivers unfortunately :-\ However, I think it is important to point out that Vertex Arrays have existed since GL 1.1. The only thing that changed in GL 2.0 was the introduction of generic vertex attrib slots. – Andon M. Coleman Feb 08 '14 at 23:21
  • 4
    Speaking of OpenGL 1.1, this version also introduced texture ***objects***. Like Vertex Array Objects, textures were already supported in GL before Texture Objects were introduced, but the data/state was not stored persistently. Instead of changing the bound texture object in OpenGL 1.0, most software was written to perform the texture data/state setup in display lists. GL 3.0 did something similar for vertex arrays, it introduced vertex array ***objects***. – Andon M. Coleman Feb 08 '14 at 23:33
  • 9
    Love the historical context here. One of the most god-awfully frustrating things about learning GL is how the API and best practices have changed over time. – Cheezmeister Mar 02 '15 at 06:22
  • 1
    For your example on OpenGL4.5, shouldn't you use `glCreateVertexArrays(1, &array)` over `glGenVertexArrays(1, &array)`? To my understanding, the vao isn't created until you first bind it using `glGenVertexArrays`, so none of your glEnableVertexArray* / glVertexArray* calls would work? – Acorn Oct 20 '16 at 02:46
  • 1
    @Acorn: Good catch, I've revised it to try to make clear the difference between generating a name and creating an object. – Dietrich Epp Oct 20 '16 at 03:55
  • @DietrichEpp great answer. Can you explain one thing though - You mention "Quickly bind all attributes to use "buffer2"", so before DSA, to switch attributes to use another buffer you would have bind the vao, and call `glVertexAttribPointer`, is just binding to another buffer significantly faster (is that the benefit)? Also why not create two bindings in the vao and change `glVertexArrayAttribBinding`? – Startec Jul 16 '17 at 21:31
  • Before ARB_direct_state_access / 4.5 you could use ARB_vertex_attrib_binding / 4.3, which is what gives you glVertexArrayAttribBinding as you mention. – Dietrich Epp Jul 16 '17 at 21:39
  • The pseudo code is a fantastic way to explain what is happening behind to curtain of the OpenGL calls. So kudos for that. Also, this particular sentence was a big 'a-ha' moment for me: `Now, your typical vertex shader takes vertexes as input, not a big blob of bits. So you need to specify how the blob of bits (the buffer) is decoded into vertexes`. – dawid Apr 26 '19 at 08:51
15

Your interpretation of the book is not completely correct. Vertex Array Objects store no data. They are a class of objects known as containers, like Framebuffer Objects. You may attach/associate other objects with them, but they never store data themselves. As such they are not a context shareable resource.

Basically Vertex Array Objects encapsulate vertex array state in OpenGL 3.0. Beginning with OpenGL 3.1 (in lieu of GL_ARB_compatibility) and OpenGL 3.2+ Core profiles, you must have a non-zero VAO bound at all times for commands like glVertexAttribPointer (...) or glDrawArrays (...) to function. The bound VAO forms the necessary context for these commands, and stores the state persistently.

In older versions of GL (and compatibility), the state stored by VAOs was a part of the global state machine.

It is also worth mentioning that the "current" binding for GL_ARRAY_BUFFER is not one of the states that VAOs track. While this binding is used by commands such as glVertexAttribPointer (...), VAOs do not store the binding they only store pointers (the GL_ARB_vertex_attrib_binding extension introduced alongside GL 4.3 complicates this a bit, so let us ignore it for simplicity).

VAOs do remember what is bound to GL_ELEMENT_ARRAY_BUFFER, however, so that indexed drawing commands such as glDrawElements (...) function as you would expect (e.g. VAOs re-use the last element array buffer bound).

Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • Why does the glVertexAttribPointer require a buffer such as GL_ARRAY_BUFFER to be bound? Doesn’t the glVertexAttribPointer only setup the state of the VAO? – Brad Zeis May 08 '14 at 00:56
  • 1
    @BradZeis: `glVertexAttribPointer` establishes a pointer that is relative to memory owned by whatever is bound to `GL_ARRAY_BUFFER` when the function is called. Honestly, it is kind of a stupid design - in an API designed like D3D, you would just pass the ID of the buffer object to the `VertexAttribPointer` and never have to bind anything. But in OpenGL, `glVertexPointer (...)` existed for 10+ years before VBOs were introduced, and they wanted to reuse as much of the API without modification as possible. They created this *bind a non-zero VBO first* business to avoid adding another parameter. – Andon M. Coleman May 08 '14 at 01:18
  • Thanks! It makes a lot more sense to think of the VAO as pointers. – Brad Zeis May 08 '14 at 17:16
  • 3
    Thanks for pointing out that **binding for GL_ARRAY_BUFFER is not one of the states that VAOs track**. – neevek Aug 06 '17 at 17:03
8

The relationship is created when calling glVertexAttribPointer.

Overview of VertexArrays

GL_VERTEX_ARRAY_BINDING and GL_ARRAY_BUFFER_BINDING are constants but they can point to the global state of the binding. I'm referring to the state not the constant(orange) in the image. Use glGet to find about different global states.

VertexArray is grouping information(including array buffer) about a vertex or many parallel vertices.

Use GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING with glGetVertexAttrib to find which attribute array buffer is set.

glBindBuffer(GL_ARRAY_BUFFER sets the global state GL_ARRAY_BUFFER_BINDING

glBindVertexArray sets the global state GL_VERTEX_ARRAY_BINDING

vallentin
  • 23,478
  • 6
  • 59
  • 81
Jossi
  • 1,020
  • 1
  • 17
  • 28
  • Hey, this is a great graph. Do you mind sharing the source of it, please? – dawid Apr 26 '19 at 08:55
  • @dawid I made it in lucidchart https://www.lucidchart.com/invitations/accept/70b4f311-6516-4fc2-9092-982cb6b981ed. I might remove it in the future because there are documents limit on free account. – Jossi Aug 26 '19 at 20:17
6

OpenGL is a stateful interface. It's bad, antiquated and ugly but that is legacy for ya.

A vertex array object is a collection of buffer bindings that the driver can use to get the data for the draw calls, most tutorials only use the one and never explain how to use multiple VAOs.

A buffer binding tells opengl to use that buffer for related methods in particular for the glVertexAttribPointer methods.

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
  • 1
    Some impressive heuristic in StackOverflow tried to prevent me from complimenting you on criticizing OpenGL for its statefulness. – ArchaeaSoftware Oct 01 '15 at 22:48
-2

There is no relationship between VertexArray and VBO.

A vertex array allocates the memory in RAM and sends pointer to the API. VBO allocates the memory in the graphics card - system memory has no address for it. If you need to access the vbo data from system you need to copy it from vbo to the system first.

Also, in newer versions of OpenGL, vertex arrays are completely removed (depcrecated in 3.0, removed in 3.1)

Gasim
  • 7,615
  • 14
  • 64
  • 131
  • 1
    Vertex arrays aren't removed in 3.x. Vertex array objects were *introduced* in 3.0, and *mandatory* in later versions of the core profile. – Dietrich Epp Feb 08 '14 at 22:54
  • 2
    The question was about Vertex Array Objects, not vertex arrays in general. And even so, vertex arrays are not deprecated either. The API used to set them up ***has*** changed radically (no more specialized VetexPointer, NormalPointer, etc...), but vertex arrays still exist (`glVertexAttrib{I|L}Pointer (...)` substitute for things like `glVertexPointer (...)` in modern GL). – Andon M. Coleman Feb 08 '14 at 23:13