周一到周五,每天一篇,北京时间早上7点准时更新~
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变量是不能在shader里进行赋值的,但是你任然可以在shader里在申明的它的时候给它一个缺省的值:
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.
如果你在不同的shader阶段使用同一个uniform变量,每个shader里使用的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:
第一个参数是GPU程序,第二个参数是uniform变量的名字。这个API返回的是一个有符号的整型,这个数字表示的就是变量的位置,下面是为了获取vColorValue这个uniform变量的位置的样本代码:
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.
在前面的那个例子中,myUniform的位置是17。如果你提前知道了你的这些uniform的位置,你就不用去调用该API。
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:
有很多的API可以用来设置uniform的值,它们都是以glUniform打头,后面接着的是数据类型。比如对于下面shader中的uniform变量:
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:
为了设置这些变量,你可以在C++代码中这么写:
glUseProgram(myShader);
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:
在C++里,你可以使用下面的代码表示一各四维数组
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:
现在我们假设我们有一组color的值:
uniform vec4 vColors[2];
Then in C++, you could represent the data and pass it in like this:
我们在C++里表达这样的值以及给shader变量赋值的办法如下:
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:
最简单的,你可以像下面代码这样给一个浮点数的uniform变量赋值:
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中的数学学完的人,你的矩阵就是不需要转置的。
在我们的教程中,我们使用的是这种uniform的操作模式,因为这种模式是可以做到全平台通用的。其次我们的同学们可以看到,这样的书不是给新手看的,是给学会了的人看的。你可以尝试回想一下我们的课程与这种书的区别。
本日的翻译就到这里,明天见,拜拜~~
第一时间获取最新桥段,请关注东汉书院以及图形之心公众号
东汉书院,等你来玩哦