Vertex Array Object

From OpenGL Wiki
Revision as of 02:21, 27 August 2009 by Alfonse (talk | contribs) (Formatting fix.)
Jump to navigation Jump to search
Vertex Array Object
Core in version 4.6
Core since version 3.0
Core ARB extension ARB_Vertex_Array_Object

Vertex Array Objects (VAO) are OpenGL Objects that store the set of bindings between Vertex Attributes and the user's source vertex data.

VAO State

Vertex Array Objects are subject to a degree of misunderstanding, primarily due to the way that Vertex Buffer Objects are associated with vertex attributes.

VAOs are a collection of state, like all OpenGL Objects. Unlike texture objects or Buffer Objects, VAOs are pure state objects; they do not contain any large blocks of data or anything.

If you were to define the VAO state in terms of a C structs, it would look like the following:

struct VertexAttribute
    bool bIsEnabled = GL_FALSE;
    int iSize = 4; //This is the number of elements in this attribute, 1-4.
    unsigned int iStride = 0;
    VertexAttribType eType = GL_FLOAT;
    bool bIsNormalized = GL_FALSE;
    bool bIsIntegral = GL_FALSE;
    void * pBufferObjectOffset = 0;
    BufferObject * pBufferObj = 0;

struct VertexArrayObject
    BufferObject *pElementArrayBufferObject = NULL;
    VertexAttributeState attributes[GL_MAX_VERTEX_ATTRIB];

The values given in the struct, the ones to the right of the "=", represent the default state of a newly-created VAO.

Legacy Note: In contexts where the fixed-function pipeline is still available, VAOs can still be used. They simply store more attributes, namely the fixed-function attributes set up by commands like glVertexPointer, glNormalPointer and so forth. So the VAO struct in the above pseudo-code example would have additional VertexAttribute objects stored in it. Similarly, compatibility mode allows pBufferObjectOffset to be a client pointer; whether it is a client pointer or a buffer object offset depends on the .


So what does all of this state mean?

Let us say that you are issuing a glDrawElements command while a particular VAO is bound. In our pseudo-code, let us say that the VAO binding is called pVAO. Here is what will happen, relative to our pseudo-code definition.

The pVAO->pElementArrayBufferObject is the buffer object from which vertex indices will be pulled. If this is NULL (ie: bound to zero), an error is raised: you can use glDrawArrays, but not glDrawElements without an element buffer.

If attribute index zero is not enabled (pVAO->attributes[0].bIsEnabled == GL_FALSE), an error is raised; attribute index 0 must always be enabled in a valid VAO.

For each attribute i for which pVAO->attributes[i].bIsEnabled == GL_TRUE, the buffer object (pBufferObj) will be read starting at the offset value (pBufferObjectOffset), with the given stride (iStride) for each element and of the given number of fields and type components (iSize and eType). One value is read from all of the enabled attributes for each index from the element array buffer. The assemblage of attributes for a single index from the element array buffer constitutes a single vertex, and will be passed along for vertex processing.

A more detailed look at how rendering from VAO state works available in the article on Vertex Specification.

VAO Functions

As with all standard OpenGL Objects, when a VAO is bound to the context, all functions that modify these state fields will modify them for this object.

The VertexArrayObject::pElementArrayBufferObject is controlled by the binding of GL_ELEMENT_ARRAY_BUFFER. Calling void glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObj) will set this field of the VAO.

The VertexAttribute::bIsEnabled field is set by glEnableVertexAttrib(attribIndex) and glDisableVertexAttrib(attribIndex), for the given attribute index.

void glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObj)

The other vertex attribute state is governed by two functions:

 void VertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *offset);
 void VertexAttribIPointer( GLuint index, GLint size, GLenum type, GLsizei stride, const void *offset );

You may notice that neither of these functions takes a buffer object name. This is because they attach the buffer object implicitly. Rather than having these functions take a buffer object directly, they simply use whatever buffer object is currently bound to GL_ARRAY_BUFFER. Thus, before calling either of these functions, the user should call glBindBuffer(GL_ARRAY_BUFFER, bufferObj) with the buffer of interest for this attribute.

Note: Changing the GL_ARRAY_BUFFER binding does not affect VAO state. This is different from the GL_ELEMENT_ARRAY_BUFFER binding, which is part of VAO state. It is only after calling one of these two functions that the GL_ARRAY_BUFFER binding matters to the VAO.

The same buffer object can be used by multiple attributes. This is perfectly legitimate and can sometimes result in faster performance. Depending on the vertex data, of course.

These functions will cause the index attribute to be modified. In terms of our pseudo-code example, the parameters are equivalent to the state as follows:

 iSize <= size
 iStride <= stride
 eType <= type
 bIsNormalized <= normalized
 pBufferObjectOffset <= offset
 pBufferObj <= current GL_ARRAY_BUFFER binding

The offset parameter is technically a pointer. However, it is really just a byte offset into the buffer object that is currently bound to GL_ARRAY_BUFFER.

The "IPointer" version of the function is used to create integral attributes. Thus, when you use this function, VertexAttribute::bIsIntegral is automatically set to GL_TRUE. Similarly, when you use the other function, it is set to GL_FALSE. Since integral attributes cannot be normalized (as attribute normalization makes no sense when applied to integral attributes).

It is perfectly legal to call these functions before calling the enable/disable functions. But it's generally poor form to do so.

These are the only functions that affect VAO state.

See Also