Vertex Specification: Difference between revisions

From OpenGL Wiki
Jump to navigation Jump to search
m (Fixing link)
m (Categorizing)
Line 294: Line 294:

* [[Primitives]]
* [[Primitives]]
* [[Conditional Rendering]]

[[Category:Vertex Specification]]
[[Category:Vertex Specification]]

Revision as of 19:59, 1 September 2009

Vertex Specification is the process of setting up the necessary objects for rendering with a particular shader program, as well as the process of using those objects to render.


Submitting vertex data for rendering requires creating a stream of vertices, and then telling OpenGL how to interpret that stream.

Vertex Stream

In order to render at all, you must be using a shader program. This program has a list of expected Vertex Attributes. This set of attributes determines what attribute values you must send in order to properly render with this shader.

For each attribute in the shader, you must provide a list of data for that attribute. All of these lists must have the same number of elements.

The order of vertices in the stream is very important; it determines how OpenGL will render your mesh. The order of the stream can either be the order of data in the arrays, or you can specify a list of indices. The indices control what order the vertices are received in, and indices can specify the same vertex more than once.

Let's say you have the following as your array of 3d position data:

 { {1, 1, 1}, {0, 0, 0}, {0, 0, 1} }

If you simply use this as a stream as is, OpenGL will receive and process these three vertices in order (left-to-right). However, you can also specify a list of indices that will select which vertices to use and in which order.

Let's say we have the following index list:

 {2, 1, 0, 2, 1, 2}

If we render with the above attribute array, but selected by the index list, OpenGL will receive the following stream of vertex attribute data:

 { {0, 0, 1}, {0, 0, 0}, {1, 1, 1}, {0, 0, 1}, {0, 0, 0}, {0, 0, 1} }

The index list is a way of reordering the vertex attribute array data without having to actually change it. This is mostly useful as a means of data compression; in most tight meshes, vertices are used multiple times. Being able to store the vertex attributes for that vertex only once is very economical, as a vertex's attribute data is generally around 32 bytes, while indices are usually 2-4 bytes in size.

