JavaScript EditorFree JavaScript Editor     Ajax Editor 



Main Page
Previous Page
Next Page

7.7. Specifying Vertex Attributes

One way you can pass vertex data to OpenGL is by calling glBegin, followed by some sequence of glColor/glNormal/glVertex/etc. A call to glEnd terminates this method of specifying vertex data.

These calls continue to work in the OpenGL programmable environment. As before, a call to glVertex indicates that the data for an individual vertex is complete and should be processed. However, if a valid vertex shader has been installed with glUseProgram, the vertex data is processed by that vertex shader instead of by the usual fixed functionality of OpenGL. A vertex shader can use the following built-in variables to access the standard types of vertex data passed to OpenGL:

attribute vec4 gl_Color;
attribute vec4 gl_SecondaryColor;
attribute vec3 gl_Normal;
attribute vec4 gl_Vertex;
attribute vec4 gl_MultiTexCoord0;
attribute vec4 gl_MultiTexCoord1;
attribute vec4 gl_MultiTexCoord2;
. . .
attribute vec4 gl_FogCoord;

OpenGL's vertex-at-a-time interface is simple and powerful, but on today's systems it is definitely not the highest-performance way of transferring vertex data to the graphics accelerator. Whenever possible, applications should use the vertex array interface instead. This interface allows you to store vertex data in arrays and set pointers to those arrays. Instead of sending one vertex at a time to OpenGL, you can send a whole set of primitives at a time. With vertex buffer objects, it is even possible that vertex arrays are stored in memory on the graphics board to exact maximum performance.

The vertex array interface also works the same way in the OpenGL programmable environment as it did previously. When a vertex array is sent to OpenGL, the vertex data in the vertex array is processed one vertex at a time, just like the vertex-at-a-time interface. If a vertex shader is active, each vertex is processed by the vertex shader rather than by the fixed functionality of OpenGL.

However, the brave new world of programmability means that applications no longer need to be limited to the standard attributes defined by OpenGL. There are many additional per-vertex attributes that applications might like to pass into a vertex shader. It is easy to imagine that applications will want to specify per-vertex data such as tangents, temperature, pressure, and who knows what else. How do we allow applications to pass nontraditional attributes to OpenGL and operate on them in vertex shaders?

The answer is that OpenGL provides a small number of generic locations for passing in vertex attributes. Each location is numbered and has room to store up to four floating-point components (i.e., it is a vec4). An implementation that supports 16 attribute locations will have them numbered from 0 to 15. An application can pass a vertex attribute into any of the generic numbered slots by using one of the following functions:

void glVertexAttrib{1|2|3|4}{s|f|d}(GLuint index, TYPE v)
void glVertexAttrib{1|2|3}{s|f|d}v(GLuint index, const TYPE *v)
void glVertexAttrib4{b|s|i|f|d|ub|us|ui}v(GLuint index, const TYPE *v)

Sets the generic vertex attribute specified by index to the value specified by v. This command can have up to three suffixes that differentiate variations of the parameters accepted. The first suffix can be 1, 2, 3, or 4 to specify whether v contains 1, 2, 3, or 4 components. If the second and third components are not provided, they are assumed to be 0, and if the fourth component is not provided, it is assumed to be 1. The second suffix indicates the data type of v and may specify byte (b), short (s), int (i), float (f), double (d), unsigned byte (ub), unsigned short (us), or unsigned int (ui). The third suffix is an optional v meaning that v is a pointer to an array of values of the specified data type.


This set of commands has a certain set of rules for converting data to the floating-point internal representation specified by OpenGL. Floats and doubles are mapped into OpenGL internal floating-point values as you would expect, and integer values are converted to floats by a decimal point added to the right of the value provided. Thus, a value of 27 for a byte, int, short, unsigned byte, unsigned int, or unsigned short becomes a value of 27.0 for computation within OpenGL.

Another set of entry points supports the passing of normalized values as generic vertex attributes:

void glVertexAttrib4Nub(GLuint index, TYPE v)
void glVertexAttrib4N{b|s|i|f|d|ub|us|ui}v(GLuint index, const TYPE *v)

