95

Just want to make sure I understand this correctly (I'd ask on SO Chat, but it's dead in there!):

We've got a Vertex Array, which we make "current" by binding it
then we've got a Buffer, which we bind to a Target
then we fill that Target via glBufferData which essentially populates whatever was bound to that target, i.e. our Buffer
and then we call glVertexAttribPointer which describes how the data is laid out -- the data being whatever is bound to GL_ARRAY_BUFFER and this descriptor is saved to our original Vertex Array

(1) Is my understanding correct?
The documentation is a little sparse about how everything correlates.

(2) Is there some kind of default Vertex Array? Because I forgot/omitted glGenVertexArrays and glBindVertexArray and my program worked fine without it.


Edit: I missed a step... glEnableVertexAttribArray.

(3) Is the Vertex Attrib tied to the Vertex Array at the time glVertexAttribPointer is called, and then we can enable/disable that attrib via glEnableVertexAttribArray at any time, regardless of which Vertex Array is currently bound?

Or (3b) Is the Vertex Attrib tied to the Vertex Array at the time glEnableVertexAttribArray is called, and thus we can add the same Vertex Attrib to multiple Vertex Arrays by calling glEnableVertexAttribArray at different times, when different Vertex Arrays are bound?

Keiwan
  • 8,031
  • 5
  • 36
  • 49
mpen
  • 272,448
  • 266
  • 850
  • 1,236

2 Answers2

218

Some of the terminology is a bit off:

  • A Vertex Array is just an array (typically a float[]) that contains vertex data. It doesn't need to be bound to anything. Not to be confused with a Vertex Array Object or VAO, which I will go over later
  • A Buffer Object, commonly referred to as a Vertex Buffer Object when storing vertices, or VBO for short, is what you're calling just a Buffer.
  • Nothing gets saved back to the vertex array, glVertexAttribPointer works exactly like glVertexPointer or glTexCoordPointer work, just instead of named attributes, you get to provide a number that specifies your own attribute. You pass this value as index. All your glVertexAttribPointer calls get queued up for the next time you call glDrawArrays or glDrawElements. If you have a VAO bound, the VAO will store the settings for all your attributes.

The main issue here is that you're confusing vertex attributes with VAOs. Vertex attributes are just the new way of defining vertices, texcoords, normals, etc. for drawing. VAOs store state. I'm first going to explain how drawing works with vertex attributes, then explain how you can cut down the number of method calls with VAOs:

  1. You must enable an attribute before you can use it in a shader. For example, if you want to send vertices over to a shader, you're most likely going to send it as the first attribute, 0. So before you render, you need to enable it with glEnableVertexAttribArray(0);.
  2. Now that an attribute is enabled, you need to define the data it's going to use. In order to do so you need to bind your VBO - glBindBuffer(GL_ARRAY_BUFFER, myBuffer);.
  3. And now we can define the attribute - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);. In order of parameter: 0 is the attribute you're defining, 3 is the size of each vertex, GL_FLOAT is the type, GL_FALSE means to not normalize each vertex, the last 2 zeros mean that there's no stride or offset on the vertices.
  4. Draw something with it - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. The next thing you draw may not use attribute 0 (realistically it will, but this is an example), so we can disable it - glDisableVertexAttribArray(0);

Wrap that in glUseProgram() calls and you have a rendering system that works with shaders properly. But let's say you have 5 different attributes, vertices, texcoords, normals, color, and lightmap coordinates. First of all, you would be making a single glVertexAttribPointer call for each of these attributes, and you'd have to enable all the attributes beforehand. Let's say you define the attributes 0-4 as I have them listed. You would enable all of them like so:

for (int i = 0; i < 5; i++)
    glEnableVertexAttribArray(i);

And then you would have to bind different VBOs for each attribute (unless you store them all in one VBO and use offsets/stride), then you need to make 5 different glVertexAttribPointer calls, from glVertexAttribPointer(0,...); to glVertexAttribPointer(4,...); for vertices to lightmap coordinates respectively.

Hopefully that system alone makes sense. Now I'm going to move on to VAOs to explain how to use them to cut down on the number of method calls when doing this type of rendering. Note that using a VAO is not necessary.

A Vertex Array Object or VAO is used to store the state of all the glVertexAttribPointer calls and the VBOs that were targeted when each of the glVertexAttribPointer calls were made.

You generate one with a call to glGenVertexArrays. To store everything you need in a VAO, bind it with glBindVertexArray, then do a full draw call. All the draw bind calls get intercepted and stored by the VAO. You can unbind the VAO with glBindVertexArray(0);

Now when you want to draw the object, you don't need to re-call all the VBO binds or the glVertexAttribPointer calls, you just need to bind the VAO with glBindVertexArray then call glDrawArrays or glDrawElements and you'll be drawing the exact same thing as though you were making all those method calls. You probably want to unbind the VAO afterwards too.

