Uniform (GLSL)

From OpenGL Wiki
Revision as of 00:52, 16 January 2010 by Alfonse (talk | contribs) (Initial Page.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

A uniform is a GLSL variable declared with the "uniform" storage qualifier. These act as parameters that the user of a shader program can pass to that program. They are stored in a program object.

Uniforms are so named because they do not change from one execution of a shader program to the next within a particular rendering call. This makes them unlike shader stage inputs and outputs, which are often different for each invocation of a program stage.

Active uniforms

GLSL compilers and linkers try to be as efficient as possible. Therefore, they do their best to eliminate code that does not affect the stage outputs. Because of this, a uniform defined in a shader file does not have to be made available in the linked program. It is only available if that uniform is used by code that affects the stage output, and that the uniform itself can change the output of the stage.

Therefore, a uniform that is exposed by a fully linked program is called an "active" uniform; any other uniform specified by the original shaders is inactive. Inactive uniforms cannot be used to do anything in a program.

Implementation limits

The number of active uniforms available is bound by a set of limits. Each shader stage has a limit on the number of available uniforms. The per-stage limits are the constants GL_MAX_VERTEX_UNIFORM_COMPONENTS, GL_MAX_GEOMETRY_UNIFORM_COMPONENTS, and GL_MAX_FRAGMENT_UNIFORM_COMPONENTS. You can query these limits with glGetIntegerv.

The limit refers to the quantity of space for individual floats, integers, or booleans. A vec3 is expected to take up 3 components. A mat2x4 should take up 8 components.

Implementation note: OpenGL implementations are allowed to reject shaders for implementation-dependent reasons. So you can have fewer active uniform components by your reckoning and still fail to link due to uniform limits. This is usually on hardware that is inately vector hardware. Pre GeForce 8xxx hardware, and all ATi hardware does this. In this case, you should assume that each separate uniform takes up 4 components, much like it would in D3D. That means a "uniform float" is 4 components, a mat2x4 is 16 components (each row is 4 components), but a mat4x2 is 8 components.

ATI/AMD note: The ATI max component values are wrong. They are the actual number of components divided by 4.

Do recall that these limits are for active uniforms only. Uniforms that are deemed inactive are not relevant to this limit.

Uniform management

Like regular OpenGL Objects, program objects encapsulate certain state. In this case, this state is a set of uniforms that will be used when rendering with that program. All of the stages within a program object use the same set of uniforms. Thus, if you have a uniform named "projectionMatrix" defined as a mat4 in both the vertex and the fragment stages, then there will be only one uniform of that name exposed by the program object. Changing this uniform will affect both the vertex and fragment stages.

Therefore, it is a linker error to have a uniform in a vertex and fragment shader that uses the same name, but has a different type in each shader.

Each active uniform has a location. The location is a numeric handle to that uniform; it functions as a shorthand that is faster to search for than the uniform's name. If you have a uniform name, you can get the uniform location via this function:

 GLint glGetUniformLocation(GLuint program, const char *name);

The program is the program object name that you want to find a uniform for. This must be a program that has successfully linked. And name is the name of the uniform you wish to search for. The return value is -1 if name isn't an active uniform in the program, name is the name of a uniform block (see below), or program isn't a successfully linked program. Otherwise, the return value is the uniform's location.

The name of the uniform does not have to be a simple name. If you want to use a struct of uniforms, each individual field in the struct has a separate location. For example, take this GLSL uniform definition:

struct MyStruct
  vec2 firstField;
  vec4 secondField;
  mat2 thirdField;

uniform MyStruct mainUniform;

The name "mainUniform" does not have a location; calling glGetUniformLocation on it will return -1. However, each of mainUniform's fields do have a location. So you can call "glGetUniformLocation(program, "mainUniform.secondField");" just fine.

Arrays are handled simply. Take this GLSL uniform definition:

uniform vec3 positions[20];

Each individual element in the uniform array has a location. The names of them are "positions[2]", "positions[3]", etc. However, the uniform location for "positions" and "positions[0]" are the same; they point to the first element of the array.

If you use the array uniform upload functions on an array location, then the referenced location and those following it will be updated.

Arrays of structures are special. Take this as an example:

uniform MyStruct manyStructs[3];

Struct uniforms, as mentioned above, do not have a location. Therefore, each entry in the array does not have a location; "manyStructs[0]" will return -1. Each struct field of each array element has its own uniform location. So you will need to set "manyStructs[0].firstField" and "manyStructs[1].firstField" separately.

The only thing you can use a uniform location for is to change the uniform's value. This is done with a large set of functions of the form glUniform*, where * defines the type of value to upload. These can upload matrices, vectors, individual values, and arrays of each of these.

However, in order to change a uniform value, you must first bind the program to the context with glUseProgram. The glUniform* functions do not take a program object name; they use the currently bound program.

Sampler uniforms

Uniforms of sampler types are used in GLSL to represent a texture of a particular kind. Therefore, sampler types represent textures. The way a program is associated with textures is somewhat unintuitive. The mapping is done with the rendering context.

Recall that the OpenGL rendering context has multiple independent bind locations for textures. The function to switch which bind location is current is glActiveTexture. The bind locations are named in this function GL_TEXTURE0, GL_TEXTURE1, etc. Alternatively, you can use bind locations of the form GL_TEXTURE0 + i, where i is a texture unit number between 0 and the implementation-defined constant GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.

This means you can bind 4 textures to texture units 0, 1, 3, and 8, so long as GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS for the implementation is greater than 8.

The value of a sampler uniform in a program is not a texture object, but a texture unit index. So you set the texture unit index for each sampler in a program. Then you bind the textures you wish to use to those texture units. Bind the program to the context, and rendering with that program will use those textures. To set the value of a sampler, use glUniformi or glUniformiv for an array of samplers.

Here is an example of how to do this.

//Initial program setup.
glLinkProgram(program); //Initial link

GLint baseImageLoc = glGetUniformLocation(program, "baseImage");
GLint normalMapLoc = glGetUniformLocation(program, "normalMap");
GLint shadowMapLoc = glGetUniformLocation(program, "shadowMap");

glUniformi(baseImageLoc, 0); //Texture unit 0 is for base images.
glUniformi(normalMapLoc, 2); //Texture unit 2 is for normal maps.
glUniformi(shadowMapLoc, 4); //Texture unit 4 is for shadow maps.

//When rendering an objectwith this program.
glActiveTexture(GL_TEXTURE0 + 0);
glActiveTexture(GL_TEXTURE0 + 2);
glActiveTexture(GL_TEXTURE0 + 4);



//Render another object with some different textures.
glActiveTexture(GL_TEXTURE0 + 0);
glActiveTexture(GL_TEXTURE0 + 2);

//Use the same shadow map.

Notice that the shadow map does not need to be changed here. This helps minimize the number of state changes. Your program could even have a global convention that texture image unit 4 is always used for shadow maps. Since all objects used the same shadow map, you can bind the shadow map before you start rendering anything, and you only have to bind it once per frame.

If a program doesn't use certain texture image units, it is still fine to have a texture bound to them. They will not affect the rendering in any way.

Uniform blocks and buffers

It is often useful to store a set of uniforms in storage that is separate from the program object. This can allow for fast switching between sets of uniforms, as well as having multiple programs share uniform data. This is done by using GLSL uniform blocks and creating Buffer Objects for storing that data. Collectively, this concept is called Uniform Buffer Objects.

Samplers cannot be part of uniform blocks.

Accessing uniform information

If you want to inspect uniforms to get more information out of them (types, array sizes, etc), there is an API to do so.

All uniforms, whether uniform block names or just uniform names, are assigned an index when the program is linked. This index is not the same as a uniform location or a uniform block index. You can query the indices for a list of uniform names with this function:

 void glGetUniformIndices(GLuint program, GLsizei uniformCount, const char ** uniformNames, GLuint *uniformIndices);

This will cause the array uniformIndices, which is of size uniformCount to be filled with the indices for the strings in the uniformNames list (also of size uniformCount); If a uniform name in the string list doesn't correspond to the name of an active uniform, then the corresponding index in uniformIndices will be GL_INVALID_INDEX.

The uniform indices are between 0 and GL_ACTIVE_UNIFORMS (an integer queried with glGetProgramiv). If you simply want to iterate over all of the available uniforms, you may simply query GL_ACTIVE_UNIFORM and loop over each uniform index on the half-open range [0, GL_ACTIVE_UNIFORMS).

Given the index, you can query the name of the uniform with this function:

 void glGetActiveUniformName( GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, char *uniformName);

This function stores the name in the uniformName buffer, which is of size bufSize bytes. The actual length of uniformName is stored in length (unless length is NULL, which is allowed).

General information about uniforms can be queried with this function, which can retrieve information for multiple uniforms at once:

 void glGetActiveUniformsiv( GLuint program, GLsizei uniformCount, GLconstuint *uniformIndices, GLenum pname, GLint *params );

The uniformIndices array is a list of uniform indices uniformCount in length. These are the uniforms that the user is asking to acquire. pname is an enum that determines what information to query for the uniforms. params are the values that get returned; it is an array uniformCount in length. One value for each uniform index is generated.

The possibilities for pname are:

  • GL_UNIFORM_TYPE: Retrieves the GLenum for the uniform's type.
  • GL_UNIFORM_SIZE: Retrieves the size of the uniform. For arrays, this is the length of the array*. For non-arrays, this is 1.
  • GL_UNIFORM_NAME_LENGTH: The length of that uniform's name.
  • GL_UNIFORM_BLOCK_INDEX: The uniform block index for this uniform. If this uniform is not in a block, the value will be -1.
  • GL_UNIFORM_OFFSET: The byte offset into the beginning of the uniform block for this uniform. If the uniform is not in a block, the value will be -1.
  • GL_UNIFORM_ARRAY_STRIDE: The byte stride for elements of the array, for uniforms in a uniform block. For non-array uniforms in a block, this value is 0. For uniforms not in a block, the value will be -1.
  • GL_UNIFORM_MATRIX_STRIDE: The byte stride for columns of a column-major matrix or rows for a row-major matrix, for uniforms in a uniform block. For non-matrix uniforms in a block, this value is 0. For uniforms not in a block, the value will be -1.
  • GL_UNIFORM_IS_ROW_MAJOR: GL_TRUE if the matrix is row-major and the uniform is in a block. If the uniform is not in a block, the uniform is column-major, or simply not a matrix type, GL_FALSE is returned.

*: To facilitate optimizations, OpenGL implementations are allowed to implicitly shrink arrays if they can determine, from the linked program code, that certain array indices cannot be reached. Thus, this value may be smaller than what the shaders originally stated.

An older function, superseded by glGetActiveUniformName and glGetActiveUniformsiv also exists.

 void glGetActiveUniform( GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, char *name );

This retrieves the name as in glGetActiveUniformName, but it also retrieves GL_UNIFORM_TYPE in type, GL_UNIFORM_SIZE in size, and GL_UNIFORM_NAME_LENGTH in length.

The length of the longest uniform name in a program can be queried with GL_UNIFORM_MAX_LENGTH through glGetProgramiv. You can use this along with GL_ACTIVE_UNIFORMS to allocate an array of strings that will be sufficiently large enough to hold the string names for a program's uniforms.