Sets the generic vertex attribute specified by index to the normalized value specified by v. In addition to N (to indicate normalized values), this command can have two suffixes that differentiate variations of the parameters accepted. The first suffix indicates the data type of v and specifies byte (b), short (s), int (i), float (f), double (d), unsigned byte (ub), unsigned short (us), or unsigned int (ui). The second suffix is an optional v meaning that v is a pointer to an array of values of the specified data type.


N in a command name indicates that, for data types other than float or double, the arguments will be linearly mapped to a normalized range in the same way as data provided to the integer variants of glColor or glNormalthat is, for signed integer variants of the functions, the most positive, representable value maps to 1.0, and the most negative representable value maps to 1.0. For the unsigned integer variants, the largest representable value maps to 1.0, and the smallest representable value maps to 0.

Attribute variables are allowed to be of type mat2, mat3, or mat4. Attributes of these types can be loaded with the glVertexAttrib entry points. Matrices must be loaded into successive generic attribute slots in column major order, with one column of the matrix in each generic attribute slot. Thus, to load a mat4 attribute, you would load the first column in generic attribute slot i, the second in slot i + 1, the third in slot i + 2, and the fourth in slot i + 3.

With one exception, generic vertex attributes are just thatgeneric. They pass additional color values, tangents, binormals, depth values, or anything. The exception is that the generic vertex attribute with index 0 indicates the completion of a vertex just like a call to glVertex.

A glVertex2, glVertex3, or glVertex4 command is completely equivalent to the corresponding glVertexAttrib command with an index argument of 0. There are no current values for generic vertex attribute 0 (an error is generated if you attempt to query its current value). This is the only generic vertex attribute with this property; calls to set other standard vertex attributes can be freely mixed with calls to set any of the other generic vertex attributes. You are also free to mix calls to glVertex and glVertexAttrib with index 0.

The vertex array API has been similarly extended to allow generic vertex attributes to be specified as vertex arrays. The following call establishes the vertex array pointer for a generic vertex attribute:

void glVertexAttribPointer(GLuint index,
                                                 GLint size,
                                                 GLenum type,
                                                 GLboolean normalized,
                                                 GLsizei stride,
                                                 const GLvoid *pointer)

Specifies the location and data format of an array of generic vertex attribute values to use when rendering. The generic vertex attribute array to be specified is indicated by index. size specifies the number of components per attribute and must be 1, 2, 3, or 4. type specifies the data type of each component (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, or GL_DOUBLE). stride specifies the byte stride from one attribute to the next, allowing attribute values to be intermixed with other attribute values or stored in a separate array. A value of 0 for stride means that the values are stored sequentially in memory with no gaps between successive elements. If set to GL_TRUE, normalize specifies that values stored in an integer format are to be mapped to the range [1.0,1.0] (for signed values) or [0.0,1.0] (for unsigned values) when they are accessed and converted to floating point. Otherwise, values are converted to floats directly without normalization. pointer is the memory address of the first generic vertex attribute in the vertex array.


After the vertex array information has been specified for a generic vertex attribute array, the array needs to be enabled. When enabled, the generic vertex attribute data in the specified array is provided along with other enabled vertex array data when vertex array drawing commands such as glDrawArrays are called. To enable or disable a generic vertex attribute array, use the commands

void glEnableVertexAttribArray(GLuint index)
void glDisableVertexAttribArray(GLuint index)

Enables or disables the generic vertex attribute array specified by index. By default, all client-side capabilities are disabled, including all generic vertex attribute arrays. If enabled, the values in the generic vertex attribute array are accessed and used for rendering when calls are made to vertex array commands such as glArrayElement, glDrawArrays, glDrawElements, glDrawRangeElements, glMultiDrawArrays, or glMultiDrawElements.


This solves the question of how generic vertex data is passed into OpenGL, but how do we access that data from within a vertex shader? We don't want to refer to these numbered locations in our shader, because this approach is not very descriptive and is prone to errors. The OpenGL Shading Language API provides two ways for associating generic vertex indices with vertex shader attribute variables.