Note: Oftentimes, authoring tools will have similar arrays, but the sizes will be differnet. These tools give each attribute array an index list, which allows for greater data compression. OpenGL (and Direct3D, if you're wondering) does not allow this. Each element array must be the same size, and you can only use one list of indices. You must manually convert the format exported by your authoring tool into the format described above.


The above stream is not enough to actually get anything; you must tell OpenGL how to interpret this stream in order to get proper rendering. And this means telling OpenGL what kind of primitive to interpret the stream as.

There are many ways for OpenGL to interpret a stream of 12 vertices. It can interpret the vertices as a sequence of triangles, points, or lines. It can even interpret these differently; it can interpret 12 vertices as 4 independent triangles (take every 3 verts as a triangle), as 10 dependent triangles (every group of 3 sequential vertices in the stream is a triangle), and so on.

The main article on this subject has the details.

Building the Stream

Making a vertex stream in OpenGL requires using two kinds of objects: Vertex Array Objects (VAO) and Vertex Buffer Objects (VBO). VBOs store the actual vertex and index arrays, while VAOs store the settings for interpreting the data in those arrays.

The first step is to create a VAO and bind it to the context.

Vertex Format

Each attribute in the VAO has its own binding point with its own parameters. In the Vertex Array Objects article, we used this pseudocode to explain the state that goes into an attribute binding:

 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;

Here, we will discuss exactly what all of these variables actually mean for building a vertex stream. Recall that this binding data is set by glVertexAttribPointer or glVertexAttribIPointer.

Similarly to how glTexImage2D has an internal format (what the data will be in OpenGL) and an external format (what the data is that you are passing), each attribute has an actual format and the format of the data you are passing.

Because attributes are defined in GLSL, the definition of the actual format is not part of the VAO. Even so, any particular shader that you intend to use a VAO with has an expected list of attributes of a particular format.

However, OpenGL is fairly flexible in conversions between your format and what GLSL expects. GLSL formats for an attribute consist of a vector of some dimensionality between 1 and 4, and are either floating-point or integer. These correspond to the type you define an attribute with (ivec3 is an integer 3d vector, for example.

So long as the attribute binding you create has the correct dimensionality and basic float/int type, that is all OpenGL needs. It will automatically convert anything else as needed.

Your options for specifying your vertex format are varied.

The iSize value is the dimensionality of the attribute. It is an integer from 1-4. If this size is less than the dimensionality of the attribute type defined in GLSL, the unspecified values are filled with 0 (except for W, which is filled with 1). If the size is greater than the dimensionality of the attribute type defined in GLSL, then the

Note: The size can also be GL_BGRA, which is equivalent to 4, but specifies that the order of the components in the buffer object is switched from the expected RGBA. This value is only allowed in glVertexAttribPointer. It requires that you use normalization and only use unsigned byte types. This feature is not very flexible, because it was primarily introduced for Direct3D compatibility reasons; you probably are better off not using it unless you're trying to be D3D compatible.

The bIsIntegral value specifies whether the value is an integer or float. If it is false, then it is a floating-point value; if it is true, then it is integral. This too must match with the attribute definition in the shader. This value is set based on which of the attribute functions you use: "IPointer" sets it to true, the regular "Pointer" version sets it to false.

Data Conversion

The eType is the type of the data in the buffer object, not the attribute's type. This value and bIsNormalized define how the value in the buffer is interpreted and converted into the attribute's type in the shader.

The possible values of eType are broken into categories: integer and floating-point.

Integer types allow signed or unsigned forms of bytes, shorts (16-bit), and ints (32-bit). Floating-point types are float (32-bit), double (64-bit), or half (16-bit half float values).

Floating-point buffer types convert to floating-point attribute types directly. Integer types can be converted to floating point types in one of two ways: either directly (convert the number into a float value) or via normalization. Normalization, activated by setting bIsNormalized to true, causes unsigned integers to be converted into floating point values on the range [0, 1], and signed integers are converted to [-1, 1]. An integer value of 0 gives a normalized float value of 0.0. To get a float value of 1.0, you pass in the largest possible integer for that size (unsigned bytes == 255, signed bytes == 127, etc). To get -1, you pass the most negative integer value (signed bytes == -128, signed shorts == -32768, etc).

Stride and Interleaving

The placement of vertex attribute data within the buffer is governed by the eType, iSize, iStride, and pBufferObjectOffset fields. The type and size determine how big an individual value for the attribute is; 2 unsigned bytes takes up, well two bytes. The iStride specifies how many bytes it takes to get from one element to the next.

You might think that this would simply be the size of the attribute data: the type * iSize. However, OpenGL is very flexible in how you can position data in the buffer. The stride field allows you to interleave vertex data within a buffer. That is, you can put the data for multiple attributes in the same buffer.

Let's say that your vertex data is built by a hard-coded C structure:

 struct Vertex
     float x, y, z;
     unsigned char r, g, b, a;
     float u, v;

You would like your vertex data in your vertex buffer to simply be an array of such structures. So when you bind your buffer and fill it with data, you give it a Vertex* of some particular length.

The position attribute would have a pBufferObjectOffset value of 0, since it is the first entry. It's size is 3 and type is GL_FLOAT. This means that the position attribute of the first vertex in the array comes from the 0'th byte in the buffer, and OpenGL will pull 12 bytes (sizeof(float) * 3) to get that data. However, you need OpenGL to jump forward 24 bytes, the size of Vertex to get to the next position. You do this by setting the iStride to 24.

The color attribute would have a pBufferObjectOffset value of 12, since they start 3 floats into the struct. The size is 4 and the type is GL_UNSIGNED_BYTE (and it is normalized). The stride is also 24. Remember that stride is the number of bytes from one vertex value to the next. Each attribute has the same stride: 24.

A stride value of 0 has a special meaning: it means the attribute data is tightly packed. So a stride of zero always means that the stride is sizeof(eType) * iSize.

It is very possible, and usually desired for performance reasons, to use a single buffer object for all attributes of a mesh's data. Indeed, thanks to the buffer object offset, it is possible to put the data for many meshes in the same buffer object. You simply use glBufferSubData or glMapBufferRange to update different sections of it, and then set the offset when you are building your VAO.

Matrix Attributes

Attributes in GLSL can be of matrix types. However, our attribute binding functions only bind up to a dimensionality of 4. OpenGL solves this problem by converting matrix GLSL attributes into multiple attribute indices.

If you directly assign an attribute index to a matrix type, it implicitly takes up more than one attribute index. How many it takes depends on the height of the matrix: a 2x2 matrix will take 2, while a 2x4 matrix will take 4. The size of each attribute is the width of the matrix.

Each bound attribute in the VAO therefore fills in a single row, starting with the top-most and progressing down. Thus, if you have a 3x3 matrix, and you assign it to attribute index 3, it will naturally take attribute indices 3, 4, and 5. Each of these indices will be 3 elements in size. Attribute 3 is the top row, 4 is the middle, and 5 is the bottom.

If you let GLSL apply attributes manually, and query them with glGetAttribLocation, then OpenGL will allocate locations for matrix attributes contiguously as above. So if you defined a 3x3 matrix, it will return one value, but the next two values are also valid, active attributes.

Index Data

If you intend to use indices for your vertex arrays, you will need an index list. This is simply a buffer object that contains a list of integer values. It is best that this not be the same buffer object you use for attributes.

To attach this to the VAO, simply call glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObj); with your buffer object. Once attached, any indexed drawing calls will pull from this buffer. Attempting to use indexed drawing calls without a buffer bound to GL_ELEMENT_ARRAY_BUFFER is an error.

Finished VAO

At this point, your VAO is finished. You can render with it immediately, or you can bind another VAO and build a new one (or render with it). It's probably a good idea not to change a VAO once you've created it.

Fixed Attribute Values

It is legal to render with a vertex shader and VAO pair that do not match completely in terms of attributes. Any attributes the VAO specifies that the vertex shader does not consume are ignored. Whereas any attributes that the vertex shader needs but the VAO does not provide will be taken from a set of fixed attribute data.

This fixed attribute data is not an array. Therefore, each vertex in the stream will get the same value. The initial value for these is (0.0, 0.0, 0.0, 1.0).

To change the value, you use a function of this form:

void glVertexAttrib*(GLuint ''index'', Type ''values'');

The * is the type descriptor, using the traditional OpenGL syntax. There is also a version for setting integral attributes, glVertexAttribI*. The index is the attribute index to change.

Note that the fixed attribute values are not part of the VAO state. Changes to them do not affect the VAO.

Note: It is not recommended that you use these. The performance characteristics of using fixed attribute data are unknown, and it is not a high-priority case that OpenGL driver developers optimize for.


Once you have a vertex array object (and the appropriate program object used to draw with it, as well as other rendering state), rendering with it is quite easy.

Regardless of which function you use, you will be required to specify a primitive type. This will be how OpenGL interprets the vertex data for that object.

As previously mentioned, there are two ways to send a stream of vertices: indexed and unindexed. All functions that draw indexd have the form glDraw*Elements*, while functions that draw unindexed have the form glDraw*Arrays*.

Basic Drawing

The basic drawing functions are glDrawArrays and glDrawElements:

void glDrawArrays( GLenum ''mode'', GLint ''first'', GLsizei ''count'' );
void glDrawElements( GLenum ''mode'', GLsizei ''count'', GLenum ''type'', void *''indices'' );

The mode parameter is the primitive type. The first and count values in glDrawArrays define the range of elements to be pulled from the buffer.

The count and indices parameters of glDrawElements define the range of indices. Count defines how many indices to use, while <cide>indices defines the offset into the index buffer object (bound to GL_ELEMENT_ARRAY_BUFFER, stored in the VAO) to begin reading data. The type field descibes what the type of the indices are: GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT.


The basic drawing functions are all you really need in order to send vertices for rendering. However, there are a number of ways to draw that optimize certain rendering cases.

Rendering with a different VAO from the last rendering command is usually a relatively expensive operation. So many of the optimization mechanisms are based on you storing the data for several meshes in the same buffer objects with the same vertex formats and other VAO data.


Binding a VAO is often an expensive operation. And there are many cases where you want to render a number of distinct meshes with a single draw call. All of the meshes must be in the same VAO, as much all of the index arrays if you are doing indexed rendering. Also, of course, they must use the same shader program with the same uniform values.

To render multiple primitives from a VAO at once, use this:

void glMultiDrawArrays( GLenum ''mode'', GLint *''first'', GLsizei *''count'', GLsizei ''primcount'' );

This function is conceptually implemented as:

for (int i = 0; i < ''primcount''; i++)
  if (''count''[i] > 0)
    glDrawArrays(''mode'', ''first''[i], ''count''[i]);

Of course, you could write this function yourself. However, because it all happens in a single OpenGL call, the implementation has the opportunity to optimize this.

There is an indexed form as well:

void glMultiDrawElements( GLenum ''mode'', GLsizei *''count'', GLenum ''type'', void **''indices'', GLsizei ''primcount'' );

Similarly, this is implemented conceptually as:

for (int i = 0; i < ''primcount''; i++)
  if (''count''[i]) > 0)
    glDrawElements(''mode'', ''count''[i], ''type'', ''indices''[i]);

