Although not really a form of storage, uniforms are an important way to get data into shaders and to hook them up to your application. You have already seen how to pass data to a vertex shader using vertex attributes, and you have seen how to pass data from stage to stage using interface blocks. Uniforms allow you to pass data directly from your application into any shader stage. There are two flavors of uniforms, which differ based on how they are declared. The first are uniforms declared in the default block and the second are uniform blocks, whose values are stored in buffer objects. We will discuss both now.

uniform是一种给shader传递参数的重要方式。你已经知道如何给vertex shader传递数据以及shader之间如何传递数据了。uniform允许你直接将数据从应用程序发送参数给shader的任意阶段。 总共有两类的uniform,一类是default block,一类是uniform blocks。我们将讨论这两种存储方式。

Default Block Uniforms

While attributes are needed for per-vertex positions, surface normals, texture coordinates, and so on, a uniform is how we pass data into a shader that stays the same —is uniform—for an entire primitive batch or longer. Probably the single most common uniform for a vertex shader is the transformation matrix. We use transformation matrices in our vertex shaders to manipulate vertex positions and other vectors. Any shader variable can be specified as a uniform, and uniforms can be in any of the shader stages (even though we discuss only vertex and fragment shaders in this chapter). Making a uniform is as simple as placing the keyword uniform at the beginning of the variable declaration:

每个顶点都有位置、法线、纹理坐标等等这样的属性。而uniform是针对一次drawcall而言的,整个drawcall的所有阶段,这些uniform变量的值都使用一样的值。比如变换矩阵就是最常见的uniform变量。 任何shader的变量都可以用uniform来的方式来申明,任何shader阶段都可以使用uniform。申明uniform很简单,就是在变量前面用uniform去修饰:

uniform float fTime;
uniform int iIndex;
uniform vec4 vColorValue;
uniform mat4 mvpMatrix;
Uniforms are always considered to be; and they cannot be assigned values by your shader code. However, you can initialize their default values at declaration time in a manner such as this:


uniform int answer = 42;
If you declare the same uniform in multiple shader stages, each of those stages will “see” the same value of that uniform.


Arranging Your Uniforms(管理你的uniform变量)

After a shader has been compiled and linked into a program object, you can use one of the many functions defined by OpenGL to set the shader’s values (assuming you don’t want the defaults defined by the shader). Just as with vertex attributes, these functions refer to uniforms by their location within their program object. It is possible to specify the locations of uniforms in your shader code by using a location layout qualifier. When you do this, OpenGL will try to assign the locations that you specify to the uniforms in your shaders. The location layout qualifier looks like this:

当你创建好了一个GPU程序后,你可以使用OpenGL的API去设置uniform变量的值。如同属性一样,设置uniform的时候我们需要通过uniform变量的位置去设置他们。 你可以通过layout的修饰符在shader里去给uniform变量指定绑定的位置,当你使用了layout修饰符了之后,OpenGL会尝试去使用你指定的这些绑定位置:

layout (location = 17) uniform vec4 myUniform;
Notice the similarity between the location layout qualifer for uniforms and the one we’ve used for vertex shader inputs. In this case, myUniform will be allocated to location 17. If you don’t specify a location for your uniforms in your shader code, OpenGL will automatically assign locations to them for you. You can figure out which locations were assigned by calling the glGetUniformLocation() function, whose prototype is

我们注意到这种方式与属性差不多。上面的代码给myUniform这个变量指定了绑定位置为17.如果你不在shader中指定这些绑定的位置,那么OpenGL会自动给他们分配位置。 你可以使用glGetUniformLocation来获取到这些位置。

GLint glGetUniformLocation(GLuint program,const GLchar* name);
This function returns a signed integer that represents the location of the variable named by name in the program specified by program. For example, to get the location of a uniform variable named vColorValue, we would do something like this:


GLint iLocation = glGetUniformLocation(myProgram, "vColorValue");
In the previous example, passing "myUniform" to glGetUniformLocation() would result in the value 17 being returned. If you know a priori where your uniforms are because you assigned locations to them in your shaders, then you don’t need to find them and you can avoid the calls to glGetUniformLocation(). This is the recommended way of doing things.