The first way is to let the linker assign the bindings automatically. In this case, the application would need to query OpenGL after linking to determine the generic vertex indices that were assigned and then would use these indices when passing the attributes to OpenGL.

The second way is for the application to choose the index value of the generic vertex attribute to be used and explicitly bind it to a specific attribute variable in the vertex shader by using the following function before linking occurs:

void glBindAttribLocation(GLuint program,
                                                GLuint index,
                                                const GLchar *name)

Associates a user-defined attribute variable in the program object specified by program with a generic vertex attribute index. The name of the user-defined attribute variable is passed as a null terminated string in name. If name was bound previously, that information is lost. Thus, you cannot bind one user-defined attribute variable to multiple indices, but you can bind multiple user-defined attribute variables to the same index. The generic vertex attribute index to be bound to this variable is specified by index. When program is made part of current state, values provided through the generic vertex attribute index modify the value of the user-defined attribute variable specified by name.

If name refers to a matrix attribute variable, index refers to the first column of the matrix. Other matrix columns are then automatically bound to locations index+1 for a matrix of type mat2; index+1 and index+2 for a matrix of type mat3; and index+1, index+2, and index+3 for a matrix of type mat4.

Applications are not allowed to bind any of the standard OpenGL vertex attributes with this command because they are bound automatically when needed. Any attribute binding that occurs after the program object has been linked does not take effect until the next time the program object is linked.


glBindAttribLocation can be called before any vertex shader objects are attached to the specified program object. It is also permissible to bind an attribute variable name that is never used in a vertex shader to a generic attribute index.

Applications are allowed to bind more than one vertex shader attribute name to the same generic vertex attribute index. This is called ATTRIBUTE ALIASING, and it is allowed only if just one of the aliased attributes is active in the executable program or if no path through the shader consumes more than one attribute of a set of attributes aliased to the same location. Another way of saying this is that more than one attribute name may be bound to a generic attribute index if, in the end, only one name is used to access the generic attribute in the vertex shader. The compiler and linker are allowed to assume that no aliasing is done and are free to employ optimizations that work only in the absence of aliasing. OpenGL implementations are not required to do error checking to detect attribute aliasing. Because there is no way to bind standard attributes, it is not possible to alias generic attributes with conventional ones.

The binding between an attribute variable name and a generic attribute index can be specified at any time with glBindAttribLocation. Attribute bindings do not go into effect until glLinkProgram is called, so any attribute variables that need to be bound explicitly for a particular use of a shader should be bound before the link operation occurs. After a program object has been linked successfully, the index values for attribute variables remain fixed (and their values can be queried) until the next link command occurs. To query the attribute binding for a named vertex shader attribute variable, use glGetAttribLocation. It returns the binding that actually went into effect the last time glLinkProgram was called for the specified program object. Attribute bindings that have been specified since the last link operation are not returned by glGetAttribLocation.

GLint glGetAttribLocation(GLuint program,
                                                 const GLchar *name)

Queries the previously linked program object specified by program for the attribute variable specified by name and returns the index of the generic vertex attribute that is bound to that attribute variable. If name is a matrix attribute variable, the index of the first column of the matrix is returned. If the named attribute variable is not an active attribute in the specified program object or if name starts with the reserved prefix "gl_", a value of 1 is returned.


Using these functions, we can create a vertex shader that contains a user-defined attribute variable named Opacity that is used directly in the lighting calculations. We can decide that we want to pass per-vertex opacity values in generic attribute location 1 and set up the proper binding with the following line of code:

glBindAttribLocation(myProgram, 1, "Opacity");

Subsequently, we can call glVertexAttrib to pass an opacity value at every vertex:

glVertexAttrib1f(1, opacity);

The glVertexAttrib calls are all designed for use between glBegin and glEnd. As such, they offer replacements for the standard OpenGL calls such as glColor, glNormal, and so on. But as we have already pointed out, vertex arrays should be used if graphics performance is a concern.