Multi-draw is useful for circumstances where you know that you are going to draw a lot of separate primitives of the same kind that all use the same shader. Typically, this would be a single conceptual object that you would always draw together in the same way.

Primitive Restart

Primitive restart functionality allows you to tell OpenGL that a particular index value means, not to source a vertex at that index, but to begin a new primitive of the same type with the next vertex. In essence, it is an alternative to glMultiDrawElements. This allows you to have an element buffer that contains multiple triangle strips or fans (or similar primitives where the start of a primitive has special behavior).

The way it works is with the function glPrimitiveRestartIndex. This function takes an index value. If this index is found in the index array, the system will start the primitive processing again as though a second rendering command had been issued. If you use a BaseVertex drawing function, this test is done before the base vertex is added to the restart. Using this feature also requires using glEnable(GL_PRIMITIVE_RESTART); to activate it, and the corresponding glDisable to turn it off.

Here is an example. Let's say you have an index array as follows:

{ 0 1 2 3 65535 2 3 4 5 }

If you render this as a triangle strip normally, you get 7 triangles. If you render it with glPrimitiveRestartIndex(65535) and the primitive restart enabled, then you will get 4 triangles: {0 1 2}, {1 2 3}, {2 3 4}, {3 4 5}.

Primitive restart works with any of the versions of these functions.

