7.8. Specifying Uniform Variables
As described in the previous section, attribute variables provide frequently modified data to the vertex shader. Less frequently changing data can be specified using uniform variables. Uniform variables are declared within a shader and can be loaded directly by the application. This lets applications provide any type of arbitrary data to a shader. Applications can modify these values as often as every primitive in order to modify the behavior of the shader (although performance may suffer if this is done). Typically, uniform variables are used to supply state that stays constant for many primitives.
The OpenGL Shading Language also defines a number of built-in variables that track OpenGL state. Applications can continue using OpenGL to manage state through existing OpenGL calls and can use these built-in uniform variables in custom shaders. Of course, if you want something that isn't already supported directly by OpenGL, it is a simple matter to define your own uniform variable and supply the value to your shader.
When a program object is made current, built-in uniform variables that track OpenGL state are initialized to the current value of that OpenGL state. Subsequent calls that modify an OpenGL state value cause the built-in uniform variable that tracks that state value to be updated as well.
The basic model for specifying uniform variables is different from the model for specifying attribute variables. As discussed in the preceding section, for attribute variables, the application can specify the attribute location before linking occurs. In contrast, the locations of uniform variables cannot be specified by the application. Instead, they are always determined by OpenGL at link time. As a result, applications always need to query the uniform location after linking occurs.
To update the value of a user-defined uniform variable, an application needs to determine its location and then specify its value. The locations of uniform variables are assigned at link time and do not change until the next link operation occurs. Each time linking occurs, the locations of uniform variables may change, and so the application must query them again before setting them. The locations of the user-defined uniform variables in a program object can be queried with the following command:
GLint glGetUniformLocation(GLuint program, const GLchar *name)
| Returns an integer that represents the location of a specific uniform variable within a program object. name must be a null terminated string that contains no white space. name must be an active uniform variable name in program that is not a structure, an array of structures, or a subcomponent of a vector or a matrix. This function returns 1 if name does not correspond to an active uniform variable in program or if name starts with the reserved prefix "gl_".
Uniform variables that are structures or arrays of structures can be queried with glGetUniformLocation for each field within the structure. The array element operator "[]" and the structure field operator "." can be used in name to select elements within an array or fields within a structure. The result of using these operators is not allowed to be another structure, an array of structures, or a subcomponent of a vector or a matrix. Except if the last part of name indicates a uniform variable array, the location of the first element of an array can be retrieved with the name of the array or with the name appended by "[0]".
The actual locations assigned to uniform variables are not known until the program object is linked successfully. After linking has occurred, the command glGetUniformLocation can obtain the location of a uniform variable. This location value can then be passed to glUniform to set the value of the uniform variable or to glGetUniform in order to query the current value of the uniform variable. After a program object has been linked successfully, the index values for uniform variables remain fixed until the next link command occurs. Uniform variable locations and values can only be queried after a link if the link was successful. |
Loading of user-defined uniform values is only possible for the program object that is currently in use. All user-defined uniform variables are initialized to 0 when a program object is successfully linked. User-defined uniform values are part of the state of a program object. Their values can be modified only when the program object is part of current rendering state, but the values of uniform variables are preserved as the program object is swapped in and out of current state. The following commands load uniform variables into the program object that is currently in use:
void glUniform{1|2|3|4}{f|i}(GLint location, TYPE v) | Sets the user-defined uniform variable or uniform variable array specified by location to the value specified by v. The suffix 1, 2, 3, or 4 indicates whether v contains 1, 2, 3, or 4 components. This value should match the number of components in the data type of the specified uniform variable (e.g., 1 for float, int, bool; 2 for vec2, ivec2, bvec2, etc.). The suffix f indicates that floating-point values are being passed, and the suffix i indicates that integer values are being passed; this type should also match the data type of the specified uniform variable. The i variants of this function should be used to provide values for uniform variables defined as int, ivec2, ivec3, and ivec4, or arrays of these. The f variants should be used to provide values for uniform variables of type float, vec2, vec3, or vec4, or arrays of these. Either the i or the f variants can be used to provide values for uniform variables of type bool, bvec2, bvec3, and bvec4 or arrays of these. The uniform variable is set to false if the input value is 0 or 0.0f, and it is set to true otherwise. |
void glUniform{1|2|3|4}{f|i}v(GLint location, GLuint count, const TYPE v) | Sets the user-defined uniform variable or uniform variable array specified by location to the values specified by v. These commands pass a count and a pointer to the values to be loaded into a uniform variable or a uniform variable array. A count of 1 should be used for modifying the value of a single uniform variable, and a count of 1 or greater can be used to modify an array. The number specified in the name of the command specifies the number of components for each element in v, and it should match the number of components in the data type of the specified uniform variable (e.g., 1 for float, int, bool; 2 for vec2, ivec2, bvec2, etc.). The v in the command name indicates that a pointer to a vector of values is being passed. The f and i suffixes are defined in the same way as for the nonvector variants of glUniform.
For uniform variable arrays, each element of the array is considered to be of the type indicated in the name of the command (e.g., glUniform3f or glUniform3fv can be used to load a uniform variable array of type vec3). The number of elements of the uniform variable array to be modified is specified by count. |
void glUniformMatrix{2|3|4}fv(GLint location, GLuint count, GLboolean transpose, const GLfloat *v) | Sets the user-defined uniform matrix variable or uniform matrix array variable specified by location to the values specified by v. The number in the command name is interpreted as the dimensionality of the matrix. The number 2 indicates a 2 x 2 matrix (i.e., 4 values), the number 3 indicates a 3 x 3 matrix (i.e., 9 values), and the number 4 indicates a 4 x 4 matrix (i.e., 16 values). If transpose is GL_FALSE, each matrix is assumed to be supplied in column major order. If transpose is GL_TRUE, each matrix is assumed to be supplied in row major order. The count argument specifies the number of matrices to be passed. A count of 1 should be used for modifying the value of a single matrix, and a count greater than 1 can be used to modify an array of matrices. |
glUniform1i and glUniform1iv are the only two functions that can be used to load uniform variables defined as sampler types (see Section 7.9). Attempting to load a sampler with any other function results in an error.
Errors can also be generated by glUniform for any of the following reasons:
If there is no current program object If location is an invalid uniform variable location for the current program object If the number of values specified by count would exceed the declared extent of the indicated uniform variable or uniform variable array Other than the preceding exceptions noted, if the type and size of the uniform variable as defined in the shader do not match the type and size specified in the name of the command used to load its value
In all of these cases, the indicated uniform variable will not be modified.
When the location of a user-defined uniform variable has been determined, the following command can be used to query its current value:
void glGetUniformfv(GLuint program, GLint location, GLfloat *params) void glGetUniformiv(GLuint program, GLint location, GLint *params) | Return in params the value(s) of the specified uniform variable. The type of the uniform variable specified by location determines the number of values returned. If the uniform variable is defined in the shader as a bool, int, or float, a single value is returned. If it is defined as a vec2, ivec2, or bvec2, two values are returned. If it is defined as a vec3, ivec3, or bvec3, three values are returned, and so on. To query values stored in uniform variables declared as arrays, call glGetUniform for each element of the array. To query values stored in uniform variables declared as structures, call glGetUniform for each field in the structure. The values for uniform variables declared as a matrix are returned in column major order.
The locations assigned to uniform variables are not known until the program object is linked. After linking has occurred, the command glGetUniformLocation can obtain the location of a uniform variable. This location value can then be passed to glGetUniform to query the current value of the uniform variable. After a program object has been linked successfully, the index values for uniform variables remain fixed until the next link command occurs. The uniform variable values can only be queried after a link if the link was successful. |
The location of a uniform variable cannot be used for anything other than specifying or querying that particular uniform variable. Say you declare a uniform variable as a structure that has three fields in succession that are defined as floats. If you call glGetUniformLocation to determine that the first of those three floats is at location n, do not assume that the next one is at location n + 1. It is possible to query the location of the ith element in an array. That value can then be passed to glUniform to load one or more values into the array, starting at the ith element of the array. It is not possible to take i and add an integer N and use the result to try to modify element i + N in the array. The location of array element i + N should be queried specifically before any attempt to set its value. These location values do not necessarily represent real memory locations. Applications that assume otherwise will not work.
For example, consider the following structure defined within a shader:
uniform struct
{
struct
{
float a;
float b[10];
} c[2];
vec2 d;
} e;
and consider the API calls that attempt to determine locations within that structure:
loc1 = glGetUniformLocation(progObj, "e.d"); // is valid
loc2 = glGetUniformLocation(progObj, "e.c[0]"); // is not valid
loc3 = glGetUniformLocation(progObj, "e.c[0].b") ; // is valid
loc4 = glGetUniformLocation(progObj, "e.c[0].b[2]"); // is valid
The location loc2 cannot be retrieved because e.c[0] references a structure.
Now consider the commands to set parts of the uniform variable:
glUniform2f(loc1, 1.0f, 2.0f); // is valid
glUniform2i(loc1, 1, 2); // is not valid
glUniform1f(loc1, 1.0f); // is not valid
glUniform1fv(loc3, 10, floatPtr); // is valid
glUniform1fv(loc4, 10, floatPtr); // is not valid
glUniform1fv(loc4, 8, floatPtr); // is valid
The second command in the preceding list is invalid because loc1 references a uniform variable of type vec2, not ivec2. The third command is invalid because loc1 references a vec2, not a float. The fifth command in the preceding list is invalid because it attempts to set values that will exceed the length of the array.
Uniform variables (either built in or user defined) that can be accessed when a shader is executed are called ACTIVE UNIFORMS. You can think of this as though the process of compiling and linking is capable of deactivating uniform variables that are declared but never used. This provides more flexibility in coding stylemodular code can define lots of uniform variables, and those that can be determined to be unused are typically optimized away.
To obtain the list of active uniform variables from a program object, use glGetActiveUniform. This command can be used by an application to query the uniform variables in a program object and set up user interface elements to allow direct manipulation of all the user-defined uniform values.
void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) | Returns information about an active uniform variable in the program object specified by program. The number of active uniform variables can be obtained by calling glGetProgram with the value GL_ACTIVE_UNIFORMS. A value of 0 for index selects the first active uniform variable. Permissible values for index range from 0 to the number of active uniform variables minus 1.
Shaders may use built-in uniform variables, user-defined uniform variables, or both. Built-in uniform variables have a prefix of "gl_" and reference existing OpenGL state or values derived from such state (e.g., gl_Fog, gl_ModelViewMatrix, etc., see Section 4.3 for a complete list.) User-defined uniform variables have arbitrary names and obtain their values from the application through calls to glUniform. A uniform 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 required to store the longest uniform variable name in program can be obtained by calling glGetProgram with the value GL_ACTIVE_UNIFORM_MAX_LENGTH. This value should be used to allocate a buffer of sufficient size to store the returned uniform variable name. The size of this character buffer is passed in bufSize, and a pointer to this character buffer is passed in name.
glGetActiveUniform returns the name of the uniform 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 uniform variable's data type. One of the following symbolic constants may be returned: GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4, GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4, GL_BOOL, GL_BOOL_VEC2, GL_BOOL_VEC3, GL_BOOL_VEC4, GL_FLOAT_MAT2, GL_FLOAT_MAT3, GL_FLOAT_MAT4, GL_SAMPLER_1D, GL_SAMPLER_2D, GL_SAMPLER_3D, GL_SAMPLER_CUBE, GL_SAMPLER_1D_SHADOW, or GL_SAMPLER_2D_SHADOW.
If one or more elements of an array are active, the name of the array is returned in name, the type is returned in type, and the size parameter returns the highest array element index used, plus one, as determined by the compiler and linker. Only one active uniform variable will be reported for a uniform array.
Uniform variables that are declared as structures or arrays of structures are not returned directly by this function. Instead, each of these uniform variables is reduced to its fundamental components containing the "." and "[]" operators such that each of the names is valid as an argument to glGetUniformLocation. Each of these reduced uniform variables is counted as one active uniform variable and is assigned an index. A valid name cannot be a structure, an array of structures, or a subcomponent of a vector or matrix.
The size of the uniform variable is returned in size. Uniform variables other than arrays have a size of 1. Structures and arrays of structures are reduced as described earlier, such that each of the names returned will be a data type in the earlier list. If this reduction results in an array, the size returned is as described for uniform arrays; otherwise, the size returned is 1.
The list of active uniform variables may include both built-in uniform variables (which begin with the prefix "gl_") as well as user-defined uniform variable names.
This function returns as much information as it can about the specified active uniform 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. |
Using glGetActiveUniform, the application developer can programmatically query the uniform variables actually used in a shader and automatically create a user interface that allows the end user to modify those uniform variables. If among the shader writers there were some convention concerning the names of uniform variables, the user interface could be even more specific. For instance, any uniform variable name that ended with "Color" would be edited with the color selection tool. This function can also be useful when mixing and matching a set of vertex and fragment shaders designed to play well with each other, using a subset of known uniform variables. It can be much safer and less tedious to programmatically determine which uniform variables to send down than to hardcode all the combinations.
|