If the return value of glGetUniformLocation() is −1, it means the uniform name could not be located in the program. You should bear in mind that even if a shader compiles correctly, a uniform name may still “disappear” from the program if it is not used directly in at least one of the attached shaders—even if you assign it a location explicitly in your shader source code. You do not need to worry about uniform variables being optimized away, but if you declare a uniform and then do not use it, the compiler will toss it out. Also, know that shader variable names are case sensitive, so you must get the case right when you query their locations

如果该API的返回结果是-1,那么就表示,无法找到这个uniform。需要注意的是即便你的shader语法没有错误,如果这个uniform的值不会被最后的的那个shader所直接或者间接的使用的话,那么 这个uniform会被优化掉,所以这个uniform就跟不存在一样。然后需要注意的是,shader变量是大小写敏感的,所以你必须正确的写出完整的名字的大小写。

Setting Uniforms(设置uniform变量)

OpenGL supports a large number of data types both in the shading language and in the API. To to allow you to pass all this data around, it includes a huge number of functions just for setting the value of uniforms. A single scalar or vector data type can be set with any of the following variations on the glUniform*() function. For example, consider the following four variables declared in a shader:


layout (location = 0) uniform float fTime;
layout (location = 1) uniform int iIndex;
layout (location = 2) uniform vec4 vColorValue;
layout (location = 3) uniform bool bSomeFlag;
To find and set these values in the shader, your C/C++ code might look something like this:


glUniform1f(0, 45.2f);
glUniform1i(1, 42);
glUniform4f(2, 1.0f, 0.0f, 0.0f, 1.0f);
glUniform1i(3, GL_FALSE);
Note that we used an integer version of glUniform() to pass in a bool value. Booleans can also be passed in as floats, with 0.0 representing false and any non-zero value representing true. The glUniform() function also comes in flavors that take a pointer, potentially to an array of values. These forms end in the letter v, indicating that they consume a vector, and take a count value that represents how many elements are in each array of x number of components, where x is the number at the end of the function name. For example, suppose you had this uniform with four components:

我们注意到,我们使用的是整型的glUniform相关的API给布尔类型传值的,同样你也可以使用浮点数类型,0.0表示false,非0表示true。glUniform系列的API也是分类的, 其中有一类就是传输数组的。这种API以v为结尾,表示传入的参数是个数组,并且还有一个数量的值来表示在每个数组里有x个元素,其中这个x一般在API名字的尾部。比如说,我们下面这个有4个元素的数组:

uniform vec4 vColor;
In C/C++, you could represent this as an array of floats:


GLfloat vColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
But this is a single array of four values, so passing it into the shader would look like this:

但这只是一个四维向量 ,所以我们给它赋值的使用使用如下C++代码:

glUniform4fv(iColorLocation, 1, vColor);
Now suppose you had an array of color values in your shader:


uniform vec4 vColors[2];
Then in C++, you could represent the data and pass it in like this:


GLfloat vColors[4][2] = { { 1.0f, 1.0f, 1.0f, 1.0f } ,
{ 1.0f, 0.0f, 0.0f, 1.0f } };
glUniform4fv(iColorLocation, 2, vColors);
At its simplest, you can set a single floating-point uniform like this:


GLfloat fValue = 45.2f;
glUniform1fv(iLocation, 1, &fValue);
Finally, we see how to set a matrix uniform. Shader matrix data types only come in the single- and double-precision floating-point variety, so we have far less variation. To set the values in uniform matrices, we call the glUniformMatrix() commands. In all of these functions, the variable count represents the number of matrices stored at the pointer parameter m (yes, you can have arrays of matrices!). The Boolean flag transpose is set to GL_FALSE if the matrix is already stored in column-major ordering (the way OpenGL prefers). Setting this value to GL_TRUE causes the matrix to be transposed when it is copied into the shader. This might be useful if you are using a matrix library that uses a row-major matrix layout instead (for example, some other graphics APIs use row-major ordering and you might want to use a library designed for one of them)

最后,我们看到如何去设置矩阵类型的uniform。shader的矩阵数据类型只有单精度和双精度这两种。设置矩阵,我们使用glUniformMatrix系列的API。在所有的这些函数中,都有一个count来表示有多少个矩阵存储 在m指针里。布尔型的参数告诉OpenGL是否需要转置,如果你是跟着我们OpenGL中的数学学完的人,你的矩阵就是不需要转置的。