The jargon in this section can get a little confusing, so let's look at a diagram to make sure we have things straight. Figure 7.1 illustrates how commands to set standard vertex attributes modify the values of built-in attribute variables defined by the OpenGL Shading Language. The mappings between commands to set standard attributes (color, normal, vertex, etc.) and the built-in attribute variables (gl_Color, gl_Normal, gl_Vertex, etc.) are done automatically, and they are done in a way that doesn't conflict with the use of any generic attribute location that will be used. Each of these calls except glVertex also sets the current state for that attribute. (The value provided in a call to glVertex is never saved as part of current state.) The value for a built-in attribute variable is automatically updated when a call is made to set the value of the corresponding standard vertex attribute.

Figure 7.1. Mapping of standard vertex attribute commands to built-in attribute variables


Now let's look at the case of generic vertex attributes as illustrated in Figure 7.2. A user-defined attribute variable must be bound to a generic vertex attribute index. This binding can be done with glBindAttribLocation, or it can happen implicitly at link time.

Figure 7.2. Mapping of generic vertex attribute commands to user-defined attribute variables


Let's assume we have a vertex shader that uses three user-defined attribute variables: Opacity, Binormal, and MyData. These are shown on the right side of Figure 7.2. These user-defined attribute variables can each be bound to a generic vertex attribute index as follows:

glBindAttribLocation(myProgram, 1, "Opacity");
glBindAttribLocation(myProgram, 2, "Binormal");
glBindAttribLocation(myProgram, 3, "MyData");

This sets up the mapping so that values written into generic vertex attribute locations 1, 2, and 3 will modify the values of the attribute variables Opacity, Binormal, and MyData in the vertex shader. Generic vertex attribute 0 can be bound to a user-defined attribute variable, or its value can be obtained through the built-in attribute variable gl_Vertex. The diagram shows that generic vertex attribute index N is not currently bound to any user-defined attribute variable.

As mentioned, each of the generic attribute locations has enough room for four floating-point components. Applications are permitted to store 1, 2, 3, or 4 components in each location. A vertex shader may access a single location by using a user-defined attribute variable that is a float, a vec2, a vec3, or a vec4. It may access two consecutive locations by using a user-defined attribute variable that is a mat2, three using a mat3, and four using a mat4.

The bindings between generic attribute index values and user-defined attribute variables (i.e., the arrows on the right side of Figure 7.2) are part of the state maintained within a program object, whereas the contents of the attribute array itself is considered current attribute state (except for the generic vertex attribute with index 0). The application can provide a different program object and specify different names and mappings for attribute variables in the vertex shader, and if no calls have been made to update the attribute values in the interim, the attribute variables in the new vertex shader get the values left behind by the previous one.

Attribute variables that can be accessed when a vertex shader is executed are called ACTIVE ATTRIBUTES. To obtain information about an active attribute, use the following command:

void glGetActiveAttrib(GLuint program,
                                         GLuint index,
                                         GLsizei bufSize,
                                         GLsizei *length,
                                         GLint *size,
                                         GLenum *type,
                                         GLchar *name)

Returns information about an active attribute variable in the program object specified by program. The number of active attributes in a program object can be obtained by calling glGetProgram with the value GL_ACTIVE_ATTRIBUTES. A value of 0 for index causes information about the first active attribute variable to be returned. Permissible values for index range from 0 to the number of active attributes minus 1.

A vertex shader can use built-in attribute variables, user-defined attribute variables, or both. Built-in attribute variables have a prefix of "gl_" and reference conventional OpenGL vertex attributes (e.g., gl_Vertex, gl_Normal, etc.; see Section 4.1.1 for a complete list.) User-defined attribute variables have arbitrary names and obtain their values through numbered generic vertex attributes. An attribute variable (either built-in or user-defined) is considered active if it is determined during the link operation that it can be accessed during program execution. Therefore, program should have previously been the target of a call to glLinkProgram, but it is not necessary for it to have been linked successfully.

The size of the character buffer needed to store the longest attribute variable name in program can be obtained by calling glGetProgram with the value GL_ACTIVE_ATTRIBUTE_MAX_LENGTH. This value should be used to allocate a buffer of sufficient size to store the returned attribute name. The size of this character buffer is passed in bufSize, and a pointer to this character buffer is passed in name.

