uniform对象及其使用

      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 的地址

   举个简单的例子,如下

uniform对象及其使用_第1张图片

   share layout 的定义如下,在std140基础上,可以在成员变量定义前面通过offset修饰符指定具体位置,align代表每一个成员变量都必须满足的对其倍数,这两个值共同决定成员变量最后的位置。

uniform对象及其使用_第2张图片

  前面提到,每一个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的基本使用方法,希望对大家有所帮助。

你可能感兴趣的:(OpenGL)