二、内建变量与内建常量、内建函数、预处理器
一、顶点着色器中的内建变量
1、内建输入变量
-
gl_VertexID
gl_VertexID是顶点着色器的一个内建输入变量,类型为“highp int”,主要用来记录顶点的整数索引。此内建变量是WebGL 2.0新增的,通过其可以很方便地实现一些1.0很难实现的功能。
-
gl_InstanceID
gl_InstanceID是顶点着色器的另一个内建输入变量,类型为“highp int”,其用来记录在采用实例绘制时当前图元对应的实例号。如果当前图元不是来自实例绘制,则gl_InstanceID的值为0。
2、内建输出变量
顶点着色器中的内建输出变量主要有gl_Position和gl_PointSize。这两个变量分别用来存放处理后的顶点位置和顶点大小,都只能在顶点着色器中使用,其具体含义如下:
-
gl_Position
顶点着色器从渲染管线中获得原始的顶点位置,它们在顶点着色器中经过平移、旋转、缩放等数学变换后,生成新的顶点位置。新的顶点位置通过在顶点着色器中写入gl_Position内建变量中以传递到渲染管线的后继阶段继续处理。gl_Position的类型是vec4,写入的顶点位置数据也必须与此类型一致。几乎在所有的顶点着色器中都必须对gl_Position写入适当的值,否则后继阶段的处理结果将是不确定的。
-
gl_PointSize
顶点着色器可以计算一个点的大小(单位为像素),并将其赋值给gl_PointSize(标量浮点型)以传递给渲染管线。如果没有明确赋值,就会采用默认值1。一般只有在采用了点绘制方式之后,gl_PointSize的值才有意义。
二、片元着色器中的内建变量
1、输入变量
- gl_FragCoord
如下图所示,内建变量gl_FragCoord中含有当前片元相对于窗口位置的坐标值x、y、z与1/w。其中x与y分别为片元相对于窗口的二维坐标。如果窗口的大小为800×480(单位为像素),那么x的取值范围为0~800, y的取值范围为0~480, z部分为该片元的深度值。
通过使用该内建变量可以很方便地开发出某些与窗口位置相关的特效,例如仅绘制窗口中某一指定区域的内容等。
-
gl_FrontFacing
gl_FrontFacing是一个布尔型的内建变量。通过其值可判断正在处理的片元是否属于在光栅化阶段此片元生成的对应图元的正面。若属于正面,则gl_FrontFacing的值为true,反之为false。其一般用于开发与双面光照功能相关的应用程序中。
像点、线段等是没有正反面之分的图元,其生成的片元都会被默认为是正面。对于三角形图元来说,其正反面取决于程序对卷绕的设置及图元中顶点的具体卷绕情况。
- gl_PointCoord
gl_PointCoord是vec2类型的内建变量,当启用点精灵时,gl_PointCoord的值表示当前图元中片元的纹理坐标,其值范围为0.0~1.0。如果当前图元不是一个点或者未启用点精灵,则gl_PointCoord的值是不确定的。
三、内建常量
内建常量用来限制每种属性变量的数量,也就是用来规定每种自定义变量数量的最大值。
对于不同品牌的手持设备,以上内建常量的默认值各不相同,上图给出的是各设备默认值的最小值,如有其他需求请自行查看此设备的默认值。
四、内建 uniform 变量
为了能够访问渲染管线的处理状态,着色器语言中加入了uniform内建变量。下面的代码片段说明了内建uniform变量的内容。
1 struct gl_DepthRangeParameters{
2 highp float near; //near值,关联于透视中的near值
3 highp float far; //far值,关联于透视中的far值
4 highp float diff; //far-near值
5 };
6 uniform gl_DepthRangeParameters gl_DepthRange;
可以认为上面的代码片段是着色语言本身内建的,如果想要访问gl_DepthRangeParameters结构体内的内容,则直接通过uniform的内建变量gl_DepthRange进行访问即可。
五、内置函数
着色语言也提供了很多内置函数。这些函数大都已被重载,一般具有5种变体,分别用来接收和返回genType、genDType、genIType、genUType及genBType类型的值。
genType、genIType、genUType和genBType分别代表的是浮点型系列、整型系列、无符号整型系列和布尔型系列。
1、角度转换与三角函数
角度转换与三角函数同时适用于顶点着色器与片元着色器,并且每个角度转换与三角函数都有4种重载变体。
由于sin(正弦函数)与cos(余弦函数)是周期性平滑变化的函数(如图3-4所示),因此除了可以作为三角函数之外,还有很多使用方式。例如,可以它们用来模拟水波纹效果,只需要选择其中的一个函数,然后进行周期性变换即可
2、指数函数
3、常见函数
这些函数可同时用于顶点着色器与片元着色器
4、几何函数
几何函数同时适用于顶点着色器与片元着色器,主要对向量进行操作。
- dot函数
此函数的功能是返回两个向量x与y的点积。两个向量点积的符号是与两个向量间的夹角直接相关的。夹角大于0并且小于90°,则点积所得结果大于0;夹角等于90°,则点积所得结果为0;夹角大于90°,则点积小于0。
- cross函数
此函数的功能是返回两个向量x与y的叉积,两个向量叉积的绝对值为这两个向量所在四边形的面积
- reflect函数
此函数功能是根据传入的入射向量I以及表面法向量N,返回反射方向的向量.
- refract函数
此函数功能是根据传入的入射向量I、表面法向量N以及折射系数eta,返回折射向量
1 k= 1.0 - eta * eta *(1.0 - dot(N, I) * dot(N, I)); //计算判断系数k2
2 if (k < 0.0){
3 return genType(0.0); //若符合全反射则无折射向量
4 }else{return eta * I - (eta *dot(N, I) + sqrt(k)) * N; }//若不符合全反射根据斯涅尔定
律计算折射向量
5、矩阵函数
矩阵函数同时适用于顶点着色器与片元着色器,主要包括生成矩阵、矩阵转置、求矩阵的行列式以及求逆矩阵等有关矩阵的操作
6、向量关系函数
向量关系函数的主要功能为将向量中的各分量进行关系比较运算(<、<=、>、>=、==、!=)
7、纹理采样函数
纹理采样函数根据指定的纹理坐标从采样器对应的纹理中进行采样,返回得到的颜色值。大部分纹理采样函数既可以用于顶点着色器也可以用于片元着色器,但有个别的仅适用于片元着色器。
返回值类型gvec4中的“g”为占位符,其中gvec4表示vec4、ivec4或uvec4 等类型。
- bias参数
含有bias参数的纹理采样函数只能在片元着色器中调用,且此参数仅对sampler为Mipmap类型的纹理时才有意义。
- Lod后缀系列
带有“Lod”后缀的纹理采样函数仅适用于顶点着色器,其中的Lod(Level of detail的缩写)参数将直接作为Mipmap纹理采样时的细节级别。因此,此系列函数也仅对sampler为Mipmap类型的纹理才有意义。
8、微分函数
微分函数仅能用于片元着色器,是从WebGL 2.0开始在标准中正式支持的。
9、浮点数的打包与解包函数
主要包括packSnorm2x16、unpackSnorm2x16、packUnorm2x16、unpackUnorm2x16、packHalf2x16以及unpackHalf2x16。
10、用invariant修饰符避免值变问题
值变问题是指同样着色器程序多次运行时,同一个表达式在同样输入值的情况下多次运行,结果出现不精确一致的现象。
在大部分情况下,这并不影响最终效果的正确性。如果在某些特定情况下需要避免值变问题,则可以用invariant修饰符来修饰变量。采用invariant修饰符修饰变量主要有如下两种方式。
(1)在声明变量时加上invariant修饰符,具体如下代码。
invariant out vec3 color;
(2)对已经声明的变量补充使用invariant修饰符进行修饰,具体情况如下代码。
1 out vec3 color;
2 invariant color;
若有多个已经声明的变量需要用invariant修饰符补充修饰,则可以在invariant修饰符后把这些变量名用逗号隔开,一次完成。另外,若用invariant修饰符补充修饰变量,则必须在变量第一次被使用前完成。
如果希望所有的输出变量都是由invariant修饰的,则可以采用如下的语句来完成。
#pragma STDGL invariant(all)
要注意的是,上述代码应该添加在顶点着色器程序的最前面,且不能在片元着色器中使用。
需要注意的是,并不是所有的变量都可以用invariant修饰符来修饰,只有符合如下几种情况的变量可以用invariant修饰符修饰。
- 顶点着色器中的内建输出变量,如gl_Position。
- 顶点着色器中声明的以out修饰符修饰的变量。
- 片元着色器中内建的输出变量。
- 片元着色器中声明的以out修饰符修饰的变量。
- 另外就是在使用时要注意,invariant修饰符要放在其他修饰符之前。同时,invariant修饰符只能用来修饰全局变量。
若不是真正绝对需要同样的输入产生同样精确的输出,则应该避免使用invariant修饰符。因为使用此修饰符后着色器中的有些内部优化就无法进行了,这会对性能有一定的影响。在大部分情况下,一些误差对程序的正确性没有影响,因此,需要使用invariant修饰符的情况并不多。
六、预处理器
预处理器是在真正编译开始之前由编译器调用的独立程序。预处理器预处理编译过程中所需的源代码字符串。WebGL着色语言的预处理器基本遵循标准C++预处理器的规则,宏定义和条件测试等可以通过预处理指令来执行。
如果定义了宏但是没有同时给出替代表达式,那么并不会默认替代表达式为“0”。
默认情况下,着色语言的编译器必须反馈不符合规范的编译时词法和语法错误。任何扩展行为都必须先启用,可以通过#extension指令控制编译器扩展行为,其基本语法为:
#extension <扩展名>:<扩展行为>
扩展名为硬件厂商提供的有特殊扩展功能的名称,使用时读者需要查阅各厂商提供的资料。
编译器的初始状态为#extension all:disable,意味着编译器关闭任何扩展。
本章参考如下:
《WebGL 3D 开发实战详解》