1. uniform 块
着色器与应用程序之间,或者着色器各阶段之间共享的变量可以组织为变量块的形式,并且有的时候必须采用这种形式。
1 | uniform b { // 限定符可以为 uniform、in、out 或者 buffer |
通常会在多个着色器程序中用到同一个 uniform 变量。
由于 uniform 变量的位置是着色器链接的时候产生的(也就是调用 glLinkProgram()
的时候),因此它在应用程序中获得的索引可能会有变化,即使我们给 uniform 变量设置的值可能是完全相同的。
uniform变量是同时存在于用户应用程序和着色器当中的,因此需要同时修改着色器的内容并调用OpenGL函数来设置uniform缓存对象。
1.1 指定着色器中的 uniform 块
1 | uniform Matrices { |
Note :
- 着色器中的数据类型有两种:不透明的和透明的;其中不透明类型包括采样器、图像和原子计数器;
- uniform 块中只可以包含透明类型的变量;
- uniform 块必须在全局作用域内声明。
1.2 uniform 块的布局控制
1.2.1 uniform 的布局限制符
布局限制符 | 描 述 |
---|---|
binding = N | 设置缓存的绑定位置,需要用到 OpenGL API |
shared | 设置 uniform 块是多个程序间共享的(这是默认的布局方式,与 shared 存储限制符不存在 |
混淆) | |
packed | 设置 uniform 块占用最小的内存空间,但是这样会禁止程序间共享这个块 |
stdl40 | 使用标准布局方式来设置 uniform 块或者着色器存储的 buffer 块, 参见附录H |
std430 | 使用标准布局方式来设置 uniform 块,参见附录I |
offset = N | 强制设置成员变量位于缓存的N字节偏移处 |
align = N | 强制设置成员变址的偏移位置是N的倍数 |
row_major | 使用行主序的方式来存储 uniform 块中的矩阵 |
column_major | 使用列主序的方式来存储 uniform 块中的矩阵(这也是默认的顺序) |
1.2.2 布局声明
Example 01 :需要共享一个uniform块,并且使用行主序的方式来存储数据
1 | layout (shared, row_major) uniform { |
- 多个限制符可以通过圆括号中的逗号来分隔。
Example 02 :需要对所有后继的uniform块设置同一种布局
1 | layout (packed, column_major) uniform; |
- 当前行之后的所有 uniform 块都会使用这种布局方式,除非再次改变全局的布局,或者对某个块的声明单独设置专属的布局方式。
1.2.3 缓存布局
如果你在着色器和应用程序之间共享了一块缓存,那么这两者都需要确认成员变量所处的内存偏移地址。
此时可以通过 offset
限制符来控制成员的精确位置,或者用 align
限制符来设置一个模糊的对齐方式。
你可以只对某些成员应用这些限制符,从而确保应用程序和着色器之间的布局是同步的。
1 |
|
Note :
- 对齐过程是自然的,只不过 stdl40 需要对类似 vec4 这样的类型增加一个额外的 16 字节对齐的限制;
- vec4 的基本对齐方式是 4 * 其基本类型的对齐方式(即:float),因此 vec4 的基本对齐方式为 16
1.3 从应用程序中访问 uniform 块
1.3.1 uniform 块的声明
- 在顶点着色器和片元着色器中添加固定的 uniform 块作为缓冲区
顶点着色器和片元着色器共享同一个名称为 “Uniforms” 的 uniform 块
顶点着色器 vShader
1 |
|
片元着色器 fShader
1 |
|
1.3.2 获取存储大小
用于将 GLSL 类型转换为存储大小的辅助函数
1 | size_t |
1.3.3 uniform 块的初始化
1 | void |
2. buffer 块
buffer 块与 uniform 块的区别 :
- 着色器可以写入buffer块,修改其中的内容并呈现给其他的着色器调用或者应用程序本身;
- 可以在渲染之前再决定它的大小,而不是编译和链接的时候。
1 | buffer Bufferobject { // 创建一个可读写的 buffer 块 |
如果在着色器中没有给出上面的数组的大小,那么可以在应用程序中编译和连接之后,渲染之前设置它的大小。
着色器中可以通过 length()
方法获取渲染时的数组大小。
Note :
- buffer 块只可以使用 std430 布局
3. in / out 块
3.1 着色器的输入块与输出块
着色器变量从一个阶段输出,然后再输入到下一个阶段中,这一过程可以使用块接口来表示。
顶点着色器的输出 :
1 | out Lighting { |
片元着色器的输入 :
1 | in Lighting { |
3.2 设置成员的位置
layout(location=N)
被用于每个独立的输入和输出变量。
它也可以作用于输入和输出块的成员,显式地设置它们的位置:
1 |
|
无论这些 location 位置信息是否在块中,都是可以等价于一个vec4的。
3.3 分量
如果用户希望把多个小的对象设置到同一个位置上,那么也可以使用分量(component)关键字。
1 |
|