GLSL中Uniform块

Uniform块

当着色器程序变复杂时,用到的uniform变量数量也会上升。通常会在多个着色器程序中用到同一个uniform变量。由于uniform变量的位置是着色器链接的时候产生的(也就是glLinkProgram()的时候),因此它在应用程序中获得的索引可能会有变化,即使我们给uniform变量设置的值可能是完全相同的。而uniform缓存对象就是一种优化uniform变量访问,以及在不同的着色器程序之间共享uniform数据的方法。

访问一组uniform变量的方法是使用诸如glMapBuffer()的openGL函数,但是我们需要在着色器中对它们的声明方式略作修改。不再分别声明方式略作修改。不再分别声明每个uniform变量,而是直接将它们成组,形成一个类似架构提体的形式,也就是uniform块。一个uniform块要使用关键字uniform指定。然后及那个块中所有需要用到的变量包含在一堆花括号中。

例如:

uniform Matrices{
mat4 ModelView;
mat4 Projection;
mat4 Color;
};

着色器中的数据类型有两种:不透明的和透明的;其中不透明类型包括采样器、图像和原子计数器。一个uniform块中只可以包含透明类型的变量。此外,uniform块变量必须在全局作用域内声明。

uniform块中可以使用不同的限制符来设置变量的布局方式,这些限制符可以用来设置单个的uniform块,也可以用来设置所有后继uniform块的排列方式(需要使用布局声明)

uniform的布局限制符
布局限制符 描述
shared 设置uniform块是多个程序间共享的(这是默认的布局方式,与shared存储符不存在混淆)
packed 设置uniform共占用最小的内存空间,但是这样会禁止程序间共享这个块
std140 使用标准布局方式来设置uniform块或者着色器存储的buffer块
std430 使用标准布局方式来设置buffer块
row_major 使用行主序的方式来存储uniform块中的矩阵
column_major 使用列主序的方式来存储unifrom块中的矩阵

 

例如:如果需要共享一个uniform块,并且使用行主序的方式来存储数据,那么可以使用

layout(shared,row_major)uniform{...}

 

多个限制符可以通过圆括号中的逗号隔开。如果需要对所有后继的uniform块设置同一种布局,那么可以使用:

layout(packed,column_major)uniform;

注意:uniform块的名称并不能作为uniform变量的夫名称,因此在两个不同名称的uniform块中声明的同名变量会在编译时造成错误。

从应用程序中访问uniform块

uniform变量是着色器与应用程序之间共享数据的桥梁,因此如果着色器中的uniform变量是定义在命名uniform块中,那么就有必要找到不同变量的偏移值。如果获得了这个变量的具体位置,那么就可以使用数据对它们进行初始化,这一过程与处理缓存对象(使用glBufferData()等函数)是一致的。

假设应用程序的着色器中uniform块的名字已知。若要对uniform块中的uniform变量进行初始化,第一步就是找到块在着色器程序中的索引位置。可以使用glGenUniformBlockIndex()函数返回对应的信息,然后再应用程序的地质空间里完成uniform变量的映射。

GLuint glGetUnifromBlockIndex(GLuint progarm,const char* uniformBlockName);

返回program中名称为uniformBlockName的uniform块的索引值。如果uniformBlockName不是一个合法的uniform程序块,那么返回GL_INVALID_INDEX。

如果要初始化uniform块对应 的缓存对象,那么我们需要使用glBindBuffer()将缓存对象绑定到目标GL_UNIFORM_BUFFER之上。

当缓存对象进行初始化之后,需要判断命名的uniform块中的变量总供占据了多大的空间。可以使用glGetActiveUniformBlock()并且设置参数为GL_UNIFORM_BLOCK_DATA_SIZE,这样就可以返回编辑器分配的块的大小。GLActiveUniformBlockiv()函数还可以用来获取一个命名的uniform块的其他一些一些相关参数。

在获取unifrm块的索引之后,我们需要将一个缓存对象与这个块相关联。最常见的方法是调用glBindBufferRange(),或者如果uniform块是全部使用缓存来存储,那么可以使用glBindBufferBase()。

void glBindBufferRange(GLenum target,GLuint index,GLuint buffer,GLintptr offset,GLsizeiptr size);
void glBindBufferBase(GLenum target,GLuint index,GLuint buffer);

将缓存对象buffer与索引为index的命名uniform块关联起来。target可以是GL_UNIFORM_BUFFER(对于uniform块)或者GL_TRANSFORM_FEEDBACK_BUFFER用于transform feedback)。index是uniform块的索引。offset和size分别指定了uniform缓存映射的起始索引和大小。

调用glBindBufferBase()等价于调用glBindBufferRange()并设置offset为0,size为缓存对象的大小。

在下列情况下调用这两个函数可能会产生OpenGL错误GL_INVALID_VALUE:size小于0;offset+size大于缓存大小;offset或size不是4的倍数;index小于0或者大于等于GL_UNIFORM_BUFFER_BINDINGS的返回值。

当建立了命名uniform块和缓存对象之间的关联之后,只要使用缓存相关的命令即可对数据块进行初始化或者修改。

如果多个着色器程序需要共享同一个uniform块,为了避免对于不同的着色器程序同一块有不同的索引号,可以直接设置某个命名uniform块和缓存对象之间的绑定关系。如果需要显式地控制一个uniform块的绑定方式,可以在调用glLinkProgram()之前调用glUniformBlockBinding()函数。

GLuint glUniformBlockBinding(GLuint program,GLuint uniformBlockIndex,GLuint uniformBlockBinding);

显式地将块uniformBlockIndex绑定到uniformBlockBinding.

在一个命名为uniform块中,uniform变量的布局是通过各种布局限制符在编译和链接时控制的。如果使用了默认的布局方式,那么需要判断每个变量在uniform块中的偏移量和数据存储大小。glGetUniformIndices()负责获取指定名称uniform变量的索引位置。

void glGetUniformIndices(GLuint program,Glsizei uniformCount,const char** uniformNames,GLuint* uniformIndices);

返回所有uniformCount个变量的索引位置,变量的名称通过字符串数组uniformNames来指定。

你可能感兴趣的:(opengl学习)