Help end child hunger

GLSL Tutorial – Uniform Variables

 
Prev: Attribute Variables Next: Uniform Blocks
 

Uniform variables act as constants, at least for the duration of a draw call. The application feeds these variables to the graphics pipeline and they are accessible in all stages of the pipeline, i.e. any shader can access every uniform variable, as long as it declares the variable. These variables are read only, as far as shaders are concerned.

Uniforms can be grouped in blocks, or defined individually. In here we will start to look at the individual definitions, and the next section will look at uniform blocks.

Inside a shader, a uniform is defined using the uniform keyword. For instance to define a vec4 named myVar we could write in a shader

uniform vec4 myVar;

Uniforms can also be initialized inside the shader, for instance to initialize a vec4 we could proceed as follows:

uniform vec4 myVar = {0.5, 0.2, 0.7, 1.0};

In-shader-initialization is great since it relieves us from having to set uniforms. We can set a default value in the shader initialization, and only have to set a value from the application if we require a different value for the uniform variable.

Inside the application, to set a uniform variable, we must first get its location, which we can retrieve with the following function:
[stextbox]
GLint glGetUniformLocation(GLuint program, const char *name);

Params:

  • program: the handle to the linked program
  • name: the name of the uniform variable

Return: the location of the variable, or -1 if the name does not correspond to an active uniform variable.
[/stextbox]

An active uniform variable is a variable that it is actually used inside the shader, not just declared. The compiler is free to throw away variables that are not used in the code. Therefore, even if a uniform is declared in the shader, as long as it is not used, its reported location can be -1.

The return value, assuming that the program is linked and that the variable is effectively being used in the code, is the location of the variable, which later can be used to set its value(s). In order to set the values, OpenGL offers a large family of function, to cover for all data types, and several ways of setting the values. For instance, consider the variable myVar as defined above. To set the vec4, and assuming p as an handle to a linked program, we could write:

GLint myLoc = glGetUniformLocation(p, "myVar");
glProgramUniform4f(p, myLoc, 1.0f, 2.0f, 2.5f, 2.7f);

or, we could write

float myFloats[4] = {1.0f, 2.0f, 2.5f, 2.7f};
GLint myLoc = glGetUniformLocation(p, "myVar");
glProgramUniform4fv(p, myLoc, 1, myFloats);

The signature of these two functions is as follows:
[stextbox]
void glProgramUniform4f(GLuint program, GLint location, GLfloat f1, …, GLfloat f4);
void glProgramUniform4fv(GLuint program, GLint location, GLsizei count, const GLfloat *values);

Params:

  • program: the handle to the linked program
  • location: the uniform’s location in program p
  • f1…f4, values: the values to set the uniform
  • count: the number of items to set. For basic data types this will always be one. When considering arrays, then this value represents the number of elements in the array to be set.

[/stextbox]

There are similar functions for all basic data types in GLSL and for the predefined matrices.

Arrays

Consider now that in our shader we have an array type of variable, declared as follows:

uniform vec4 color[2];

From the application point of view, we can look at the variable as an array and set all the components at once. This is true for most basic types, not structs, see below.

float myColors[8] = {1.0f, 0.0f, 0.0f, 0.0f,     0.0f, 1.0f, 0.0f, 0.0f};
GLint myLoc = glGetUniformLocation(p, "color");
glProgramUniform4fv(p, myLoc, 2, myColors);

Notice that we set the third parameter of glProgramUniform4fv to 2, since we are setting two elements of the array, i.e. two vec4. The application array myColors has eight floats as required.

Another approach would be to set each element individually. For instance, assume that we only want to set the second element of the GLSL array, i.e. color[1]. Then we can write:

GLfloat aColor[4] = {0.0f, 1.0f, 1.0f, 0.0f};
myLoc = glGetUniformLocation(p, "color[1]");
glProgramUniform4fv(p, myLoc, 1, aColor);

There is a glProgramUniform function for each basic data type and they come up in two flavours as shown for the vec4 case:
[stextbox]
void glProgramUniform4f(GLuint program, GLint location, GLfloat v1, …, GLfloat v4);
void glProgramUniform4fv(GLuint program, GLint location, GLsizei count, GLfloat *v);

Params:

  • program: the linked program handle
  • location: the location of the variable
  • v1..v4: the values for each component of the vec4
  • count: the number of vec4 to set
  • v: a pointer to where the float data may be found

[/stextbox]

For matrices, there is a special version of this function, see below the example for a mat4:
[stextbox]
void glProgramUniformMatrix4fv(GLuint program, GLint location, GLsizei count, GLboolean transpose, GLfloat *v);

Params:

  • program: the linked program handle
  • location: the location of the variable
  • count: the number of vec4 to set
  • transpose: if true the matrix will be transposed prior to loading to the uniform
  • v: a pointer to where the float data may be found

[/stextbox]
Structs

In GLSL we can define structs, in a way similar to C. For instance, the following snippet of code declares a struct with two vec4s, and defines a uniform variable of that type.

struct Colors{
	vec4 Color1;
	vec4 Color2;
};

uniform Colors myColors;

To use the struct’s fields inside a shader we use the same notation as in C. For instance, the following main function assumes the declarations above:

void main()
{
	vec 4 aColor = myColors.Color1 + myColors.Color2;
	...
}

To set these variables in the application we also use the same notation as in C. We can’t set the struct as a whole, as it can’t get a location for it. We must set it field by field. The following example shows how:

float myFloats[8] = {0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0};

GLint myLoc = glGetUniformLocation(p, "myColors.Color1");
glProgramUniform4fv(p, myLoc, 1, myFloats);
myLoc = glGetUniformLocation(p, "myColors.Color2");
glProgramUniform4fv(p, myLoc, 1, &(myFloats[4]));

Working with arrays of structs is simple. Consider the following uniform declaration:

uniform Colors myColors[2];

In the shader we can write the following:

void main()
{
	vec 4 aColor = myColors[0].Color1 + myColors[1].Color2;
	...
}

Inside our application we proceed in the same manner:

GLint myLoc;
float myFloats[8] = {0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
float myOtherFloats[8[ = {1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0};

myLoc = glGetUniformLocation(p, "myColors[0].Color1");
glProgramUniform4fv(p, myLoc, 1, myFloats);
myLoc = glGetUniformLocation(p, "myColors[0].Color2");
glProgramUniform4fv(p, myLoc, 1, &(myFloats[4]));
myLoc = glGetUniformLocation(p, "myColors[1].Color1");
glProgramUniform4fv(p, myLoc, 1, myOtherFloats);
myLoc = glGetUniformLocation(p, "myColors[1].Color2");
glProgramUniform4fv(p, myLoc, 1, &(myOtherFloats[4]));

 

Prev: Attribute Variables Next: Uniform Blocks
 

  4 Responses to “GLSL Tutorial – Uniform Variables”

  1. Nice article – thanks

    Suggested typo fix:
    “…it reliefs us from having to set uniforms.”

    should be

    “…it relieves us from having to set uniforms.”

  2. void glGetProgramUniform4f(GLuint program, GLint location, GLfloat v1, …, GLfloat v4);
    I think the function should instead be “glProgramUniform4f”?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: