1. GLSL ES介绍
使用WebGL渲染三维图形核心是绘制图形的着色器程序,GLSL就是专门编写着色器程序的编程语言。它与C语言有很多相似之处;简单列举一下它的特性
- 是一种 强类型语言,赋值时等号左右两侧的数据类型必须一致,否则报错,定义函数时必须指定函数的返回值类型
- 对大小写敏感
- 每个语句都以英文分号结束
- 程序自上而下逐行执行
2. 变量定义
与其他编程语言一样,从变量的定义开始,定义GLSL ES 语言的变量,需要遵循以下原则:
- 变量名只能包含英文字符、数字和下划线即(a-z、A-Z、0-9、_)
- 变量名的首字母不能是数字
- 不能以 gl_ 、webgl_ 或 webgl 开头,这些前缀已经被OpenGL ES 保留了
- 不能是GLSL ES内置的关键字和保留的关键字,具体如下
3. 变量类型
GLSL ES中变量类型总体分为基本类型(布尔类型、整形、浮点型)、矢量类型、矩阵类型、结构体类型、数组类型和取样器类型
3.1 基本类型
3.1.1 基本类型的介绍
3.1.2 基本类型的赋值和类型转换
之前介绍过GLSL ES 是强类型的语言赋值时等号左右类型必须相同,接下来看看具体使用
通过以上例子发现不同类型无法直接赋值,不过我们可以使用内置函数int()、float() 和bool()来强制将转换成对应的类型,例如
3.2 矢量
3.2.1 矢量类型介绍
3.2.2 矢量构造和赋值
先把v2的元素填充进来,如果还没有填充满,继续使用第二个参数v4里的元素填充
注:如果向构造函数中只传一个参数,构造函数会自动将这个参数赋值所有元素,但是如果给构造函数传了不止一个值,但又比所需的参数个数少,就会报错。例如向vec3中传入两个参数就会报错
3.3 矩阵
3.3.1 矩阵类型介绍
3.3.2 矩阵构造和赋值
在学习矩阵构造和赋值前先搞明白两个概念,按行主序和按列主序
在矩阵中像上图一样按水平一行一行排列的矩阵称为按行主序
在矩阵中像上图一样按垂直方向上一列一列的排列的矩阵称为按列主序
我们在WebGL中使用的矩阵都是 按列主序 的,
例如你用构造函数创建一个4×4的矩阵时
创建的矩阵是按这样的顺序排列的
创建矩阵示例
注:与矢量的构造函数类似,如果传入参数数量大于1但又没有达到矩阵要求的数量,就会报错。例如向mat4中传入三个参数就会报错,因为mat4构造函数需要传入16个元素
3.4 结构体
与C语言类似,GLSL ES 也支持用户自定义数据类型,这种类型就是 结构体,定义结构体是用关键字 struct
3.4.1 定义结构体
实际开发中,有时候需要将很多基本类型的变量聚合在一起,例如一个光源包括位置、颜色等属性,于是可以使用结构体自定义类型,以下代码是简单定义Light结构体类型的示例
3.4.2 结构体构造和赋值
结构体有自己的构造函数,构造函数的名称与结构体名称一样,构造结构体时需要注意,构造函数的参数顺序必须与结构体定义成员的顺序一致
3.4.3 结构体成员访问
结构体的成员通过 点运算符 (.) 来访问,只需要在结构体变量后面加上点运算符再加上成员变量名即可
3.4.4 结构体支持的运算符
结构体成员支持自身类型支持的任何运算符,但是结构体本身支持两种运算符,赋值(=)和比较(==或!=)。需要注意,含有数组或纹理类型成员的结构体不适合进行赋值和比较运算,当结构体进行比较运算时,当且仅当两个结构体变量所对应的所有成员都相等时,==运算符才会返回true
3.5 数组
GLSL ES 语言同样支持数组类型,数组类型相比结构体类型,声明和访问要容易一些
3.5.1 数组声明
声明数组很简单,只需要在变量名后面加上中括号([]) 和数组的长度,例如
使用变量定义数组长度时,该变量必须使用const限定字修饰,例如
注意,与JavaScript和C语言不同,数组不能在声明时一次性的完成初始化,必须显示的对每个元素进行初始化
3.5.2 访问数组元素
数组元素可以通过索引值来访问,与其他语言一样索引也是从0开始
3.5.2 数组支持的运算符
数组本身只支持[]运算符,但数组元素支持自身类型所支持的任意运算符
3.6 取样器
GLSL ES 支持一种特殊的内置类型称为 取样器(sampler) ,使用该类型的变量可以访问纹理,常用的取样器类型:sampler2D和samplerCube 分别用于2D纹理取样和盒纹理取样。GLSL ES 提供了内置函数texture2D,通过texture2D函数,可以从纹理中提取纹素,还需要注意一下,取样器变量只能是uniform变量。接下来看看使用示例
实例中的片元着色器部分,通过uniform sampler2D u_Sampler; 语句声明了取样器变量u_Sampler,唯一可以传给该取样器的就是纹理单元编号,而且只能使用WebGL中的gl.uniform1i()传值,传值过程如下: