OpenGL ES 3.0编程指南:第五章. OpenGL ES
[TOC]
17. Preprocessor and Directives(预处理器和指令)
Shader语言的另一个功能是预处理器,遵循许多C++预处理器的约定。可以使用如下指令来定义宏和条件测试:
#define
#undef
#if
#ifdef
#ifndet
#else
#elif
#endif
注意,shader语言里的宏定义不能使用参数(在C++中可以,这是两者不同的地方)。下面的宏是预定义的:
__LINE__ // Replaced with the current line number in a shader
__FILE // Always 0 in OpenGL ES 3.0
__VERSION__ // The OpenGL ES shading language version (eg. 300)
GL_ES // This will be defined for ES shaders to a value of 1
指令#error会在shader编译时引发一个编译错误,并打印出相关日志。指令#pragma用于为编译器指定特定于实现的指令。
另一个重要指令是#extension,用来启用和设置扩展行为。当设备供应商扩展OpenGL ES shader语言时,它们将创建一个语言扩展声明(例如 GL_NV_shadow_samplers_cube)。Shader必须告诉编译器是否允许使用扩展,如果不允许,应该采取什么行动。使用方法:
#extension extension_name : behavior
#extension all : behavior
第一个参数是扩展的名字或者all(对所有扩展都应用该行为),第二个参数是要采取的行为:
扩展行为 | 描述 |
---|---|
require | require指明该扩展是必须的,如果该扩展不受支持则预处理器会抛出错误。如果指定了all,将总是抛出错误。 |
enable | enable指明该扩展被启用,如果该扩展不受支持则预处理器会发出警告。如果扩展被启用,语言将会被处理。如果指定了all,将总是抛出错误。 |
warn | warn指明当该扩展被使用时发出警告(除非这种使用是另一个已经被启用的扩展所必须的)。如果指定了all,则在任何该扩展被使用时发出警告。同时,如果该扩展不被支持也会发出警告。 |
disable | disable指明该扩展是禁用的,所有当该扩展被使用时将会抛出错误。如果指定了all(默认),则不启用任何扩展。 |
18. Uniform and Interpolator Packing
正如前面几个小节中所介绍的,在底层硬件中可用于储存uniform和输入输出变量的资源是有限的。Uniform储存在“常量储存区”,顶点输出变量和片段输入变量储存在插值器中,通常都保存为一个向量数组。那么,这些变量在底层硬件上到底是如何进行实际储存的呢?
在OpenGL ES 3.0中,通过打包规则(packing)来定义这些变量是如何映射到物理储存空间的。打包规则基于这样一个概念,物理储存空间被组织为一个4列的网格(每一行是一个储存位置)。来看一个具体例子:
uniform mat3 m;
uniform float f[6];
uniform vec3 v;
如果不进行打包,那么矩阵m会占用3行,数组f会占用6行,向量v会占用1行,这样会浪费许多空间,如下表所示:
Location | X | Y | Z | W |
---|---|---|---|---|
0 | m[0].x | m[0].y | m[0].z | -- |
1 | m[1].x | m[1].y | m[1].z | -- |
2 | m[2].x | m[2].y | m[2].z | -- |
3 | f[0] | -- | -- | -- |
4 | f[1] | -- | -- | -- |
5 | f[2] | -- | -- | -- |
6 | f[3] | -- | -- | -- |
7 | f[4] | -- | -- | -- |
8 | f[5] | -- | -- | -- |
9 | v.x | v.y | v.z | -6(书上写的-6,不明白为什么,觉得应该也是空才对) |
如果进行打包,这些变量会被组织成下面这样:
Location | X | Y | Z | W |
---|---|---|---|---|
0 | m[0].x | m[0].y | m[0].z | f[0] |
1 | m[1].x | m[1].y | m[1].z | f[1] |
2 | m[2].x | m[2].y | m[2].z | f[2] |
3 | v.x | v.y | v.z | f[3] |
4 | -- | -- | -- | f[4] |
5 | -- | -- | -- | f[5] |
如果所示,如果使用了打包规则,只需要使用6个物理常量位置,否则就要使用10个。另外,数组元素需要跨行存储,这是因为GPU通常根据物理常量位置索引来对常量储存进行索引,如果数组的元素没有跨行存储,那么数组的索引将不会起作用。
打包将影响uniform和输入输出变量的计数方式。如果想要编写在所有实现都能正常运行的shader,就要确保使用的变量数不超过OpenGL ES的所有实现所支持的数量。因此,了解打包规则就很重要的。