定义:OpenGL中接口块是用来对着色器中的变量进行统一管理。
声明:声明方式和C中的结构体的声明有点相像。声明伪代码如下:
限定符(可以为in,out,uniform或者buffer) 用户程序访问的块名称
{
块中变量列表
}着色器代码中访问的块名称;
布局方式:由于接口块对象并不会保存具体类型的数据,所以我们还需要告诉OpenGL内存的哪一部分对应着接口块中的哪一个成员变量。
常见的布局方式如下所示:
布局限制符 | 描述 |
---|---|
binding = N | 设置缓存的绑定位置,需要用到OpenGL API |
shared | 设置uniform块是多个程序间共享的,也是默认的布局方式 |
packed | 设置uniform块占用最小的内存空间,但是这样会禁止程序间共享这个uniform块 |
std140 | 使用标准布局方式来设置uniform块或者buffer块 |
std430 | 使用标准布局方式来设置uniform块 |
offset = N | 强制设置成员变量位于缓存的N字节偏移处 |
align= N | 强制设置成员变量的偏移位置是N的倍数 |
row_major | 使用行主序的方式来存储uniform块中的矩阵 |
column_major | 使用列主序的方式来存储uniform块中的矩阵 |
std140布局规则如下所示:
类型 | 布局规则 |
---|---|
标量,比如int和bool | 每个标量的基准对齐量为N。 |
向量 | 2N或者4N。这意味着vec3的基准对齐量为4N。 |
标量或向量的数组 | 每个元素的基准对齐量与vec4的相同。 |
矩阵 | 储存为列向量的数组,每个向量的基准对齐量与vec4的相同。 |
结构体 | 等于所有元素根据规则计算后的大小,但会填充到vec4大小的倍数。 |
std140布局例子如下所示:
layout (std140) uniform UserBlockName
{
// 基准对齐量 // 对齐偏移量
float value; // 4 // 0
vec3 vector; // 16 // 16 (必须是16的倍数,所以 4->16)
mat4 matrix; // 16 // 32 (列 0)
// 16 // 48 (列 1)
// 16 // 64 (列 2)
// 16 // 80 (列 3)
float values[3]; // 16 // 96 (values[0])
// 16 // 112 (values[1])
// 16 // 128 (values[2])
bool boolean; // 4 // 144
int integer; // 4 // 148
}ShaderBlockName;
uniform接口块对象:当不同着色器中存在多个uniform变量时,管理和维护这些uniform变量会显得十分麻烦。另外当不同着色器中用到了同一个uniform变量时,对应的索引值是不一样的,这样导致用户程序设置这个uniform变量显得十分的麻烦。所以uniform接口块对象可以优化uniform变量访问,以及在不同的着色器间共享uniform数据。
声明方式如下所示:
uniform UserBlockName
{
float value;
vec3 vector;
}ShaderBlockName;
常用接口如下所示:
GLuint glGetUniformBlockIndex(GLuint program, const GLchar *uniformBlockName)
:获取uniform接口块对象索引位置。
.program代表着色器程序对象标志。
.uniformBlockName代表uniform接口块的名称。
void glGetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params)
:获取指定uniform接口块对象索引的属性信息。
.program表示着色器程序标志。
.uniformBlockIndex表示uniform接口块对象索引。
.pname代表属性类型,如大小属性GL_UNIFORM_BLOCK_DATA_SIZE。
.params表示属性值。如大小值。
void glGetUniformIndices(GLuint program, GLsizei uniformCount, const char** uniformNames, GLuint* uniformIndices)
:获取指定名称uniform变量的索引位置。
.program表示着色器程序标志。
.uniformCount表示uniform变量个数。
.uniformNames表示uniform名称列表。
.uniformIndices表示uniform变量索引位置列表。
void glGetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params)
:获取指定uniform变量索引的属性信息。
.program表示着色器程序标志。
.uniformCount表示uniform变量个数。
.uniformIndices表示uniform变量索引位置列表。
.pname代表属性类型,如大小属性。
.params代表属性数值,如大小数值。
void glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
:将指定uniform接口块绑定到一个特定的绑定点上。
.program代表着色器程序对象标志。
.uniformBlockIndex代表接口块对象索引位置。
.uniformBlockBinding代表绑定点。
uniform缓冲对象:就是使用glGenBuffers创建缓冲对象,并使用glBindBuffer绑定到GL_UNIFORM_BUFFER上,然后通过glBufferData分配内存空间。
创建方式如下所示:
unsigned int uboExampleBlock;
glGenBuffers(1, &uboExampleBlock);
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
glBufferData(GL_UNIFORM_BUFFER, 152, NULL, GL_STATIC_DRAW); // 分配152字节的内存
glBindBuffer(GL_UNIFORM_BUFFER, 0);
常用接口如下所示:
void glBindBufferBase(GLenum target, GLuint index, GLuint buffer)
:将指定uniform缓冲对象绑定到一个特定的绑定点上。
.target代表目标类型,这里应该使用GL_UNIFORM_BUFFER。
.index代表绑定点数值。可以是绑定到一个独立的点,也可以是绑定到uniform接口块对象索引。
.buffer代表uniform缓冲对象索引值。
void glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)
:将指定uniform缓冲对象中指定区间内存绑定到一个特定的绑定点中。
.target代表目标类型,这里应该使用GL_UNIFORM_BUFFER。
.index代表绑定点数值。可以是绑定到一个独立的点,也可以是绑定到uniform接口块对象索引。
.buffer代表uniform缓冲对象索引值。
.offset代表uniform缓冲对象起始值。
.size代表需要绑定的内存大小。
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data)
:将指定数据更新到指定缓冲对象中指定位置处的内存上。
.target代表目标类型,这里应该使用GL_UNIFORM_BUFFER。
.offset代表缓冲对象中的偏移位置。
.size代表要更新数据在缓冲对象中占用的内存大小。
.data代表指定要更新的数据。
使用uniform缓冲:就是将uniform接口块对象和uniform缓冲对象通过绑定点进行关联。当更新uniform缓冲对象中的数据时,关联的uniform接口块对象数据也将同步的发生改变。
绑定关系如图所示:
具体使用流程如下:
1.绑定uniform接口块到绑定点上。
绑定代码如下所示:
// 使用相关接口进行分布操作来绑定
unsigned int lights_index = glGetUniformBlockIndex(shaderA.ID, "Lights");
glUniformBlockBinding(shaderA.ID, lights_index, 2);
或者使用声明来单独绑定,如下所示:
layout(std140, binding = 2) uniform Lights { ... };
2.绑定uniform缓冲对象到绑定点上。
绑定代码如下所示:
glBindBufferBase(GL_UNIFORM_BUFFER, 2, uboExampleBlock);
// 或
glBindBufferRange(GL_UNIFORM_BUFFER, 2, uboExampleBlock, 0, 152);
3.更新uniform缓冲对象数据,进而更新uniform接口块中的uniform变量数据。
更新代码如下所示:
// 使用GL_UNIFORM_BUFFER缓冲对象
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
// GLSL中的bool是4字节的,所以我们将它存为一个integer
int b = true;
// 向GL_UNIFORM_BUFFER代表的uniform缓冲对象中基准对齐量为4,偏移量为144处的成员赋值
glBufferSubData(GL_UNIFORM_BUFFER, 144, 4, &b);
// 卸载GL_UNIFORM_BUFFER缓冲对象
glBindBuffer(GL_UNIFORM_BUFFER, 0);
buffer接口块:就是着色器的存储缓存对象。
常见特性如下:
1.不仅可以被应用程序进行修改,而且还可以被着色器程序修改。
2.可以在渲染之前决定buffer接口块大小。
3.只能使用std430布局方式。
声明方式如下所示:
buffer UserBlockName
{
float value;
vec3 vector;
}ShaderBlockName;
in或out接口块:就是输入或输出接口块。通常一个着色器中的输出接口块作为另外一个着色器中的输入接口块。
声明方式如下所示:
in UserBlockName
{
float value;
vec3 vector;
}ShaderBlockName;
out UserBlockName
{
float value;
vec3 vector;
}ShaderBlockName;
in或out块位置和分量:在OpenGL4.4版本开始,支持在in和out块中使用layout(location = num)来指定成员变量位置;以及使用layout(component = num)来指定成员变量所在位置的分量。