Warning: It is technically legal to use this with non-indexed rendering. You should not do this, as it will not give you a useful result.

Base Index

It is sometimes useful to render the same index buffer, the same effective topology, but with a set of vertex attribute data that is in a slightly different location from the base binding position. This is similar to the primitive restart or multi-draw method, only with stricter requirements.

The way it works is that you provide a base index. This is some index value. This index is added to the index loaded from the buffer to determine the effective index to pull vertex data from. You do this with:

void glDrawElementsBaseVertex( GLenum ''mode'', GLsizei ''count'', GLenum ''type'', void *''indices'', GLint ''basevertex'' );

This works as though the value of basevertex were added to each index in the index buffer. Note that this is different from changing indices, the offset into the index buffer.

There is no "Arrays" version, as this only works for indexed rendering. There is a "MultiDraw" version, as well as a "Range" version (see below).


It is often useful to be able to render multiple copies of the same mesh in different locations. If you're doing this with small numbers, like 5-20 or so, multiple draw commands with shader uniform changes between them (to tell which is in which location) is reasonably fast in performance. However, if you're doing this with large numbers of meshes, like 5,000+ or so, then it can be a performance problem.

Instancing is a way to get around this. The idea is that your vertex shader has some internal mechanism for deciding where each instance of the rendered mesh goes based on a single number. Perhaps it has a table (stored in a Texture Buffer) that it indexes with the instance number to get the per-instance data it needs. Or perhaps it has a simple algorithm for computing the location of an instance based on its number.

Regardless of the mechanism, it is based the shader getting an instance number that changes only when it is rendering a new instance. If you want to do instanced rendering, you call:

void glDrawArraysInstanced( GLenum ''mode'', GLint ''first'', GLsizei ''count'', GLsizei ''primcount'' );
void glDrawElementsInstanced( GLenum ''mode'', GLsizei ''count'', GLenum ''type'', const void *''indices'', GLsizei ''primcount'' );

It will send the same vertices <count>primcount number of times, as though you called glDrawArrays/Elements in a loop of primcount length. However, the vertex shader is given a special input value: gl_InstanceID. It will receive a value from 0 to primcount-1 based on which instance of the mesh is being rendered. This is the only mechanism the vertex shader has for differentiating between instances; it is up to the shader itself to decide how to use this information.

There is a "BaseVertex" version of this function, but not a "MultiDraw" version.


Implementations of OpenGL can often find it useful to know how much vertex data is being used in a buffer object. For non-indexed rendering, this is pretty easy to determine: the first and count parameters of the Arrays functions gives you appropriate information. For indexed rendering, this is more difficult, as the index buffer can use potentially any index up to its size.

Still, for optimization purposes, it is useful for implementations to know the range of indexed rendering data. Implementations may even read index data manually to determine this.

The "Range" series of glDrawElements commands allows the user to specify that this indexed rendering call will never cause indices outside of the given range of values to be sourced. The call works as follows:

void glDrawRangeElements( GLenum ''mode'', GLuint ''start'', GLuint ''end'', GLsizei ''count'', GLenum ''type'', void *''indices'' );

Unlike the "Arrays" functions, the start and end parameters specify the minimum and maximum index values that this draw call will use (rather than a first and count-style). If you try to violate this restriction, you will get implementation-behavior (ie: rendering may work fine, or you may get garbage).

There is one index that is allowed outside of the area bound by start and end: the primitive restart index. If primitive restart is set and enabled, it does not have to be within the given boundary.

Implementations may have a specific "sweet spot" for the range of indices, such that using indices within this range will have better performance. They expose such values with a pair of glGetIntegerv enumerators. To get the best performance, end - start should be less than or equal to GL_MAX_ELEMENTS_VERTICES, and count (the number of indices to be rendered) should be less than or equal to GL_MAX_ELEMENTS_INDICES.

This function has a "BaseVertex" version. In the case of that function, the boundary is for the indices before adding the base vertex.


See Also