Once you unbind the VAO, all the state returns to how it was before you bound the VAO. I'm not sure if any changes you make while the VAO is bound is kept, but that can easily be figured out with a test program. I guess you can think of glBindVertexArray(0); as binding to the "default" VAO...


Update: Someone brought to my attention the need for the actual draw call. As it turns out, you don't actually need to do a FULL draw call when setting up the VAO, just all the binding stuff. Don't know why I thought it was necessary earlier, but it's fixed now.

Robert Rouhani
  • 14,512
  • 6
  • 44
  • 59
  • 10
    "A Vertex Buffer Object, or VBO (sometimes referred to as just a Buffer Object)" It's "sometimes" called that because that's actually what it is called. It is just a buffer object, no different from any other buffer object that you might use for uniform blocks, pixel transfer, transform feedback, or any other use. The OpenGL spec *never* refers to anything as a "vertex buffer object"; even the [original extension spec](http://www.opengl.org/registry/specs/ARB/vertex_buffer_object.txt) never calls it that. – Nicol Bolas Jan 02 '12 at 21:08
  • 3
    Excellent answer. Thanks for taking the time to write this out! A couple follow up questions though: (1) You said "before you render, and before you define the attribute, you need to enable it with glEnableVertexAttribArray(0)" -- are you sure it needs to be enabled before the call to `glVertexAttribPointer`? In my tests, the order doesn't seem to matter. (2) If I understand you correctly, the Vertex Attributes are global, and only their enabled/disabled state is saved to the currently bound VAO? – mpen Jan 02 '12 at 21:55
  • 1
    (1) I don't think the order matters, as long as you've enabled it before `glDrawArrays` or `glDrawElements`. I'll update the post to reflect that (2) Yes, but it's not just the enable/disable state that's stored, it's everything relating to those calls - what was bound to GL_ARRAY_BUFFER at the time, the type, stride, and offset. Essentially it's storing enough to change all the Vertex Attributes back to the way you set them up with the VAO. – Robert Rouhani Jan 02 '12 at 22:02
  • @RobertRouhani: In that case, you're telling me I could bind one VAO, set up some vertex attributes, then bind a different VAO and reuse *the same attribute indexes* without any information being lost? – mpen Jan 02 '12 at 23:24
  • 2
    yes, VAOs are designed to let you replace most of a draw method with binding a VAO. Every entity can have a separate VAO and it'll still work fine. You'll still have to update uniforms and bind your own textures though. And you have to use the same attribute indexes as you have to bind your shader's attributes with attribute indices, whether it's through `layout(location = x)` in the shader or with `glBindAttributeLocation` when compiling the shader. [Example](http://www.opengl.org/wiki/Tutorial1:_Rendering_shapes_with_glDrawRangeElements,_VAO,_VBO,_shaders_(C%2B%2B_/_freeGLUT)) – Robert Rouhani Jan 02 '12 at 23:38
  • 8
    Note that in modern OpenGL there is no default vertex array object anymore, you have to create one yourself or your application will not work in a forward-compatible context. – Overv Oct 15 '12 at 12:16
2

The terminology and sequence of APIs to be called is quite confusing indeed. What's even more confusing is how the various aspects - buffer, generic vertex attribute and shader attribute variable get associated. See OpenGL-Terminology for a pretty good explanation.

Further, the link OpenGL-VBO,shader,VAO shows a simple example with the necessary API calls. It's particularly good for those transitioning from immediate mode to the programmable pipeline.

Hope it helps.

Edit: As you can see from the comments below, people can make assumptions and jump to conclusions. The reality is that it's quite confusing for beginners.

ap-osd
  • 2,624
  • 16
  • 16
  • "*See OpenGL-Terminology for a pretty good explanation.*" In less than 1 minute looking, I already found one piece of misinformation: "These are replaced with generic vertex attributes with an identifier (called index) that associates to a shader variable (for cooordinates, color etc.) that process the attribute." They're not called "indices"; they're "locations". That's a very important distinction because vertex attributes [*also* have "indices"](https://www.opengl.org/wiki/Program_Introspection#Attributes), which is *very different* from locations. That is a very terrible website. – Nicol Bolas Apr 30 '16 at 04:41
  • 2
    That's a fair comment but not entirely accurate. If you look at the OpenGL API to define a generic attribute [glVertexAttribPointer](https://www.opengl.org/sdk/docs/man/html/glVertexAttribPointer.xhtml), `void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)`, the identifier is referred to as `index`. The same identifier in the context of the program is called `location` in the API [glGetAttribLocation](https://www.opengl.org/sdk/docs/man/html/glGetAttribLocation.xhtml). – ap-osd May 09 '16 at 04:27
  • Does someone know how normalization is defined if both the buffer and shader variable are float? Does it just do nothing? – Desperado17 Jun 15 '21 at 11:30