30

I am writing a little graphics engine using OpenGL ( via OpenTK with C# ).

To define vertex attributes, I have a VertexDeclaration class with an array of VertexElement structures that are mapped to glEnableVertexAttribArray/glVertexAttribPointer calls.

Also, to support multiple vertex streams, I have a special structure holding a vertex buffer, vertex declaration, vertex offset and instance frequency (like the XNA's VertexBufferBinding structure).

Currently, whenever a drawing call is invoked, I iterate over all the set vertex streams and bind their vertex buffers, apply vertex declarations, disable unused vertex attributes and draw the primitives.

I would like to use VAOs to cache the glEnableVertexAttribArray calls into them, and whenever a vertex stream is applied, bind the VAO and change its array buffer binding.

Is that a correct usage of VAOs?

Luna Inverse
  • 303
  • 1
  • 3
  • 4
  • it seems [this question](https://stackoverflow.com/questions/17149728/when-should-glvertexattribpointer-be-called) is a useful link here. Not sure why the answer says "No" to "caching" the `glEnableVertexAttribArray` calls. The answer to the linked question "When to call `glVertexAttribPointer`?" says that's exactly what VAOs are for and that's the recommended approach nowadays. – xealits May 12 '18 at 20:42

1 Answers1

63

Is that a correct usage of VAOs?

No1.

glVertexAttribPointer uses the buffer object that was bound to GL_ARRAY_BUFFER at the moment the function was called. So you can't do this:

glVertexAttribPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, bufferObject);
glDrawArrays(...);

This will not use bufferObject; it will use whatever was bound to GL_ARRAY_BUFFER when glVertexAttribPointer was originally called.

VAOs capture this state. So the VAO will, for each vertex attribute, store whatever buffer object was bound to GL_ARRAY_BUFFER when it was called. This allows you to do things like this:

glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, buffer1);
glVertexAttribPointer(0, ...);
glVertexAttribPointer(1, ...);
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
glVertexAttribPointer(2, ...);

Attributes 0 and 1 will come from buffer1, and attribute 2 will come from buffer2. VAO now captures all of that state. To render, you just do this:

glBindVertexArray(VAO);
glDraw*();

In short, if you want to change where an attribute's storage comes from in OpenGL, you must also change it's format. Even if it's the same format, you must call glVertexAttribPointer again.

1: This discussion assumes you're not using the new ARB_vertex_attrib_binding. Or, as it is otherwise known, "Exactly how Direct3D does vertex attribute binding." If you happen to be using an implementation that offers this extension, you can effectively do what you're talking about, because the attribute format is not tied with the buffer object's storage. Also, the tortured logic of glVertexAttribPointer is gone.

In general, the way we solve this in the OpenGL world is to put as many things as possible in the same buffer object. Failing that, just use one VAO for each object.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks for this great answer. Then where does the `glBufferData` step come into action? Don't you have to buffer the data first before using the `glVertexAttribPointer`? Sorry I am very new to OpenGL – Ramon Blanquer May 25 '15 at 16:58
  • I know this is 10 years old, I'm just surprised that no one else has pointed out that OP was working with C#, and the code in this answer is C++. – RWolfe Aug 28 '23 at 06:54