Difference between revisions of "Vertex Array Object"

From OpenGL Wiki
Jump to navigation Jump to search
(No longer needed, as 100% of its info is elsewhere.)
Line 1: Line 1:
{{infobox feature
#REDIRECT [[Vertex Specification#Vertex Array Object]]
| core = 3.0
| core_extension = [http://www.opengl.org/registry/specs/ARB/vertex_array_object.txt 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 [[Buffer Objects]] are associated with vertex attributes.
VAOs are a collection of state, like all OpenGL Objects. Unlike [[Textures|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:
<source lang="cpp">
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;
    VertexAttribute 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 {{code|glVertexPointer}}, {{code|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 whether a buffer object is in {{code|pBufferObj}}.
=== Semantics ===
So what does all of this state mean?
Let us say that you are issuing a {{apifunc|glDrawElements}} or similar command while a particular VAO is bound. In our pseudo-code, let us say that the VAO binding is called {{code|pVAO}}. Here is what will happen, relative to our pseudo-code definition.
The <code>pVAO->pElementArrayBufferObject</code> 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 {{apifunc|glDrawArrays}}, but not {{apifunc|glDrawElements}} without an element buffer.
If attribute index zero is not enabled (<code>pVAO->attributes[0].bIsEnabled == GL_FALSE</code>), an error is raised; attribute index 0 must always be enabled in a VAO you render with.
For each attribute ''i'' for which <code>pVAO->attributes[i].bIsEnabled == GL_TRUE</code>, the buffer object (<code>pBufferObj</code>) will be read starting at the offset value (<code>pBufferObjectOffset</code>), with the given stride (<code>iStride</code>) for each element and of the given number of fields and type components (<code>iSize</code> and <code>eType</code>). 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 is 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 <code>VertexArrayObject::pElementArrayBufferObject</code> is controlled by the binding of GL_ELEMENT_ARRAY_BUFFER. Calling <code>void glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObj)</code> will set this field of the VAO.
The <code>VertexAttribute::bIsEnabled</code> field is set by {{code|glEnableVertexAttribArray(attribIndex)}} and {{code|glDisableVertexAttribArray(attribIndex)}}, for the given attribute index.
The other vertex attribute state is governed by these functions:
  void {{apifunc|glVertexAttribPointer}}( GLuint ''index'', GLint ''size'', GLenum ''type'',
    GLboolean ''normalized'', GLsizei ''stride'', const void *''offset'');
  void {{apifunc|glVertexAttribIPointer}}( GLuint ''index'', GLint ''size'', GLenum ''type'',
    GLsizei ''stride'', const void *''offset'' );
You may notice that these functions do not take 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 {{enum|GL_ARRAY_BUFFER}}. Thus, before calling either of these functions, the user should call <code>glBindBuffer(GL_ARRAY_BUFFER, bufferObj)</code> with the buffer of interest for this attribute.
: '''Note:''' Changing the {{enum|GL_ARRAY_BUFFER}} binding does ''not'' affect VAO state. This is different from the {{enum|GL_ELEMENT_ARRAY_BUFFER}} binding, which is directly part of VAO state. It is only when calling one of these two functions that the {{enum|GL_ARRAY_BUFFER}} binding matters to the VAO.
The same buffer object can be used by multiple attributes. This is perfectly legitimate and can result in faster performance. Depending on the vertex data, of course.
These functions will cause the <code>index</code> 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 {{code|offset}} parameter is technically a pointer in {{apifunc|glVertexAttribPointer}} calls. It is however interpreted as a byte-offset into the buffer object that is currently bound to {{enum|GL_ARRAY_BUFFER}}. To call this function, you have to perform a cast operation:
<source lang="cpp">
glVertexAttribPointer(..., (void*)(byte_offset));
Where {{code|byte_offset}} is an integer.
The "IPointer" version of the function is used to create integral attributes. Thus, when you use this function, <code>VertexAttribute::bIsIntegral</code> 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), this function does not have a ''normalized'' field.
It is perfectly legal to call these functions ''before'' calling the enable/disable functions.
These are the only functions that affect VAO state.
=== Double-precision attributes ===
OpenGL 4.1, and implementations that support the [http://www.opengl.org/registry/specs/ARB/vertex_attrib_64bit.txt GL_ARB_vertex_attrib_64bit extension], allow the use of double-precision vertex attributes.
All vertex attribute functions previously discussed set the {{code|bIsDouble}} field to GL_FALSE. The only way to specify a double-precision attribute is with this new function:
  void {{apifunc|glVertexAttribLPointer}}( GLuint ''index'', GLint ''size'', GLenum ''type'',
    GLsizei ''stride'', const void *''offset'' );
The ''type'' field must be {{enum|GL_DOUBLE}}. The rest work as expected. Using this function sets {{code|bIsDouble}} to {{enum|GL_TRUE}}. If this function is used, the user must be using a `double` or `dvec` attribute type in the vertex shader.
== Legacy ==
If you are not using shaders (or using a form of shader that doesn't accept [[GLSL]]'s generic attributes), there are a number of alternate functions for setting the non-generic vertex attributes.
VAOs encapsulate the non-generic attribute data as well. <code>glEnableClientState</code> and <code>glDisableClientState</code> function like <code>glEnableVertexAttrib</code> and <code>glDisableVertexAttrib</code>, except for non-generic attributes.
Texture coordinates are access a bit differently. There is only one texture coordinate setup function, <code>glTexCoordPointer</code>, and it only works for one texture coordinate. You can change which coordinate this function modifies by calling <code>glClientActiveTexture</code> with the texture index you wish to use. This function also controls which texture gets enabled/disable with <code>glEnableClientState</code> and <code>glDisableClientState</code>.
Overall, the gl*Pointer calls take the same parameters as <code>glVertexAttribPointer</code>, but there are some differences. None of the non-generic vertex arrays can feed an integral attribute, so there is no equivalent to <code>glVertexAttribIPointer</code>. And some of these functions are missing some fields compared to <code>glVertexAttribPointer</code>, because these fields are assumed to be a certain value. They also usually lack an explicit "normalize" field; the values are normalized or not intrinsically.
Table 2.5 in the OpenGL 3.2 compatibility specification details what the restrictions are on these functions.
== See Also ==
* [[Vertex Specification]]
* [[Vertex Attributes]]
== Reference ==
* [[:Category:Core API Ref Vertex Arrays]]: Function documentation for all functions that affect VAOs and VAO state.
[[Category:Vertex Specification]]
[[Category:Vertex Specification]]

Latest revision as of 01:22, 8 September 2012