本文共 2013 字,大约阅读时间需要 6 分钟。
OpenGL ES 着色语言中配备的预处理器遵循大多标准 C++ 预处理器的约定,如下面的宏指令和条件测试:
#define#undef#if#ifdef#ifndef#else#elif#endif
OpenGL ES 着色语言中不支持含有参数的宏定义
__LINE__
表示着色器中的行号__FILE__
表示文件名,但在 OpenGL ES 3.0 中为 0__VERSION__
表示着色语言版本,如 300GL_ES
表示着色器中的 1 除了 #error
#pragma
还有重要的 #extension
指令,用于启用和设置扩展的行为。
设置某一个扩展的行为:#extension <扩展名> : <行为>
设置所有扩展的行为:#extension all : <行为>
可选择的扩展行为如下:
扩展行为 | 说明 |
---|---|
require | 表示扩展是必须的,所以预处理器在扩展不受支持时将抛出错误,如果指定 all 为这种行为,则始终抛出错误 |
enable | 表示启用扩展,如果不支持,预处理器将抛出警告,否则就处理该扩展,如果指定 all 为这种行为,则始终抛出错误 |
warn | 使用扩展时抛出警告,除非该扩展是另一个启用的扩展所必须的,如果指定 all ,那么使用任何扩展时都会抛出警告,即使扩展不被支持,也会抛出警告 |
disable | 禁用扩展,若使用则抛出错误,如果指定 all(默认)则不启用任何扩展 |
由于硬件的限制,统一变量及插值器的存储空间是宝贵的,所以合理使用存储空间十分必要。在 OpenGL ES 3.0 中,为了更好的利用空间,采用打包规则将插值器和统一变量映射到物理存储空间中。
打包规则基于物理存储空间被组织为一个每个存储位置4列和1行的网格的概念,这里可以连系到矩阵的默认列优先存储。如下面的例子,可看出打包与未打包之间的区别。
uniform mat3 m;uniform float f[6];uniform vec3 v;
如下是未进行打包的统一变量存储
存储位置 | 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 | - |
如下是进行了打包的统一变量存储
存储位置 | 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] |
可见,使用打包后,节省了4个物理常量位置,需要注意的是,数组的元素必须跨越行边界进行存储,因为 GPU 通常是按照向量位置索引对常量存储进行索引,所以要使索引有效,需要数组元素跨越行边界。
虽然打包是对 OpenGL ES 语言使用者是透明的,但是打包会影响统一变量和插值器的计数方式,这就关系着着色器最终所需的运行存储空间,所以要编写可移植的着色器,这个所需的运行存储不应超过 OpenGL ES 3.0 所支持的最小存储空间。
在着色器中指定变量的计算精度,有助于着色器的运行并可以节约电量,但也可能因精度不够导致伪像。
精度关键字有:highp mediump lowp,分别表示高、中、低三个精度。
highp vec4 position;varying lowp vec4 color;mediump float pi;
另外可以指定变量类型的默认精度
precision highp float;precision mediump int;
顶点着色器中的默认精度是最高的 highp ,但是在片段着色器中 float 没有默认精度,需要自己指定。当然,指定的精度的表示范围在不同的硬件上可能是不同的。
编译器在编译着色器时,可能会对指令进行重排优化,这就意味着相同的输入参数及计算条件可能会得到两个不同的结果。如在使用 Alpha 混合绘制时,可能因为计算的输出位置精度不一致而导致伪像,即深度冲突(Z fighting)。
所以为了避免类似的情况,可以使用 invariant 关键字将输出变量声明为不可变,那么编译器将保证在相同计算和着色器输入条件下,输出结果是相同的。
invariant gl_Position;
另外,还可以指定所有变量的不可变性:#pragma STDGL invariant(all)
,但是这会导致性能下降。
转载地址:http://dadws.baihongyu.com/