uniform代表opengl中用到的全局数据,是客户端向opengl传送数据的重要方式,该类数据不属于某个具体的shader。每一个uniform数据都属于一个uniform block,而uniform block分为两类,第一类是default block,第二类是uniform block,第一类block是每个program对象默认分配的,每一个program对象都有唯一的default block,通过glUseProgram切换程序对象时,对应的default block也会相应变化。第二类uniform block则可以在各个程序间共享,不随着program对象的变化而变化,下面就介绍下两种block对象及其使用。
Default Block及其使用
首先介绍default block,其变量定义在shader中,通过客户端赋值,shader程序不能对uniform变了赋值,shader中的定义如下:
uniform float fTime;
uniform vec4 testColor;
如上定义,uniform变量的定义只需在一般变量定义的最前面加上uniform关键字即可,这些变量都会分配在default block中,default block中的每个uniform变量都有一个唯一的位置用于标识,shader程序不需要显示指定其位置,系统会自动为变量分配位置,如果有需要,使用者也可以显示指定位置,方法如下
layout (location = 15) uniform vec4 myUniform
对于系统自动分配的uniform变量,可以使用如下方法查询其位置,如下:
GLint glGetUniformLocation(GLuint program,const GLchar * name)
例如上面定义的变量,可通过GLint location = glGetuniformLocation(myProgram,"myUniform")查询其位置,返回值location为15。
接下来,需要为我们定义的uniform变量赋值,注意,shader程序不允许直接赋值,但可以在定义的时候赋予一个初始值,要想改变变量的值,需要通过glUniform*函数族,这类函数都需要知道uniform变量的位置,根据uniform数据类型的不同,函数名称也不同,一些常用的类型和使用方法如下
glUniform1f(0,45.2f) glUniform1i(1,42) glUniform4f(2,1.0f,1.0f,1.0f,1.0f) glUniform1i(3,GL_FALSE)
需要注意的是,bool型uniform变量和int型变量的赋值方法一样。上面各方法都是直接在参数中指定uniform变量需要的值,一来参数较多书写不方便,二来如果uniform变量是一个数组,参数无法指定,所以opengl又提供了以向量为参数的函数族,与上面的函数族相比,每一个函数后面都有一个“v”作为标识,如下:
GLfloat vColors[4][2] = {{1.0f,1.0f,1.0f,1.0f},{1.0f,0.0f,1.0f,1.0f}};
glUniform4fv(iColorLocation,2,vColors); //中间的参数代表数组元素个数
Uniform Block及其使用
接下来介绍uniform block,这种类型的block适合保存一些不随程序变化的数据,比如变换矩阵等等,定义如下
uniform TransformBlock
{
float scale;
vec3 translation;
mat4 projection_matrix;
}transform;
如上所示,定义和之前介绍的interface block非常相似,唯一的区别是关键字由in或out换成了uniform,TransformBlock是uniform block的名字,transform是实例名称,有点类似c++中的类和对象的关系。每一个uniform block都有一个唯一的block index,block中的每一个成员变量都有一个唯一的index,这个index是跨block的,也就是说,系统是把所有的uniform block中的所有成员变量统一布局和划分index,通过idnex可以查询成员变量的大小,位置,类型,名字等各种详细信息。
uniform block都是用户自定义的结构体,opengl没有函数可以直接进行赋值,唯一的方法是绑定一个自定义的vbo对象,用户自己按照uniform block的实际布局在vbo对象中相应的位置对各个分量进行赋值,shader则直接从vbo对象中获得正确的值。这就涉及到结构体的布局问题,也就是常说的aligment对其方式,常用的有standard layout和share layout两种布局方法。stdlayout是一种约定好的标准,只要按照这个标准进行声明,在对应的vbo对象中也按照这个标准进行布局和赋值即可。share layout是在满足standard layout 的基础上,用户可以显示指定更为详细的布局方法。
标准布局的声明如下:
layout (std140) uniform TransformBlock
{
float scale;
... ...//
}
std140的具体规则是:
1 float bool int等基本类型必须放在4N的地址 ,如0,4,8等
2 vec2,ivec2,dvec2等两维向量必须放在8N的地址,如0,8,16等,vec3,vec4等高阶向量必须放在16N的地址
3 数组中的每一个成员变量都必须放在16N的地址,矩阵或者矩阵数组都可以认为是由相应列向量组成的数组
4 结构体成员变量必须放在16N 的地址
举个简单的例子,如下
share layout 的定义如下,在std140基础上,可以在成员变量定义前面通过offset修饰符指定具体位置,align代表每一个成员变量都必须满足的对其倍数,这两个值共同决定成员变量最后的位置。
前面提到,每一个uniform block的成员变量都有一个全局index,获取index方法如下:
void glGetUniformIndices(GUuint program,GLsizei uniformCount, const GLchar ** uniformNmaes,GUuint *uniformIndices);
使用方法如下:
static const GLchar * uniformNames[2] =
{
"TransformBlock.scale",
"TransformBlock.translation"
}
GLuint uniformIndices[2];
glGetUniformIndices(programe,2,uniformNames,uniformIndices);
有了成员变量的index,我们可以通过void glGetActiveUniformsiv(GUuint program, GLsizei uniformCount,const GUuint *uniformIndices, Glenum pname,GLint *params)来查询成员变量的详细信息,比较重要的是pname参数,这个参数代表要查询的属性,可取值范围如下:
GL_UNIFORM_TYPE //数据类型
GL_UNIFORM_SIZE //数组长度,非数组类型返回1
GL_UNIFORM_NAME_LENGTH//名字长度
GL_UNIFORM_BLOCK_INDEX //block index
GL_UNIFORM_OFFSET //成员变量在uniform block中的偏移量
GL_UNIFORM_ARRAY_STRIDE //数组中两个连续成员的偏移量,非数组成员返回0
GL_UNIFORM_MATRIX_STRIDE //连续两个行向量或列向量的偏移量,非矩阵成员变量返回0
GL_UNIFORM_IS_ROW_MAJOR //矩阵成员变量并且是行向量形式存储则返回1,其他返回0
该函数对于shader源码不可见的情况有较大用处,在对uniform block进行赋值的时候,通过查询可得知其内部布局,从而对vbo对象进行赋值操作,然后将vbo对象和uniform block进行绑定即可完成赋值操作,绑定相关方法如下:
1 通过GLuint glGetUniformBlockIndex(GUuint program ,const GLchar *uniformBlockName)获取block index;
2 通过void glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex,Gluint uniformBlockBinding)绑定block和uniform buffer的binding point
3 通过glBindBufferBase(GL_UNIFORM_BUFFER,index,buffer)将特定buffer 绑定在unifrom buffer对应的binding point
以上步骤即可完成对unifromBlock的赋值操作,但有一种更为简单的绑定方法,即在shader中指定绑定即可,如下:
layout (std140,binding = 2)uniform TransformBlock
{
....
} transfrom;
以上就是uniform block的基本使用方法,希望对大家有所帮助。