OpenGL超级宝典(第7版)笔记21 着色器存储区块shader storage block 清单5.29-5.30
文章目录
前言
上一篇为大家介绍了uniform变量和uniform块的用法,其中有许许多多要注意的点,uniform变量和块使得我们可以更方便的传入数据到着色器中去,但是我们的着色器并不能对其数据进行修改,这非常难受,本篇中会为大家介绍着色器储存区块,看看我们是如何建立着色器存储区块(shader storage block)、用着色器修改它的值并传递回来的。
1 着色器存储区块shader storage block 初介绍
其实存储区块(shader storage block)和统一区块(uniform block)很像,都可以向着色器提供数据,但是存储区块的优点在于着色器可以对其数值进行修改,而且存储区块可以更大(uniform块一般大小限制在64k,而存储区块可以达到1G的级别)。当然其也有在一定的缺点,首先是由于存储区块很灵活,导致OpenGL很难对它进行优化,所以访问的时候会稍慢一些。其次uniform块可以更早的读入数据到着色器中,而存储区块可能再顶点着色器开始运行了才读入数据。
两者还是有一些区别的:
第一是存储区块的数据布局是std140或std430(shared不知道行不行),其增加的std430布局在数组和结构的对齐上可以更加紧凑。
std430的布局的工作方式与std140相似,不同之处在于对标量和矢量元素的数组和结构的对齐方式和跨度进行了一些优化(vec3元素除外)。具体来说,它们不再四舍五入为16个字节的倍数。因此,一个
float
s[]数组将与一个C++float
s[]数组匹配。可详见:OpenGL wiki中的介绍
第二是存储区块还支持原子内存操作,通过将程序序列化执行,防止对同一变量修改的争抢所导致的数据出错(比如两个着色器同时对一个存储区块中的数据进行m=m+1的修改,会产生冲突,结果并不一定是m+2,而原子内存操作就解决了这个问题)
下面我们依次介绍这些内容
1 着色器存储区块
1.1 声明
跟uniform块一样,我们先来看看声明:
layout (binding=6,std430) buffer TransformBlock{
bool open;
mat4 camera;
float iso[6];
vec4 fscolor1;
};
与之相似的uniform块声明:
uniform TransformBlock{
bool open;
mat4 camera;
float iso;
vec4 fscolor1;
} transform;
其实你仔细对比下面的uniform块,你会发现最后的transform没有了,所以我们在引用存储区块中的变量时不用transform.fscolor来调用了,直接用fscolor就行了。
比如:
void main(void){
fscolor = fscolor1.y*vec4(vscolor,1.0);
gl_Position = camera * vec4(vsposition,1.0);
}
1.2准备数据
由于是std430数据布局所以我们先在纸上演算一下其数据的起始位置都是多少:
layout (binding=6,std430) buffer TransformBlock{
bool open; //起始:0 对齐基数:4 占用空间:4*1 结束:4
mat4 camera; //起始:16 对齐基数:16 占用空间:16*4 结束:80
float iso[6]; //起始:80 对齐基数:4 占用空间:4*6 结束:104
vec4 fscolor1; //起始:112 对齐基数:16 占用空间:16*1 结束:128
};
观察到iso[6]中的每个元素并不是占16字节了,而是其本身