OpenGL ES 编程基础(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011374318/article/details/80561602

OpenGL ES 着色语言基础

预处理器和指令

OpenGL ES 着色语言中配备的预处理器遵循大多标准 C++ 预处理器的约定,如下面的宏指令和条件测试:

#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif

OpenGL ES 着色语言中不支持含有参数的宏定义

  • __LINE__ 表示着色器中的行号
  • __FILE__ 表示文件名,但在 OpenGL ES 3.0 中为 0
  • __VERSION__ 表示着色语言版本,如 300
  • GL_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) ,但是这会导致性能下降。

猜你喜欢

转载自blog.csdn.net/u011374318/article/details/80561602