GLSL 着色器语言
笔记总结自 《Vulkan 开发实战详解》第三章
GLSL 着色语言是一种易于实现、功能强大、便于使用,并且可以高度并行处理、性能优良的高级图形编辑语言。
特性:
- 高级的过程语言;
- 对顶点着色器、片元着色器、曲面细分着色器、几何着色器以及计算着色器使用相同的语言,不做区分;
- 基于C/C++ 的基本语法及流程控制;
- 支持向量及矩阵的各种操作;
- 通过特定限定符来管理输入与输出;
- 拥有大量的内置函数来提供丰富的功能;
1. 着色器语言基础
1.1 数据类型
1) 标量
标量的值只有大小,没有方向。着色器语言支持的标量类型有 bool、int、uint 与 float 。
- 布尔类型——bool:
bool b; // 声明一个布尔类型的值, true/false
- 有符号整型/无符号整型——int/uint
// 着色器语言支持32位精度,超出范围会导致溢出问题。
// 一般情况使用有符号整型
// 无符号整型不能用负数来声明
int a = 15; // 十进制
uint b = 3u; // 无符号十进制,声明无符号整型字面常量时,需要在数字后添加后缀u或 U,否则该字面常量的类型为有符号整型。
int c = 036; // 八进制
int d = 0x3D; // 十六进制
- 浮点类型——float:
// 浮点类型可以用十进制形式和指数形式两种方式表示
float f; // 声明float变量
float g = 2.0; // 声明变量并赋值
float h, i ; // 声明多个变量
float j, k = 2.56, 1; // 声明多个变量并赋值
flaot s = 3e2; // 声明变量,并赋予指数形式的初值,表示3 乘以 10 的平方
2) 向量
向量可以看作由同样类型的标量组成,基本类型也分为 bool、int、uint 及 float。每个向量可以由 2 个、3 个或者 4 个相同的标量组成。
C语言中也可以自定义结构体来支持向量,但进行向量运算必须由CPU 将每个分量依次顺序计算,效率不高。着色器中的向量由硬件原生支持,进行向量的运算时是各分量并行一次完成的(n个分量只需要一次计算)。
向量在着色器中可以很方便的存储及操作颜色、位置、纹理坐标等不仅包含一个组成部分的量。
-
vec 包含浮点数的向量
- vec2 、vec3、vec4
-
ivec 包含整数的向量
- ivec2、ivec3、ivec4
-
uvec 包含无符号整数的向量
- uvec2、uvec3、uvec4
-
bvec 包含布尔数的向量
- bvec2、bvec3、bvec4
-
向量的声明
vec2 v2;
ivec3 v3;
uvec3 vu3;
bvec4 v4;
- 将向量看作颜色时,可以使用r、g、b、a 等4个分量名,分别代表 红、绿、蓝、透明度 4 个颜色通道。
aColor.r = 0.6; // 给向量aColor 的红色通道分量赋值
aColor.a = 0.8; // 给向量aColor 的透明通道分量赋值
- 将向量看作位置时,可以使用x、y、z、w 等4个分量名,分别代表x轴、y轴、z轴分量及W值。
aPosition.x = 108.5; // 给向量aPosition的x轴分量赋值
aPosition.z = 2422; // 给向量aPosition的z轴分量赋值
- 将向量看作纹理坐标时,可以使用s、t、p、q 等4个分量名,分别代表纹理坐标的不同分量。
aTexColor.s = 0.65; // 给向量aTexColor的 s 分量赋值
aTexColor.t = 0.82; // 给向量aTexColor的 t 分量赋值
- 访问向量中的各个分量不仅可以采用“.” 加上不同的分量名,还可以将向量看作一个数组,用数组下表来访问
aColor[0] = 0.6; // 给向量aColor 的红色通道分量赋值
aColor[3] = 0.8; // 给向量aColor 的透明通道分量赋值
3)矩阵
- 矩阵的值为浮点数。
- 3D 场景中的移位、旋转、缩放等变换都是由矩阵的运算来实现的,故着色器提供了对矩阵类型的支持。
- 矩阵按尺寸分为 2 × 2 矩阵、2 × 3 矩阵、2 × 4 矩阵、3 × 2 矩阵、3 × 3 矩阵、3 × 4 矩阵、4 × 2 矩阵、4 × 3 矩阵、4 × 4 矩阵。
- 矩阵第一个数字表示矩阵的列数,第二个数字表示矩阵的列数。
着色器语言中,可以将一个矩阵看作几个列向量组成,比如 mat3 可以看作3个 vec3 组成。可以将矩阵作为列向量的数组来访问,比如mat4 的 matrix[2] 表示第三列,值为 vec4; 也可以使用 matrix[[2][[2] 表示第三列向量的第三个分量,值为float
mat2 m2; // 声明一个mat2 类型矩阵
mat3 m3; // 声明一个mat3 类型矩阵
mat4 m4; // 声明一个mat4 类型矩阵
mat3×2 m5; // 声明一个 mat3×2 类型的矩阵
4)采样器
-
着色器语言中不同于C语言的一种特殊基本数据类型,专门用来进行纹理采样的相关操作。
-
一般情况下,一个采样器变量代表一幅或者一套纹理贴图。
-
采样器变量不能在着色器中进行初始化。
-
一般情况下采样器变量都用uniform 限定符来修饰,从宿主语言(如 C++)接收传递进着色器的值。
-
采样器变量也可以用作函数的参数,但是作为函数时不可以使用out 或 inout 修饰符来修饰。
-
采样器基本类型
- sampler2D: 用于浮点型的二维纹理
- sampler3D: 用于浮点类型三维纹理
- samplerCube: 用于访问浮点型的立方贴图纹理
- samplerCubeShadow: 用于访问浮点型的立方阴影纹理
- smapler2DShaodow: 用于访问浮点型的二维阴影纹理
- sampler2DArray: 用于访问浮点型的二维纹理数组
- sampler2DArrayShadow: 用于访问浮点型的二维阴影纹理数组
- isampler2D: 用于访问整型的二维纹理
- isampler3D: 用于访问整型的三维纹理
- isamplerCube: 用于访问整型的立方贴图纹理
- isampler2DArray: 用于访问整型的二维纹理数组
- usampler2D: 用于访问无符号整型的二维纹理
- usampler3D: 用于访问无符号整型的三维纹理
- usamplerCube: 用于访问无符号整型的立方贴图纹理
- usampler2DArray: 用于访问无符号整型的二维纹理数组
5) 结构体
- 类似于C语言的自定义结构体,同样使用 struct 关键字进行声明。
struct info { // 声明结构体 info
vec3 color; // 颜色成员
vec3 position; // 位置成员
vec2 textureColor; // 纹理坐标成员
}
// 使用结构体
info CubeInfo; // 声明info 类型的变量 CubeInfo
6) 数组
- 着色语言支持自定义数组
- 着色器语言只支持一维数组,不支持二维数组。
// 方式一: 在声明数组的同时指定数组大小
vec3 position[20]; // 声明了一个包含20个 vec3 的数组
// 方式二: 在声明数组并初始化的同时,可以不指定数组的大小
float x[] = float[2](1.0, 2.0); // 数组的长度为2
float y[] = float[](1.0, 2.0, 3.0); // 数组的长度为3
7) 空类型
- 空类型使用 void 表示,仅用来表明不返回任何值的函数。 如在顶点着色器以及片元着色器中必须存在的 main 函数是一个返回值为空类型的函数。
void main() {
// ...
}
1.2 数据类型的基本使用
1) 声明、作用域及初始化
- 变量声明及作用域 与 C/C++ 语言类似,可以在任何需要的位置声明变量。
- 作用域与C/C++ 语言类似,分为局部变量与全局变量。
int a, b; // 声明全局变量 a、 b, 作用域为整个着色器
vec3 aPosition = vec3(1.0, 2.0, 3.3); // 声明全局变量 aPosition 并赋值
void myFunction() {
int c = 14; // 声明局部变量c 并赋值, 作用域为 myFunction 函数内
a = 4; // 给全局变量a 赋值
b = a * c; // 给全局变量b 赋值
}
- 变量命名规则:
- 由字母、数字、下划线组成,且必须以字母或者下划线开头。
- 系统很多内建变量以“gl_”开头,自定义变量不允许使用“gl_” 作为开头(建议)。
- 取名尽量采用有意义的拼写,多个单词组合时,第一个字母小写,其余采用驼峰命名(建议)。
// 向量各分量值相同,可以简化写法
vec4 vc = vec4(0.2);
- 矩阵初始化的技巧:
- 初始化矩阵的各个元素可以i使用字面常量,也可以使用变量,还可以从其他向量直接获取。
- 初始化时若矩阵只有对角线上有值且相同,可以通过给出1个字面常量初始化矩阵。
- 初始化时矩阵M1 的行列数(N × N) 小于构造器中矩阵 M2 的行列数(M × M)时(N < M),矩阵 M1 的元素值为 矩阵 M2 左上角 N × N 子阵的元素值。
- 初始化时矩阵 M1 的行列数(N × M)与构造器中矩阵 M2 的行列数(P × Q) 不同,且 P 和 Q 之间的最大值大于N 和M 之间的最大值时(假设 M1 为 mat2 × 3, M2 为 mat 4 × 2),矩阵 M1 和左上角 N × N 的元素值为矩阵 M2 左上角 N × N 元素的值,矩阵 M1 的其他行的元素值为 0。
- 初始化时矩阵 M1 的行列数(N × N)大于构造器中矩阵 M2 的行列数(M × M)时(即 N > M),矩阵M1 左上角 M × M 的元素值为矩阵M2 的元素值,矩阵M1 右下角的元素值为 1, 矩阵M1 剩余其他的元素值为0。
float a = 6.3;
float b = 11.4;
float c = 12.5;
vec3 va = vec3(2.3, 2.5, 3.8);
vec3 vb = vec3(a, b, c);
vec3 vc = vec3(vb.x, va.y, 14.4);
mat3 ma = mat3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, c); // 通过给出9个字面常量初始化 3 × 3 矩阵
mat3 mb = mat3(va, vb, vc); //通过给出3个向量初始化 3 × 3 的矩阵
mat3 mc = mat3(va, vb, 1.0, 2.0, 3.0); // 通过给出2个向量和3个字面常量初始化 3 × 3 的矩阵
mat3 md = mat3(2.0); // 通过给出 1 个字面常量初始化 3 × 3 的矩阵
mat4×4 me = mat4×4(3.0); // 等价于(3.0, 0, 0, 0, 0, 3.0, 0, 0, 0, 0, 3.0, 0, 0, 0, 0, 3.0);
mat3×3 mf = mat3×3(me); // 等价于(3.0, 0, 0, 0, 3.0, 0, 0, 0, 3.0);
vec2 vd = vec2(a, b);
mat4×2 mg = mat4×2(vd, vd, vd, vd);
mat2×3 mh = mat2×3(mg); // 等价于 mat2×3(6.3, 11.4, 0.0, 6.3, 11.4, 0.0)
mat4×4 mj = mat4×4(mf); // 等价于 mat4×4(3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0 )
2) 变量初始化的规则
- 为了防止重复计算,着色器中应少用字面长两个,而用常量代替,如有多个1.0、0.0,可以声明常量,重复使用。
- 常用初始化方式为变量可以在声明的时候就进行初始化
int a = 2, b = 3, c;
- 用 const 限定符修饰的变量必须在声明的时候进行初始化
const float k = 1.0;
- 全局的输入变量、一致变量以及输出变量在声明的时候一定不能进行初始化
int float angleSpan; // 不可对输入变量进行初始化
uniform int k; // 不可对一致变量进行初始化
out vec3 position; // 不可对输出变量进行初始化
1.3 运算符
- 运算符列表(按优先级排列,从左到右、从上到下)
运算符 | 说明 | 运算符 | 说明 |
---|---|---|---|
() | 括号分组 | [] | 数组下标 |
() | 函数调用和构造函数结构 | . | 用于成员选择与混合 |
++ – | 自增自减前缀 | ++ - - | 自增自减前缀 |
+ - ~ ! | 一元运算符 | * / % | 乘法、除法、取余 |
+ - | 加法减法 | << >> | 逐位左移和逐位右移 |
<> <= >= | 关系运算符 | == != | 等于 不等于 |
& | 逐位与 | ^ | 逐位异或 |
| | 逐位或 | && | 逐位与 |
^^ | 逻辑异或 | || | 逐位或 |
?: | 选择 | = += -= *= /= | 赋值运算符 |
%= <<= >>>= &= ^= |= | 赋值运算符 | , | 按顺序排列 |
1) 索引
用在对数组、向量或者矩阵的操作中,获取数组、向量或者矩阵的元素。
float array[10];
2) 混合选择
运算符“.”进行混合选择操作。
vec4 color = vec4(0.7, 0.1, 0.5, 1.0);
vec3 temp = color.agb; // 相当于拿到一个向量(1.0, 1.0, 0.5) 并赋值给temp
vec4 tempL = color.aabb; // 相当于拿到一个向量 (1.0, 1.0, 0.5, 0.5) 并赋值给 tempL
vec3 tempL;
tempLL.grb = color.aab; // 对向量 tempLL 的三个分量赋值
- 一次混合最多只能列出4个分量名称,且一次出西安的各部分的分量名称必须时来自同一名称组
- 各分量的名称在进行混合时可以改变顺序以进行重新排列
- 以赋值表达式中的“=” 为界,做测成为L 值,右侧成为R 值。进行混合时,R值可以使用一个向量的各个分量任意地组合以及重复。
3) 算数运算符
- 对标量而言,算数运算符与C 语言相同。
- 矩阵运算,进行的是线性代数的相关运算。
4) 其他运算符
- 关系运算符只能用在浮点数或整数标量的操作中。 如果想要得到两个向量中的每一个元素比较大小的结果,则可调用内置函数 lessThan、lessThanEqual、 greaterThan、greaterThanEqual 实现。
- 等于运算符可以用在任意类型数据的操作中。如果想要得到向量中的每一个元素是否相等,可以调用内置函数 equal 和 noEqual。
- 逻辑运算符 用在类型为布尔标量的表达式中,不可以用在矩阵中。
- 选择运算符的使用与 C 相同,可以用在除数组之外的任何类型中。但是要注意的式 第二个和第三个表达式必须是相同的类型。
- 位运算符 只适用于有符号或者无符号的整型标量 或者整型向量的类型。
- 赋值运算符最常用的 “=” 在操作时,要求符号两边的操作数必须类型完全相同。 着色器语言的赋值没有自动类型转换或提升功能。
1.4 各个数据类型的构造函数
构造函数的使用可以看作函数调用,函数名称是某一数据类型的名称,结果是得到的指定类型的实例。构造函数还可以用来进行数据类型的转换。
1) 向量的构造函数
- 向量的构造函数可以用来创建指定类型向量的实例,其入口参数一般可以为基本类型的字面常量、变量或其他向量。
- 如果向量构造函数内只有一个标量,那么该向量的所有分量都等于该值
- 如果向量的构造函数内有多个标量或者向量参数,那么向量的分量则由左向右一次被赋值。在这种情况下,参数的分量和向量的分量至少要一样多。
- 如果向量构造函数的参数与对应的向量类型不相符,则会选择数据类型转换的方式转换参数类型,与向量类型匹配。
vec4 myVec4 = vec4(1.0); // 每一个分量值都是1.0
vec3 myVec3 = vec3(1.0, 0.0, 0.5); // 分量分别为1.0, 0.0, 0.5
2) 矩阵的构造函数
- 矩阵构造函数的基本形式
- 如果矩阵的构造函数内只有一个标量值,那么矩阵的对角线上的分量都等于该值,其余值为0。
- 举着可以由许多向量构造而成。
- 矩阵可以由大量的标量构成,矩阵的分量由左向右依次被赋值。
3) 结构体的构造函数
如果一个结构体被定义,并且赋予了一个类型名,即可用该类型名去构造该结构体的实例。
struct {
float intensity;
vec3 position;
};
light lightVar = light(2.0, vec3(1.0, 2.0, 3.0));
4) 数组的构造函数
- 数组类型同样可以作为构造函数的名称,该构造函数同样适用于初始化或者表达式。
- 使用数组的构造函数时,需要保证参数的个数与定义的数组长度相同。
const float i[3] = float[3](1.0, 2.0, 3.0);
const float j[3] = float[](1.0, 2.0, 3.0);
float k = 1.0;
float m[3];
m - float[3](k, k_1.0, k+2.0);
1.5 类型转换
- 着色器没有提供类型的自动提升功能
- 着色器没有提供数据类型强转的功能
- 只能通过构造函数来完成类型转换
float f =1.0;
bool b = bool(f); // 非0 为true, 0 为false
float f1 = float(b); // true 为 0.1 , false 为 0.0
int c =- int(f1); // 去掉小数点
- 着色器语言的设计可以避免类型转换带来的性能、复杂性缺陷,简化了对硬件实现
// 以下声明会导致编译错误
float f0 = 1;
1.6 存储限定符
1) in/centroid in 限定符
- in/centrol in 限定修饰符修饰的全局变量又称 输入变量,其形成当前着色器与渲染管线前一阶段的动态输入接口。
- 输入变量的值实在着色器开始执行时,由渲染管线的前一阶段送入。
- 在着色器程序执行过程中,变量不可以被重复赋值。
- 使用情况:
- 顶点着色器的输入变量(只能使用in来修饰全局变量)
- 用来接收渲染管线传递进顶点着色器的当前待处理顶点的各种属性值。属性值每个顶点各自拥有独立的副本,用于描述顶点的各项特性,如:顶点坐标、法向量、颜色、纹理坐标等。
- 修饰的变量的值 实质是由宿主程序批量传入渲染管线,管线进行i基本处理后再传递给顶点着色器。
- 数据有多少顶点就调用多少次顶点着色器,每次将一个顶点的各种属性传递给in变量。因此顶点着色器每次执行将完成对一个顶点各项属性数据的处理。
- 顶点着色器的in限定符只能用来修饰浮点数标量、浮点数向量、矩阵变量以及有符号合伙则会无符号的整型标量或者整型向量。
- 顶点着色器的输入变量(只能使用in来修饰全局变量)
// 顶点着色器中使用 in 限定符
layout (location = 0) in vec3 aPosition; // 顶点位置
layout (location = 1) in vec3 aNormal; // 顶点法向量
-
- 片元着色器的输入变量(in/ centroid in)
- 用来接收来自前一阶段着色器(一般是顶点着色器)的相关数据,最典型的是接收根据顶点着色器的顶点数据插值产生的片元数据。
- 可以修饰的类型包括有符号或者无符号的整型标量或整型向量、浮点数标量、浮点数向量、矩阵变量、数组变量以及结构体变量。
- 当类型为有符号和或无符号整型标量或整型向量时,变量也必须使用flat限定符来修饰。
- 片元着色器的输入变量(in/ centroid in)
layout (location = 0) in vec3 vPosition; // 接收从顶点着色器传递过来的顶点数据
centroid layout (location = 1) in vec2 vTexCoord; // 接收从顶点着色器传递过来的纹理坐标
flat layout (location = 2) in vec3 vColor; // 接收从顶点着色器传递过来的颜色数据
2) uniform 限定符
- 一致变量限定符。 一致变量指对于同一组顶点组成的单个3D物体中所有顶点都相同的量。
- 可以使用在顶点着色器或片元着色器中,其支持用来修饰所有的基本数据类型。
- 一致变量的值也是从宿主程序传入。
unform mat4 uMVPMatrix; // 总变换矩阵
uniform mat4 uMatrix; // 变换矩阵
uniform vec3 uLightLocation; //光源位置
uniform vec3 uCamera; // 摄像机位置
3) out/centroid out 限定符
- 限定的变量又称输出变量,其形成当前着色器与渲染管线后继阶段的动态输出接口。
- 通常在当前着色器程序执行完毕时,输出变量的值才被送入后继阶段进行处理。
- 不能在着色器中声明同时起到输入和输出作用的inout 全局变量
- 使用情况
- 顶点着色器的输出变量
- 用out/centrioid out 修饰全局变量
- 被修饰的全局变量用于向渲染管线后继阶段传递当前顶点的数据
- 可以用来修饰浮点型标量、浮点型向量、矩阵变量、有符号或无符号的整型标量或整型向量、数组变量及结构体变量。
- 当顶点类型为有符号或无符号整型标量或整型向量时,变量必须使用flat限定符来修饰。
- 顶点着色器out变量的工作原理
- 首先,顶点着色器在每个顶点中都对out 变量 vPosition 进行赋值。
- 接着在片元着色器中接收in 变量 vPosition 的值时得到的并不是某个顶点赋的特定值,而是根据图元所在位置及图元中各个顶点的位置进行插值计算产生的值。
- 光栅化后产生了多少个片元,就会插值计算出多少套 in 变量。同时渲染管线就会调用多少次片元着色器。
- 一般情况下,对一个3D 物体的渲染中,片元着色器执行的次数会大大超出顶点着色器。
- GPU 硬件中配置的片元着色器硬件数量往往多余顶点着色器硬件数量,通过这些硬件单元的并行执行,提高渲染速度。
- 顶点着色器的后继阶段一般是片元着色器,但也有可能是曲面系分着色器或几何着色器。
- 片元器着色器的输出变量
- 只能用 out 限定符来修饰全局变量
- 片元着色器的out 输出变量一般指的是由片元着色器写入计算完成后片元颜色值的变量,一般在片元着色器的最后都需要对其进行赋值,然后将其送入渲染管线的后继阶段进行处理。
- out 限定符只能用来修饰浮点型标量、浮点型向量、有符号或无符号的整型标量或整型向量以及数组变量。
- 顶点着色器的输出变量
// 片元着色器中使用 out 限定符
out vec4 outColor; // 输出的片元颜色值
out vec4 outWorldPos; // 输出的片元世界坐标系坐标
4) const 限定符
- 只读变量,值是不变的,也就是常量,又称编译时常量。
- 声明时必须初始化。
- 在着色器外部是完全不可见的。
// 声明整型常量
const int tempx = 1;
1.7 插值限定符
- 主要用于控制顶点着色器传到片元着色器数据的插值方式。顶点着色器的 out 和 片元着色器的 in。
- 默认的插值方式为 smooth。
- 无论顶点着色器中的out 全局变量被哪种插值限定符修饰,后继 片元着色器中都必须含有与之对应的修饰符修饰的 in 全局变量。
1) smooth 限定符
- 顶点着色器传到片元着色器数据 参数的默认限定符
- 是在光栅化阶段由管线根据片元所属图元各个顶点对应的顶点着色器对 out 变量的赋值情况及片元与个顶点的位置关系插值产生
smooth out vec3 normal; // 顶点着色器 out 变量
smooth in vec3 normal; // 片元着色器 in 变量
2) flat 限定符
- 如果顶点着色器中out 变量之前含有 flat 限定符, 则传递到后继片元着色器中对应的in 的值不是在光栅化阶段插值产生的,一般是由图元的最后一个顶点对应的顶点着色器对此 out 变量所赋的值决定。此时图元中每个片元的此项值都相同。
- 若顶点着色器中的out 类型为整型标量或整型向量,则变量必须使用 flat 限定符修饰。
- 若片元着色器中的输入变量的类型为整型标量或整型向量,变量必须使用 flat 限定符修饰。
flat out vec4 vColor; // 顶点着色器 out 变量
flat in vec4 vColor; // 片元着色器 in 变量
1.8 一致块
- 多个一致变量的声明可以通过类似结构体形式的接口块实现,该接口块又称 一致块。
- 一致块的数据是通过缓冲对象送入渲染管线的,以一致块的形式批量传送数据比单个传送效率高。
- 一致块内不允许声明in 或 out 变量、采样器类型的变量,也不能定义结构体类型。
- 内建变量、数组变量及已定义结构体类型的变量可以作为一致块的成员变量。
- 创建一致块时,可以声明实例名,也可以不声明。
// 基本语法
// uniform 为一致块的修饰关键字,声明时必须使用
// 应用程序通过一致块名称识别一致块
// 成员变量列表可以包含多个变量的声明, 与普通结构体内成员变量的声明类似
// 实例名是一致块的实例名称,命名规则与一致块名称相同。
[<layout 限定符>] uniform 一致块名称 {<成员变量列表>} [实例名]
// 例如:
uniform Transform {
float radius;
mat4 modelViewMatrix;
uniform mat3 normalMatrix;
} block_Transform;
- 未声明实例名
- 一致块内的成怨怒变量与在块外一样,名称作用域时全局的,开发时可以直接通过一致块的成员变量名称访问对应变量。
- 声明实例名
- 一致块内成员变量的作用域为从声明开始到一致块结束,变成时通过<实例名> .<成员变量> 在块外访问成员变量
uniform MatrixBlock {
mat4 uMVPMatrix; // 块成员变量(总变换矩阵)
} mb; // 实例名 mb
gl_Position = mb.uMVPMatrix * vec4(aPosition, 1); // 根据总变换矩阵计算此次绘制此顶点的位置
1.9 layout 限定符
- 主要用于设置变量的存储索引(即 引用)值。
- 可以作为接口块的一部分,也可以单独修饰接口块中的成怨怒。
- 可以用于修饰被接口限定修饰符的单独变量
// 语法
// 接口限定符有 in out uniform 三种选择
<layout 限定符> <接口限定符> <变量声明>
1) layout 输入限定符
- 与 in 限定符 配合使用,用在顶点着色器中
layout (location = 0) in vec3 aPosition; //aPosition 输入变量的引用索引为0
layout (location = 1) in vec4 aColor; // aColor 输入变量的引用索引为1
2) layout 输出限定符
- 与 out 限定符 配合使用,用在片元卓i瑟琪中
// location 的值是有范围的,范围为 [0, MAX_DRAW_BUFFERS - 1],MAX_DRAW_BUFFERS 为目标硬件支持的最大绘制用缓冲数
layout (location = 0) out vec4 outColor; // 第一个输出
layout (location = 1) out vec4 outColorR; // 第二个输出
layout (location = 2) out vec4 outColorG; // 第三个输出
layout (location = 3) out vec4 outColorB; // 第四个输出
3) 一致块 layout 限定符
- 一致块可以用layout限定符,一般还需要给出所需的属性设置
- std140
- 表示一致块的内存布局基于 std140 标准
- row_major
- 表示一致块中的矩阵元素在内存中将按照行优先的顺序存放
- column_major
- 表示一致块中的矩阵元素在内存中将按照列优先的顺序存放
layout (std140, row_major) uniform MatrixBlock { // 块布局时std140,矩阵行优先
mat4 M1;
layout (column_major) mat4 uMVPMatrrix; // 该变量布局是列优先
mat4 M2;
}
1.10 流程控制
1) if-else 条件语句
if(<表达式>) { // 执行逻辑处理}
else { // 执行逻辑处理}
2) switch-case-default 条件语句
// 初始表达式必须是整型标量
switch(<初始表达式>) {
// 语句序列
case 0:
break;
...
default:
break;
}
3) while/do-while 循环
while (<条件表达式>) { // 语句序列}
do { // 语句序列 } while (<条件表达式7>)
4) for 循环
for(初始化表达式; 条件表达式; 更新语句列表) { // 语句序列 }
5) break 与 continue 循环控制
- break 在循环中中断循环
- continue 在循环中跳过本次进入下一次循环
1.11 函数的声明和使用
- 着色器语言中可以开发自定义函数
// 精度限定符可以选择 highp、 mediump、 lowp 这三种之一
// 返回类型为数组式,必须指定数组的长度
// 返回类型可以是采样器之外的任意类型
// 参数序列的参数除了可以指定类型外,还可以指定用途。常用用途如下:
// in 修饰的参数为输入参数,进攻函数接收外界传入的值,若某个参数没有明确给出用途修饰符,等同于使用in 修饰符
// out 修饰符,修饰的参数为输出参数,在函数中对输出参数赋值可以将值传递到哦调用其的外界变量中。需要注意的是在调用时不可以使用字面常量(const)
// inout 修饰的参数为输入输出参数,具有输入输出两种参数的功能。输入输出参数在调用时也不可以使用字面常量(const)。
[<精度限定符>] <返回类型> 函数名称 ([<参数序列>]) { //函数体 }
// 例(注意: 着色器内只能重载用户自定义的函数)
void pointLight(int vec4 x, out vec4 y) {}
void pointLight(int vec4 x, out ivec4 y) {}
void pointLight(int vec4 x, out vec4 y, out vec4 z) {}
1.12 片元着色器中浮点及整型变量精度的指定
- 在片元着色器中可以指定精度
// 精度有3种选择: lowp、 mediump、 highp,分别代表 低、 中、高 3种精度等级,不通一ing吉安种实现可能会有所不同
// 一般情况下使用 highp 精度
// 浮点或整型相关类型,不仅包含 float int, 还包含 与之对应的向量类型,以及与之对应的矩阵类型。
lowp float color;
in mediump vec2 Coord;
highp mat4 m;
highp ivec22 k;
- 在同一个片元着色器种浮点或整型相关类型的变量都选用同一精度,则可以指定整个着色器中相关类型的默认精度
precision <精度> <类型>;
precision mediump int;
1.13 程序的基本结构
#version 400 // 声明使用着色器语言版本
#extension GL_ARB_separate_shader_objects : enable // 启动GL_ARB_separate_shader_objects 必要扩展
#extension GL_ARB_shading_language_420pack : enable // 启动GL_ARB_shading_language_420pack 必要扩展
layout (std140, set = 0, binding = 0) uniform bufferVals { // 一致块
mat4 mvp; // 总变换矩阵
} myBufferVals; // 实例名为 myBufferVals
layout (location = 0) in vec3 pos; // 传入的物体坐标系顶点位置
layout (location = 1) in vec3 color; // 传入如的顶点颜色
layout (location = 0) in vec3 vcolor; // 传到片元着色器的顶点颜色
// 自定义函数不可以递归调用
out gl_PerVertex { // 计算顶点位置的方法
gl_Position = myBufferVals.mvp * vec4(pos, 1.0); // 计算最终顶点位置
}
void main() { // 主函数
positionShift(); // 调用计算顶点位置的方法,必须先声明
vcolor = color; // 传递顶点颜色给片元着色器
}
2. 内建变量
- 着色器中提供的用来满足特定需求的内建变量
- 内建变量不需要声明
- 一般用来实现渲染管线固定功能部分与可编辑着色器(顶点、片元、几何、曲面系分着色器)之间的信息交互
- 可分为两类: 输入变量与输出变量
- 输入变量: 负责将渲染管线中固定功能部分产生的信息传递进着色器
- 输出变量: 负责将着色器中产涩会给你的信息传递给渲染管线中的固定功能部分。
2.1 顶点着色器中的内建变量
1) 内建输入变量
- 主要有 gl_VertexID 和 gl_InstanceID,分别为当前处理顶点的整数索引以及当前处理实例的整数索引以及当前处理实例的整数索引。
- gl_VertexID
- 类型为 “heighp int” , 用来将当前被处理顶点的索引传递给顶点着色器。
- gl_instanceID
- 类型为 “heighp int”, 用来将当前被处理实例的索引传递给顶点着色器。 若不是在多实例渲染的情况下,此内建变量的值一直为0。
2) 内建输出变量
- 主要由 gl_Position 以及 gl_PointSize。 这两个变量分别用来存放处理后顶点的位置和顶点的尺寸。
- gl_Position
- 顶点着色器从渲染管线中获得原始的顶点位置数据,这些原始的顶点位置数据在顶点着色器中经过平移、旋转、缩放、摄像机观察、摄影等数学变换后,生成新的顶点位置。 新的顶点位置通过在顶点着色器中写入 gl_Position 内建变量传递到渲染管线的后继阶段继续处理。
- 类型是 vec4 , 写入的顶点位置数据爷必须与其类型一致。几乎在所有的顶点着色器中都必须对gl_Position 写入适当的值,否则后继阶段的处理结果将是不确定的。
- gl_PointSize
- 顶点着色器中可以计算一个点的大小(单位为像素),并将其赋值给 gl_PointSize(标量 float 类型)以传递给渲染管线。如果没有明确赋值的化,采用默认值 1.0。
- gl_PointSize 的值一般只有在采用了点绘制方式之后才有意义。
2.2 片元着色器中的内建变量
- 也分为输入变量和输出变量
- 输入变量包括 gl_FragCoord、 gl_FrontFacing 和 gl_PointCoord。
- 输出变量包括 gl_FragDepth,值为 片元深度值,可以对其赋值,然后送进深度缓冲参与后继计算。
- gl_FragCoord
- 内建变量gl_FragCoord(vec4 类型)含有当前片元相对于窗口位置的坐标值 x、y、z 与 1/w。 其中 x y 为片元相对窗口的二维坐标,z 为片元的深度值。
- gl_FrontFacing
- gl_FrontFacing 是一个布尔型的内建变量,通过读取该值可以判断正在处理的片元是否数与在光栅阶段生成此片元的对应图元的正面。如果属于正面值为true,反之 false。
- 一般用于开发双面光照功能相关的程序中。
- 对于点、线没有正反面的图元,默认正面。
- 对于三角形图元来说,正反面取决于应用程序中对卷绕的设置以及图元中顶点的具体卷绕情况。
- gl_PointCoord
- vec2 类型的内建变量,当启用 点精灵时, gl_PointCoord 的值表示当前图元中片元的纹理坐标, 值范围是 0.0 到 1.0。
- 如果当前图元不是一个点或者未启用点精灵,gl_PointCoord 的值是不确定的。
2.3 内建常量
- 内建常量适用于所有的着色器,用来限制每种属性高变量的数量。也就是用来规定每种自定义变量数量的最大值。
- 不同品牌、不同型号的设备,内建常量的默认值不同。下表的默认值为可能默认值的最小值。
内建常量 | 默认值 | 说明 |
---|---|---|
const mediump int gl_MaxVertexAttribs | 16 | 顶点着色器in 变量数量的最大值 |
const mediump int gl_MaxVertexUniformComponents | 1024 | 顶点着色器 uniform 修饰的 vec4 变量数量的最大值 |
const mediump int gl_MaxVertexOutputComponents | 64 | 顶点着色器 out 修饰的 vec4 变量数量的最大值 |
const mediump int gl_MaxVertexTextureImageUnits | 16 | 顶点着色器中科利用的纹理单元数量的最大值 |
const mediump int gl_MaxCombinedTextureImageUnits | 80 | 顶点着色器和片元着色器中可利用的 纹理单元数量的最大值的总和 |
const mediump int gl_MaxTextureImageUnits | 16 | 片元着色器中可利用的纹理图像单元数量的最大值 |
const mediump int gl_MaxFragmentInputComponents | 128 | 片元着色器 in 变量数量的最大值 |
const mediump int gl_MaxFragmentUniformComponents | 1024 | 片元着色器 uniform 修饰的vec4 变量数量的最大值 |
const mediump int gl_MaxDrawBuffers | 8 | 片元着色器中多重渲染目标数量的最大值 |
3. 内置函数
- 着色器提供了很多内置函数,这些函数大多已经被重载,一般四种变体,分别用来接收和返回 genType,genIType,genUType 以及 genBType 类型的值。
变体类型 | 说明 | 变体类型 | 说明 |
---|---|---|---|
genType(浮点) | flloat, vec2, vec3, vec4 | genUType(无符号整型) | uint, uvec2, uvec3, uvec4 |
genIType(整型) | int, ivec2, ivec3, ivec4 | genBType(布尔类型) | bool, bvec2, bvec3, bvec4 |
- 内置函数通常以最有方式实现,有部分函数甚至由硬件直接支持。
- 大部分内置函数同时适用于顶点、片元、集合、曲面细分等着色器,也有部分只适用于顶点或者片元着色器
- 内置函数按照设计目的可以分为3个类别
- 提供独特硬件功能的访问接口,如纹理采样系列函数,用户无法自行开发。
- 简单的数学函数,内置函数相比自己开发效率更高,是厂商根据硬件的特点用最高效的方式实现的。
- 一些复杂的函数,如三角函数等,用户可以自己编写,编写过程繁琐。主流硬件往往都有将进行这些计算的专用指令,更高效。
3.1 角度转换与三角函数
- 适用于多种着色器,且每个角度转换与三角函数都有4种重载变体
内置函数签名 | 说明 |
---|---|
genType radians(genType degrees) | 将角度转为弧度, result =(Π / 180) * degrees |
genType degrees(genType radians) | 将弧度转换为角度, result = (Π / 180) * radians |
genType sin(genType angle) | 求正弦值,返回 [-1, 1], angle 以弧度为单位 |
genType cos(genType angle) | 求余弦值 |
genType tan(genType angle) | 求正切值 |
genType asin(genType x) | 求反正弦值 [-Π/2, Π/2],x取值[-1, 1] ,x 绝对值大于1, 结果不确定 |
genType acos(genType x) | 求反余弦值 [0, Π] ,x 绝对值大于1, 结果不确定 |
genType atan(genType y, genType x) | 求反正切函值 [-Π, Π] ,x y 全为0, 返回值不确定 |
genType atan(genType y_over_x) | 求反正切值 [-Π/2, Π/2] , y_over_x 不存在范围限制 |
genType sinh(genType x) | 双曲正弦函数,返回 ( e x − e − x ) / 2 ( e^x - e^{-x} )/2 (ex−e−x)/2 x不存在范围限制 |
genType cosh(genType x) | 双曲余弦函数,返回 ( e x + e − x ) / 2 (e^x + e^{-x}) / 2 (ex+e−x)/2 x不存在范围限制 |
genType tanh(genType x) | 双曲正切函数, 返回 sinh(x)/consh(x) ,双曲正切函数由 双曲正弦函数和双曲余弦函数推出 |
genType asinh(genType x) | 反双曲正先函数,双曲正弦函数的反函数 |
genType acosh(genType x) | 反双曲余弦函数。返回值为其非负部分, x< 1, 返回值不确定 |
genType atanh(genType x) | 反双曲正切函数, 若 |x| ≥ 1,返回值不确定 |
3.2 指数函数
- 适用于多种着色器
内置函数签名 | 说明 |
---|---|
genType pow(genType x, genType y) | 返回x 的y 次方,x 小于0,返回值不确定, x值等于0 并且y值小于等于0,返回值不确定 x y x^y xy |
genType exp(genType x) | 返回e(约等于2.718281828) 的 x 次方,即 e x e^x ex |
genType log(genType x) | 返回e 为底的x 的对数(x 小于0,返回值不确定),即 l o g e ( x ) log_e(x) loge(x) 如果返回值为y,那么也满足方程 x = e y x = e^y x=ey |
genType exp2(genType x) | 返回 2 的 x 次方,即 2 x 2^x 2x |
genType log2(genType x) | 返回 2 为底的x 的对数,即 l o g 2 ( x ) log_2(x) log2(x) |
genType sqrt(genType x) | 返回x 的平方根 x \sqrt{x} x |
genType inversesqrt(genType x) | 返回x 正平方根的倒数 1 x \cfrac 1 {\sqrt{x}} x1 |
3.3 常见函数
内置函数签名 | 说明 |
---|---|
genType abs(genType x) | 求绝对值 |
genType sign(genType x) | 与0 进行比较,返回 1.0 、0 、 -1.0 |
genType floor(genType x) | 返回小于或者等于x 的最大整数值 |
genType trunc(genType x) | 截取并返回x 整数部分 |
genType round(genType x) | 四舍五入 |
genType roundEven(genType x) | 偶四舍五入,例如 3.5 和 4.5 都返回 4.0 |
genType ceil(genType x) | 返回大于等于x的最小整数 |
genType fract(genType x) | 返回 x - floor(x) |
genType mod(genType x, float y) | 取模运算 |
genType mode(genTypex x, genType y) | 取模运算 |
genType modf(genType x, out genType i) | 返回x 的小数部分,并将x 的整数部分存入输出变量i。 同时返回值以及输出变量i的符号与x相同 |
genType min(genType x, genType y) | 获得x 和y 中的最小值 |
genType miiiiin(genType x, float y) | 获得 x 和y 中的最小值 |
genType clamp(genType x, genType minVal, genType maxVal) | 返回 min(max (x, minVal), maxVal) |
genType clamp(genType x, float minVal, float maxVal) | 返回 min(max (x, minVal), maxVal) |
genType mix(genType x, genType y, genType a) | 使用因子 a 对 x 与 y 执行西安行混合,即 返回 x ∗ ( 1 − a ) + y ∗ a x^*(1 - a) + y^*a x∗(1−a)+y∗a |
genType mix(genType x, genType y, float a) | 使用因子 a 对 x 与 y 执行西安行混合,即 返回 x ∗ ( 1 − a ) + y ∗ a x^*(1 - a) + y^*a x∗(1−a)+y∗a |
genType mix(genType x,genType y, genBType a) | 使用参数 a 的布尔值选择返回参数 x 或 y 的值 |
genType step(genType edge, genType x) | 通过 x 与 edge 比较返回相应的值 |
genType step(float edge, genType x) | 通过x 与 edge 比较返回相应的值 |
genType smoothstep(genType edge0, genType edge1, genType x) | 通过 x 与 edge0、edge1 比较返回相应的值 |
genType smoothstep(float edge0, float edge1, genType x) | 通过 x 与 edge0、edge1 比较返回相应的值 |
genBType isnan(genType x) | 判断参数 x 是否为NaN,是返回 true |
genBType isinf(genType x) | 判断x 是否为正无穷或负无穷 |
genType floatBitsToInt(genType value) | 将表示浮点的比特序列看作表示整数的比特序列, 并将对应的整数返回 |
genUType floatBitsToUint(genType value) | 将表示浮点的比特序列看作表示整数的比特序列, 并将对应的整数返回 |
genType intBitsToFloat(genType value) | 将表示整数的比特序列看昨表示附带念书的比特序列 |
genType uintBitsToFloat(genUType value) | 将表示整数的比特序列看昨表示附带念书的比特序列 |
float t; // 声明变量,用来存储平滑过渡中的值
t = clamp ( ( x - edge0 ) / (edge1 - edge0 ), 0.0, 1.0); // 计算x位置对edge1 与 edge0 之间的线性插值
return t*t*(3.0 - 2.0*t); // 产生对应此x 位置的平滑过渡值
3.4 几何函数
内置函数签名 | 说明 |
---|---|
float length(genType x) | 返回 向量x 的长度 x [ 0 ] ∗ x [ 0 ] + x [ 1 ] ∗ x [ 1 ] + . . . \sqrt{ x[0]^*x[0] + x[1]^*x[1] + ... } x[0]∗x[0]+x[1]∗x[1]+... |
float distance(genType p0, genType p1) | 返回 p0 与 p1 之间的距离 即 length(p0 - p1) |
float dot(genType x, genType y) | 返回向量x 与y 的点积, 即 x [ 0 ] ∗ y [ 0 ] + x [ 1 ] ∗ y [ 1 ] + x [ 2 ] ∗ y [ 2 ] + . . . x[0]^*y[0] + x[1]^*y[1] + x[2]^*y[2] + ... x[0]∗y[0]+x[1]∗y[1]+x[2]∗y[2]+... |
vec3 cross(vec3 x, vec3 y) | 返回向量 x 与 y 的叉积,即: [ x [ 1 ] ∗ y [ 2 ] − y [ 1 ] ∗ x [ 2 ] x [ 2 ] ∗ y [ 0 ] − y [ 2 ] ∗ x [ 0 ] x [ 0 ] ∗ y [ 1 ] − y [ 0 ] ∗ x [ 1 ] ] \begin{bmatrix} x[1]^*y[2] - y[1]^*x[2] \\ x[2]^*y[0] - y[2]^*x[0] \\ x[0]^*y[1] - y[0]^*x[1] \end{bmatrix} x[1]∗y[2]−y[1]∗x[2]x[2]∗y[0]−y[2]∗x[0]x[0]∗y[1]−y[0]∗x[1] |
genType normalize(genType x) | 返回与向量 x 方向相同,并且长度为 1 的向量,对向量进行规格化 |
genType faceforward(genType N, genType I, genType Nref) | 根据 dot(Nref, I) 的值返回相应的值,如果 do(Nref, I) < 0, 则返回N,否则返回 -N |
genType reflect(genType I, genType N) | 根据传入的入射向量I 以及表面法向量 N , 返回反射方向的向量 |
genType refract(genType I, genType N, float eta) | 根据传入的入射向量I 、 表面法向量 N 以及折射系数 eta, 返回折射向量。 |
3.5 矩阵函数
- 主要包括生成矩阵、矩阵转置 、求矩阵行列式、求逆矩阵
内置函数签名 | 说明 |
---|---|
mat matrixCompMult( mat x, mat y) | 按各个部分将矩阵 x 与 矩阵 y 相乘,即 返回值 result[i][j] 是 x[i][j] 与 y[i][j]标量的乘积 |
mat2 outerProduct(vec2 c, vec2 r) mat3 outerProduct(vec3 c, vec3 r) mat4 outerProduct(vec4 c, vec4 r) mat2x3 outerProduct(vec3 c, vec2 r) mat3x2 outerProduct(vec2 c, vec3 r) mat2x4 outerProduct(vec4 c, vec2 r) mat4x2 outerProduct(vec2 c, vec4 r) mat3x4 outerProduct(vec4 c, vec3 r) mat4x3 outerProduct(vec3 c, vec4 r) | 将参数c 和参数r 分别看成只有一列的矩阵和只有一行 的矩阵,并将其进行线性矩阵乘积,产生一个新的矩阵 |
mt2 transpose(mat2 m) mat3 transpose(mat3 m) mat4 transpose(mat4 m) mat2x3 transpose(mat3x2 m) mat3x2 transpose(mat2x3 m) mat2x4 transpose(mat4x2 m) mat4x2 transpose(mat2x4 m) mat3x4 transpose(mat4x3 m) mat4x3 transpose(mat3x4 m) | 返回参数矩阵 m 的转置矩阵,原始m矩阵不变 |
float determinant(mat2 m) float determinant(mat3 m) float determinant(mat4 m) | 返回参数矩阵m 的行列式 |
mat2 inverse(mat2 m) mat3 inverse(mat3 m) mat4 inverse(mat4 m) | 返回矩阵 m 的逆矩阵, 原始矩阵 m 不变 |
3.6 向量关系函数
内置函数签名 | 说明 |
---|---|
bvec lessThan(vec x, vec y) bvec lessThan(ivec x, ivec y) bvec lessThan(uvec x, uvec y) | 返回向量 x 与向量 y 中的各个分量 执行 x < y 的结果 |
bvec lessThanEqual(vec x, vec y) bvec lessThanEqual(ivec x, ivec y) bvec lessThanEqual(uvec x, uvec y) | 返回向量 x 与向量 y 中的各个分量执行 x <= y 的结果 |
bvec greaterThan(vec x, vec y) bvec greaterThanEqual(ivec x, ivec y) bvec greaterThanEqual(uvec x, uvec y) | 返回向量 x 与向量 y 中的各个分量执行 x > y 的结果 |
bvec greaterThanEqual(vec x, vec y) bvec greaterThanEqual(ivec x, ivec y) bvec greaterThanEqual(uvec x, uvec y) | 返回向量 x 与向量 y 中的各个分量执行 x >= y 的结果 |
bvec equal(vec x, vec y) bvec equal(ivec x, ivec y) bvec equal(bvec x, bvec y) bvec equal(uvec x, uvec y) | 返回向量 x 与向量 y 中的各个分量 执行 x == y 的结果 |
bvec notEqual(vec x, vec y) bvec notEqual(ivec x, ivec y) bvec notEqual(bvec x, bvec y) bvec notEqual(uvec x, uvec y) | 返回向量 x 与向量 y 中的各个分量 执行 x != y 的结果 |
bool any(bvec x) | 如果x 中任何一个分量为 true , 返回 true |
bool all(bvec x) | x 中所有组成元素都为 true, 返回 true |
bool not(bvec x) | 对于 x 中各个分量执行的逻辑非运算 |
3.7 纹理采样函数
- sampler 为指定纹理的采样器
- P 为纹理坐标
- lod 为细节级别
- bias 为偏移值
- gvec4 中的 g 表示占位符,可以表示 vec4、ivec4、uvec4
- 投影采样相关功能与函数 textureProj 相同
- 偏移采样相关功能与函数textureOffset 相同
- dPdx 为 P 对窗口 x 坐标的偏导数
- dPdy 为 P 对窗口 y 坐标的偏导数
- 渐变采样相关功能与函数 textureGrad 相同
内置函数签名 | 说明 |
---|---|
highp ivec2 textureSize(gsampler2D sampler, int lod) highp ivec2 textureSize(gsampler3D sampler, int lod) highp ivec2 textureSize(gsamplerCube sampler, int lod) highp ivec2 textureSize(sampler2DShadow sampler, int lod) highp ivec2 textureSize(gsamplerCubeShadow sampler, int lod) highp ivec3 textureSize(gsampler2DArray sampler, int lod) highp ivec3 textureSize(gsampler2DArrayShadow sampler, int lod) | 根据参数smapler 的细节级别,按顺序返回纹理 的宽度,高度以及深度。对于 Array 格式,返回 值的最后一个元素为纹理数组的层数。 |
gvec4 texture(gsampler2D sampler, vec2 P[, float bias]) gvec4 texture(gsampler3D sampler, vec3 P[, float bias]) gvec4 texture(gsamplerCube sampler, vec3 P[, float bias]) gvec4 texture(gsampler2DArray sampler, vec3 P[, float bias]) | 纹理坐标 P 在 sampler 参数 指定的纹理坐标执行纹理采样 |
float texture(sampler2DShadow sampler, vec3 P[, float bias] float texture(samplerCubeShadow sampler, vec4 P[, float bias] float texture(sampler2DArrayShadow sampler, vec4 P[, float bias] | 执行阴影类纹理采样 |
gvec4 textureProj(gsampler2D sampler, vec3 P[,float bias] gvec4 textureProj(gsampler2D sampler, vec4 P[,float bias] gvec4 textureProj(gsampler3D sampler, vec4 P[,float bias] float textureProj(sampler2DShadow sampler, vec3 P[,float bias] | 执行投影纹理采样。采样时的纹理坐标为 P 参数 的前几个分量(不含最后一个)分别除以最后 一个分量所得 |
gvec4 textureOffset(gsampler2D sampler, vec2 P, ivec2 offset [, float bias]) gvec4 textureOffset(gsampler3D sampler, vec3 P, ivec3 offset [, float bias]) gvec4 textureOffset(sampler2DShadow sampler, vec3 P, ivec2 offset [, float bias]) gvec4 textureOffset(gsampler2DArray sampler, vec3 P, ivec2 offset [, float bias]) | 使用偏移纹理坐标进行纹理采样 |
gvec4 texelFetch(gsampler2D sampler, ivec2 P, int lod) gvec4 texelFetch(gsampler3D sampler, ivec3 P, int lod) gvec4 texelFetch(gsampler2DArray sampler, ivec3 P, int lod) | 使用整型纹理坐标 P 在 sampler 参数 指定的纹理中执行纹理采样 |
gvec4 texelFetchOffset(gsampler2D sampler, ivec2 P, int lod, ivec2 offset) gvec4 texelFetchOffset(gsampler3D sampler, ivec3 P, int lod, ivec3 offset) gvec4 texelFetchOffset(gsampler2DArray sampler, ivec3 P, int lod, ivec2 offset) | 使用整型偏移纹理坐标在 sampler 参数 指定的纹理中获取对应坐标处的纹素。 |
gvec4 textureProjOffset(gsampler2D sampler, vec3 P, ivec2 offset [, float bias]) gvec4 textureProjOffset(gsampler2D sampler, vec4 P, ivec2 offset [, float bias]) gvec4 textureProjOffset(gsampler3D sampler, vec4 P, ivec2 offset [, float bias]) float textureProjOffset(sampler2DShadow sampler, vec4 P, ivec2 offset [, float bias]) | 执行投影偏移纹理采样 |
gvec4 textureLodOffset(gsampler2D sampler, vec2 P, float lod, ivec2 offset) gvec4 textureProjOffset(gsampler3D sampler, vec3 P, float lod, ivec3 offset) float textureLodOffset(sampler2DShadow sampler, vec3 P, float lod, ivec2 offset) float textureProjOffset(gsampler2DArray sampler, vec3 P,float lod, ivec2 offset) | 执行投影偏移纹理采样 |
gvec4 textureProjLod(gsampler2D sampler, vec2 P, float lod) gvec4 textureProjLodtextureProjLod(gsampler2D sampler, vec4 P, float lod) float textureProjLod(gsampler3D sampler, vec4 P, float lod) float textureProjLod(sampler2DShadow sampler, vec4 P, float lod) | 执行投影偏移纹理采样 |
gvec4 textureProjLodOffset(gsampler2D sampler, vec3 P, float lod, ivec2 offset) gvec4 textureProjLodOffset(gsampler2D sampler, vec4 P, float lod, ivec2 offset) gvec4 textureProjLodOffset(gsampler3D sampler, vec4 P, float lod, ivec3 offset) float textureProjLodOffset(sampler2DShadow sampler, vec4 P, float lod, ivec2 offset) | 执行指定细节级别的投影偏移纹理采样 |
gvec4 textureGrad(gsampler2D sampler, vec2 P, vec2 dPdx, vec2 dPdy) gvec4 textureGrad(gsampler3D sampler, vec3 P, vec3 dPdx, vec3 dPdy) gvec4 textureGrad(gsamplerCube sampler, vec3 P, vec3 dPdx, vec3 dPdy) gvec4 textureGrad(gsampler2DArray sampler, vec3 P, vec2 dPdx, vec2 dPdy) | 执行纹理渐变采样 |
float textureGrad(sampler2DShadow sampler, vec3 P, vec2 dPdx, vec2 dPdy) float textureGrad(samplerCubeShadow sampler, vec4 P, vec3 dPdx, vec3 dPdy) float textureGrad(sampler2DArrayShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy) | 执行纹理阴影类型纹理渐变采样 |
gvec4 textureGradOffset(gsampler2D sampler, vec2 P, vec2 dPdx, vec2 dPdy, ivec2 offset) gvec4 textureGradOffset(gsampler3D sampler, vec3 P, vec3 dPdx, vec3 dPdy, ivec3 offset) float textureGradOffset(sampler2DShadow sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset) gvec4 textureGradOffset(gsampler2DArray sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset) gvec4 textureGradOffset(sampler2DArrayShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset) | 执行纹理渐变偏移采样 |
gvec4 textureProjGrad(gsampler2D sampler, vec3 P, vec2 dPdx, vec2 dPdy) gvec4 textureProjGrad(gsampler2D sampler, vec4 P, vec2 dPdx, vec2 dPdy) gvec4 textureProjGrad(gsampler3D sampler, vec4 P, vec3 dPdx, vec3 dPdy) float textureProjGrad(sampler2DShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy) | 执行投影纹理渐变采样 |
gvec4 textureProjGradOffset(gsampler2D sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset) gvec4 textureProjGradOffset(gsampler2D sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset) gvec4 textureProjGradOffset(gsampler3D sampler, vec4 P, vec3 dPdx, vec3 dPdy, ivec3 offset) gvec4 textureProjGradOffset(sampler2DShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset) | 执行投影纹理渐变偏移采样 |
3.8 微分函数
- 仅能用于 片元着色器,主要包括 dFdxz 方法、dFdy 方法以及 fwidth 方法
内置函数签名 | 说明 |
---|---|
genType dFdx(genType p) | 返回参数 p 在 x 方向的偏导数 |
genType dFdx(genType p) | 返回参数 p 在 y 方向的偏导数 |
genType fwidth(genType p) | 返回参数 p 在 x 与 y 方向偏导数的绝对值之和 abs (dFdx ( p) + abs(dFdy (p))x |
3.9 浮点数打包与解包函数
内置函数签名 | 说明 |
---|---|
highp uint packUnorm2x16(vec2 v) | 首先将二维向量 v 中的每个规格化浮点数分量转换为 16 比特的整数,然后两个 16 比特的整数被打包城一个 32 比特的无符号整数返回。 r o u n d ( c l a m p ( c , 0 , + 1 ) ∗ 65535.0 round(clamp(c, 0, +1) * 65535.0 round(clamp(c,0,+1)∗65535.0 |
highp vec2 unpackUnorm2x16(highp uint p) | 将一个32位无符号的整数 p 解包成一对 16 位的无符号整数,并将每一个分量转换成规格化的浮点值,生成一个 vec2 并返回 |
highp uint packUnorm4x8(highp uint p) | 首先将四维向量 v 中的每个规格化浮点数分量转换成 8 比特的整数,然后四个8比特的整数被打包成一个32 比特的无符号整数返回。 r o u n d ( c l a m p ( c , 0 , + 1 ) ∗ 255.0 ) round(clamp(c, 0, +1 ) * 255.0) round(clamp(c,0,+1)∗255.0) |
highp vec4 unpackUnorm4x8(highp uint p) | 将一个 32 位无符号的整数 p 解包成四个 8 位的无符号整数,并将每一个分量转换成规格化的浮点数,生成一个 vec4 并返回。 可以理解位packUnorm4x8 函数的逆操作 |
highp uint packSnorm4x8(vec4 v) | 首先将四维向量 v 中的每个规格化浮点数分量转为 8 比特的整数,然后四个 8 比特的整数被打包成一个32 比特的无符号整数返回。 r o u n d ( c l a m p ( c , − 1 , + 1 ) ∗ 127.0 ) round(clamp(c, -1, +1 ) * 127.0) round(clamp(c,−1,+1)∗127.0) |
highp vec4 unpackSnorm4x8(highp uint p) | 将一个32 位无符号的整数 p 解包成四个 8 位的无符号整数,并将每一个分量转换成规格化的浮点值,生成一个 vec4 并返回 |
highp double packDouble2x32(uvec2 v) | 首先将二维向量 v 中的每个规格化浮点数分量转换为 32 比特的浮点数,然后两个32 比特的浮点数被打包成一个 64 比特的浮点数返回。 |
highp uvec2 unpackDouble2x32(double p) | 将一个 64 比特的浮点数 p 解包成两个 32 位的浮点数,并将每一个分量转换成规格化的浮点值,生成一个 uvec2 并返回。 |
4. invariant 修饰符避免值变问题
- 值变问题是指 在同样的着色器程序多次运行时,同一个表达式在同样输入值的情况下多次运行,结果不精确一致的情况。
- 大部分情况下值变不影响最终效果的正确性。
- 以下几种情况可以使用 invariant
- 顶点着色器中的内建输出变量,如 gl_Position
- 顶点着色器中声明的以 out 修饰符修饰的变量
- 片元着色器中内奸的输出变量
- 片怨怒着色器中声明的以 out 修饰符修饰的变量
- 某些特殊情况需要避免值变问题(很少),主要有两种方式
- 在声明便力量时加上 invariant 修饰符
- 对已经声明的变量补充使用 invariant 修饰符进行修饰
// 此部分代码只能位于着色器程序的前面,且不能在片元着色器中使用
// 方式一
invariant out vec3 color;
// 方式二
out vec3 color;
invariant color;
// 所有的输出变量都是 invariant
#pragma STDGL invariant (all)
5. 预处理器
- 在真正的编译开始之前由编译器调用的独立程序
- 处理编译过程中所需的源字符串
- 遵循 C/C++ 语言预处理器的规则,宏定义和条件测试等可以通过预处理指令执行。
预处理器 | 说明 |
---|---|
# | 预处理 |
#define | 定义宏 |
#define | 删除事先定义的宏 |
#if | 条件测试 |
#ifdef | 条件测试,是否定义 |
#ifndef | 条件测试,是否未定义 |
#else | 条件测试 |
#elif | 条件测试 |
#endif | 条件测试,结束编译块的控制 |
#error | 将诊断信息保存到着色器对象的信息日志中 |
#pragma | 允许依赖于实现的编译控制。如默认情况下 开启着色器优化 #pragram optimize(off) |
#extension | 激活指令的扩展行为 |
#line | #line 后面是整型常量表达式,表示从其开始的起始行号 |
- 着色器还定义了一些可以直接使用的宏
- 所有以
GL_
为前缀的宏名和包含两个连续下划线__
的宏名称都是着色器语言保留的。 - 重定义内建宏名和预定义宏名是错误的
预定义宏 | 说明 |
---|---|
__LINE__ | 当前按被编译代码行的行号,十进制整数 |
__FILE__ | 当前被处理的源代码字符串符号,十进制整数 |
__VERSION__ | 用来替代着色器语言的版本号 |
- 与C++ 类似,如果定义了宏没有给出其替代表达式,并不会默认其替代表达式为“0”
- 预定义表达式在编译时执行,可操作符如下:
优先级 | 操作符类型 | 操作符 | 结合性 |
---|---|---|---|
1(最高) | () | NA | |
2 | 一元操作符 | define + - ~ ! | 从右到左 |
3 | 乘、除、取余 | * / % | 从左到右 |
4 | 加、减 | + - | 从左到右 |
5 | 比特左/右移 | << >> | 从左到右 |
6 | 比较操作符 | < > <= >= | 从左到右 |
7 | 等于/不等于操作符 | == != | 从左到右 |
8 | 比特与 | & | 从左到右 |
9 | 比特异或 | ^ | 从左到右 |
10 | 比特或 | | | 从左到右 |
11 | 逻辑与 | && | 从左到右 |
12(最低) | 逻辑或 | || | 从左到右 |
- 着色器语言的编译器必须反馈不符合规范的编译时词法和语法错误,同时任何扩展行为必须先启用才能使用
- 控制编译器使用的扩展行为 通过
#extension
指令实现
#extension <扩展名>:<扩展行为>
- 扩展名为各个硬件厂商提供的特殊功能扩展的名称,使用时查阅厂商提供资料
- 编译器的初始状态为
#extension all:disable
,意味着编译器关闭任何扩展 - 主要包含以下类型:
扩展行为 | 主要的扩展行为 |
---|---|
require | 说明需要指定扩展名的扩展,如果编译器不支持指定扩展名对应的扩展,或扩展名为 all,则反馈错误 |
enable | 启用指定扩展名的扩展,如果编译器不支持扩展名对应的扩展 ,则给出警告。如果扩展名为all ,则反馈错误 |
warn | 检测是否使用了指定名称的扩展,如果使用了则给出警告。如果为all 则检测是否使用了任何扩展,若使用了给出警告。如果编译器不支持此扩展,则给出警告 |
disable | 金庸指定扩展名的扩展,如果编译器不支持指定扩展名,则给出警告。如果为all,则禁用所有使用的扩展回复到默认核心版本 |