glGetActiveAttrib returns the name of the attribute variable indicated by index, storing it in the character buffer specified by name. The string returned is null terminated. The actual number of characters written into this buffer is returned in length, and this count does not include the null termination character. If the length of the returned string is not required, a value of NULL can be passed in the length argument.

The type argument returns a pointer to the attribute variable's data type. The symbolic constants GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4, GL_FLOAT_MAT2, GL_FLOAT_MAT3, and GL_FLOAT_MAT4 may be returned. The size argument returns the size of the attribute in units of the type returned in type.

The list of active attribute variables may include both built-in attribute variables (which begin with the prefix "gl_") as well as user-defined attribute variable names.

This function returns as much information as it can about the specified active attribute variable. If no information is available, length is 0 and name is an empty string. This situation could occur if this function is called after a link operation that failed. If an error occurs, the return values length, size, type, and name are unmodified.


The glGetActiveAttrib command can be useful in an environment in which shader development occurs separately from application development. If some attribute-naming conventions are agreed to between the shader writers and the application developers, the latter could query the program object at runtime to determine the attributes that are actually needed and could pass those down. This approach can provide more flexibility in the shader development process.

To query the state of a particular generic vertex attribute, call one of the following commands:

void glGetVertexAttribfv(GLuint index,
                                             GLenum pname,
                                             GLfloat *params)
void glGetVertexAttribiv(GLuint index,
                                             GLenum pname,
                                             GLint *params)
void glGetVertexAttribdv(GLuint index,
                                             GLenum pname,
                                             GLdouble *params)

Returns in params the value of a generic vertex attribute parameter. The generic vertex attribute to be queried is specified by index, and the parameter to be queried is specified by pname. Parameters and return values are summarized in Table 7.3. All the parameters except GL_CURRENT_VERTEX_ATTRIB represent client-side state.

Table 7.3. Generic vertex attribute parameters

Parameter

Operation

GL_VERTEX_ATTRIB_ARRAY_ENABLED

params returns a single value that is non-zero (true) if the vertex attribute array for index is enabled and 0 (false) if it is disabled. The initial value is GL_FALSE.

GL_VERTEX_ATTRIB_ARRAY_SIZE

params returns a single value, the size of the vertex attribute array for index. The size is the number of values for each element of the vertex attribute array, and it is 1, 2, 3, or 4. The initial value is 4.

GL_VERTEX_ATTRIB_ARRAY_STRIDE

params returns a single value, the array stride (number of bytes between successive elements) for the vertex attribute array for index. A value of 0 indicates that the array elements are stored sequentially in memory. The initial value is 0.

GL_VERTEX_ATTRIB_ARRAY_TYPE

params returns a single value, a symbolic constant indicating the array type for the vertex attribute array for index. Possible values are GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, and GL_DOUBLE. The initial value is GL_FLOAT.

GL_VERTEX_ATTRIB_ARRAY_NORMALIZED

params returns a single value that is nonzero (true) if fixed-point data types for the vertex attribute array indicated by index are normalized when they are converted to floating point and 0 (false) otherwise. The initial value is GL_FALSE.

GL_CURRENT_VERTEX_ATTRIB

params returns four values that represent the current value for the generic vertex attribute specified by index. Generic vertex attribute 0 is unique in that it has no current state, so an error is generated if index is 0. The initial value for all other generic vertex attributes is (0, 0, 0, 1).



void glGetVertexAttribPointerv(GLuint index,
                                                          GLenum pname,
                                                          GLvoid **pointer)

Returns pointer information. index is the generic vertex attribute to be queried, pname is a symbolic constant specifying the pointer to be returned, and params is a pointer to a location in which to place the returned data. The only accepted value for pname is GL_VERTEX_ATTRIB_ARRAY_POINTER. This causes params to return a single value that is a pointer to the vertex attribute array for the generic vertex attribute specified by index.



Previous Page
Next Page



R7
JavaScript EditorAjax Editor     JavaScript Editor