GLSL ES
# GLSL ES概述
GLSL ES
编程语言是在OpenGL
着色器语言的基础上,删除和简化一部分功能后形成的。GLSL ES
的目标平台是消费电子产品或嵌入式设备,如智能手机或游戏主机等。
GLSL ES的语法与C语言的较为类似。
注意
- 程序是大小写敏感的(marina和Marina不同)
- 每个语句应该以;结束
- 着色器程序必须有且只有一个main函数,而且不接收任何参数
- 与js不同,函数必须有返回值,如果无返回值需要用void标识
- 添加注释与js相同 // 单行注释 /* */ 多行注释
- 是强类型语言,声明变量时需要指定数据类型,赋值的时候,=左右两侧的数据类型也必须一样,否则就会出错
# 数据值类型
GLSL 支持两种数字值类型
- 数值类型:GLSL ES支持整型数和浮点数。没有小数点.的数被认为是整型数,而有小数点的值则被认为是浮点数。
- 布尔值类型:GLSL ES支持布尔值类型,包括true和false两个布尔常量。
GLSL基本类型
- float,单精度浮点数类型,表示一个单精度浮点数
- int,整型数,表示一个整数
- bool,布尔值,表示一个布尔值(true或false)
类型转换
float(int)将整型数转为浮点数(比如将8转换为8.0)
float(bool)true被转换为1.0,false被转换为0.0
int(float)将浮点数的小数部分删去,转换为整型数(比如3.14转换为3)
int(bool)true转换为1,false转换为0
bool(int)0被转换为false,其他非0值被转换为true
bool(float)0.0被转换为false,其他非0值被转换为true
# 运算符
- 取负 -
- 乘法 *
- 除法 /
- 加法 +
- 减法 -
- ++ 自增
- -- 自减
- = 赋值
- += -= *= /= 算数赋值
- < > <= >= 比较
- == != 比较是否相等
- ! 取反
- && 逻辑与
- || 逻辑或
- ^^ 逻辑异或
- condition? expression1:expression2 三元选择
# 变量
- 只包括a-z,A-Z,0-9和下划线_
- 变量名首字母不能是数字
- 不能是关键字,也不能是保留字
- 不能以gl_、webgl_或_webgl_开头,这些前缀已经被OpenGL ES保留了
# 矢量和矩阵
GLSL ES支持矢量和矩阵类型,这两种数据类型很适合用来处理计算机图形。矢量可以表示点的坐标或颜色,矩阵可以表示变换。
- vec2、vec3、vec4 具有2、3、4个浮点数元素的矢量
- ivec2、ivec3、ivec4 具有2、3、4个整型数元素的矢量
- bvec2、bvec3、bvec4 具有2、3、4个布尔值元素的矢量
- mat2、mat3、mat4 2x2、3x3、4x4的浮点数元素的矩阵(分别具有4、9、16个元素)
# 构造函数
这种专门创建指定类型的变量的函数被称为构造函数(constructor functions),构造函数的名称和其创建的变量的类型名称总是一致的。 下面的构造函数是正确的
- vec3 v3 = vec3(1.0, 0.0, 0.5);
- vec2 v2 = vec2(v3); //构造函数自动忽略v3的第三个分量
- vec4 v4 = vec4(1.0); //构造函数自动填充后面的分量为vec4(1.0,1.0,1.0,1.0); vec4 v4 = vec4(1.0, 1.0); 这样是错误的,vec4(1.0, 1.0,1.0); 这样也是错误的
- vec4 v4b = vec4(v2, v4); //先用v2填充,不够的话剩下的分量用v4的填充
矩阵构造函数使用的方法和矢量构造函数的使用方式很类似。但是要保证存储在矩阵中的元素是按照列主序排列的
- mat4 m4 = mat4(16个浮点数);
- vec2 v2_1 = vec2(1.0,3.0);vec2 v2_2 = vec2(2.0,4.0);mat2 m2_1 = mat2(v2_1,v2_2); 使用两个矢量生成一个矩阵
- ve4 v4 = vec4 (1.0,2.0,3.0,4.0);mat2 m2_2 = mat2(v4); 使用一个vec4对象创建一个mat2对象
- mat2 m2 = mat2(1.0,3.0,v2_2); 使用两个浮点数和一个vec2对象来创建mat2对象
- mat4 m4 = mat4(1.0); 生成一个对角线上是1.0,其余是0.0的矩阵
- 与矢量构造函数类似,如果传入的数值的数量大于1,又没有达到矩阵元素的数量,就是出错。 mat4 m4 = mat4(1.0,2.0,3.0)错误,mat4需要16个元素
# 访问矢量元素
为了访问矢量或者矩阵中的元素,可以使用.或[]运算符。
# 矢量运算符
在矢量变量名后接点运算符(.),然后接上分量名,就可以访问矢量的元素了。
x,y,z,w 用来获取顶点坐标分量
r,g,b,a 用来获取颜色分量
s,t,p,q 用来获取纹理坐标分量
[*] 乘法 适用于vec[234]和mat[234]
[/] 除法
[+] 加法
[-] 减法
++ 自增
-- 自减
= 赋值
+=,-=
*=, /= 运算赋值,当运算赋值操作作用于矢量或矩阵时,实际上是逐分量地对矩阵或矢量的每一个元素进行独立的运算赋值。
== ,!= 比较 ,适用于vec[234]和mat[234]。不可以使用>、<、>=和<=。如果比较矢量和矩阵的大小,应该使用内置函数,比如lessThan(),如果想逐分量比较,可以使用内置的函数equal()或notEqual()。
注意
访问超过矢量长度的分量就会报错
提示
将(同一个集合的)多个分量名共同置于点运算符后,就可以从矢量中同时抽取出多个分量。这个过程称作混合(swizzling)。
vec3 v3 = vec3(1,0,2.0,3.0)
vec2 v2;
v2 = v3.xy;//设v2为(1.0,2.0)
v2 = v3.xx;//设v2为(1.0,1.0)
vec3 v3a;
v3a = v3.zyx;//设v3a为(3.0,2.0,1.0) 可以使用所有分量
提示
聚合分量名也可以用来作为赋值表达式的左值
vec4 position = vec4(1.0, 2.0, 3.0, 4.0);
position.xw = vec2(5.0,6.0);//此时的多个分量名必须属于同一个集合,比如说,你不能使用v3.was;
除了.运算符,还可以使用[]运算符并通过数组下标来访问矢量或矩阵的元素。此外连续使用两个[]可以访问某列的某个元素。
mat4 m4 = mat4(16个元素)
float m23 = m4[1][2] //将m23设置为m4的第2列中的第3个元素
float m32 = m4[2].y //将m32设为m4矩阵第3列中的第2个元素
注意
这里有个限制,就是[]中只能出现的索引值必须是常量索引值,常量索引值的定义如下:
- 整型字面量(0,1,2 ....)
- 用const修饰的全局变量或局部变量
- 循环索引
- 由前述三条中的项组成的表达式
# 矢量和浮点数的运算
vec3 v3a,v3b,v3c
mat3 m3a,m3b,m3c
float f
v3b = v3a + f; //v3b.x = v3a.x + f; v3b.y = v3a.y + f; v3b.z = v3a.z +f; v3a = vec3 (1.0,2.0,3.0); f = 1.0; v3b = (2.0, 3.0, 4.0)
v3c = v3a + v3b; //v3a.x + v3b.x; v3a.y + v3b.y; v3a.z + v3b.z; v3a = vec3(1.0,2.0,3.0); v3b = vec3(4.0,5.0,6.0); v3c = (5.0,7.0,9.0)
# 矩阵和浮点数的运算
矩阵和浮点数的运算发生在矩阵的每个分量上。
vec3 v3a,v3b,v3c
mat3 m3a,m3b,m3c
float f
m3b = m3a * f; m3a的每个分量乘以f,作为m3b的每个分量
# 矩阵右乘矢量
矩阵右乘矢量的结果是矢量,其中每个分量都是原矢量中的对应分量,乘上矩阵对应行的每个元素的积的加和。
v3b = m3a * v3a;
//v3b.x = m3a[0].x * v3a.x + m3a[1].x * v3a.y + m3a[2].x * v3a.z;
//v3b.y = m3a[0].y * v3a.y + m3a[1].y * v3a.y + m3a[2].y * v3a.z;
//v3b.z = m3a[0].z * v3a.x + m3a[1].z * v3a.y + m3a[2].z * v3a.z;
# 矩阵左乘矢量
v3b = v3a * m3a;
# 矩阵与矩阵相乘
m3c = m3a * m3b;
# 结构体
GLSL ES支持用户自定义的类型,即结构体(structures)。关键字struct
//定义了结构体类型light
struct light {
vec4 color;
vec3 position;
}
2
3
4
5
赋值和构造:结构体有标准的构造函数,其名称与结构体名一致。构造函数的参数的顺序必须与结构体定义中的成员顺序一致。
结构体的成员可以参与其自身类型支持的任何运算。但结构体本身只支持赋值(=)和比较(==和!=)。当且仅当两个结构体变量所对应的所有成员都相等时,==运算符才会返回true。如果任意某个成员不相等,那么!=运算符返回true。
# 数组
GLSL ES只支持一维数组,而且数组对象不支持pop()和push()操作。创建数组时也不需要new运算符
申明数组:
float floatArray[4] //声明含有4个浮点数元素的数组
vec4 vec4Array[2] //声明含有2个vec4对象的数组
数组的长度必须是大于0的整型常量表达式(intergral constant expression)
数组元素可以通过索引值访问 float f = floatArray[0];访问数组的第一个元素
注意
数组不能在被声明时一次性地初始化,而必须显式地对每个元素进行初始化。
vec4Array[0] = vec4(4.0,3.0,6.0,1.0);
vec4Array[1] = vec4(3.0,2.0,5.0,1.0);
数组本身只支持[]运算符,但数组的元素能够参与其自身类型支持的任意运算。
# 取样器(纹理)
将GLSL ES支持的一种内置类型称为取样器(sampler),我们必须通过该类型变量访问纹理。取样器变量只能是uniform修饰的变量。
- sampler2D
- samplerCube
唯一能赋值给取样器变量的就是纹理单元编号,而且必须使用WebGL方法gl.uniformli()来进行赋值。
gl.uniformli(u_Sampler,0)将纹理单元编号0传给着色器
除了= 、==和!=,取样器变量不可以作为操作参数参与运算。
# 运算符优先级
- 圆括号 ()
- 函数调用(),数组索引[],点操作符.
- 自增和自减(++、--),负(-),取反(!)
- 乘(*)、除(/),余(%)
- 加(+)、减(-)
- 按位移(<<,>>)
- 大小比较(<,<=,>,>=)
- 判断相等(==,!=)
- 按位与(&)
- 按位异或(^)
- 按位或(|)
- 与(&&)
- 异或(^^)
- 或(||)
- 三元判断(? : )
- 运算赋值(+=、-=、*=、/=、%=,<<=、>>=、&=、^=、|=)
- 顺序运算符,即逗号(,)
# 流程控制
- for
- if ... else ...
- continue,break,discard
# 函数
与js中函数的定义方式不同,GLSL ES中定义函数的方式更接近于C语言。
如果函数的定义在其调用之后,那么我们必须在进行调用之前先声明该函数的规范。
在GLSL ES中,可以为函数参数指定限定字,以控制参数的行为。可以将函数参数定义成:(1)传递给函数的 (2)将要在函数中被赋值的 (3)既是传递给函数的,也是将要在函数中被赋值的。

GLSL ES提供给用户很多内置函数

# 存储限定字
在WebGL ES中,我们使用attribute、varying和uniform限定字来修饰变量。有时也会使用const限定字,它表示着色器中的某个变量是恒定的常量。
const声明变量时就需要赋初始值。
attribute修饰的变量只能出现在顶点着色器中,只能被声明为全局变量,被用来表示逐顶点的信息。WebGL的环境都支持至少8个attribute变量。
uniform变量可以用在顶点着色器和片元着色器中,且必须是全局变量。uniform变量是只读的,可以是除了数组或结构体之外的任意类型。
varying变量必须是全局变量,它的任务是从顶点着色器向片元着色器传递数据,我们必须在两种着色器中声明同名、同类型的varying变量。 顶点着色器中赋给varying变量的值并不是直接传递给了片元着色器的varying变量,这其中发生了光栅化的过程。因为在值的传递过程中存在内插,所以数据类型有限制。也就是说只支持可以内插数据的类型的数据。

# 精度限定字
GLSL ES新引入了精度限定字,目的是棒状着色器程序提高运行效率,削减内存开支。顾名思义,精度限定字用来表示每种数据具有的精度。使用精度限定字可以控制效果和性能之间的平衡。
- highp(高精度)
- mediump(中精度)
- lowp(低精度)
精度检查方法:gl.getShaderPrecisionFormat()
提示
统一声明默认精度关键字是 precision
precision 精度限定字 类型名称
precision mediump float //所有浮点数默认是中精度
precision highp int //所有整型数默认为高精度


# 预处理指令
GLSL ES支持预处理指令。预处理指令用来在真正编译之前对代码进行预处理,都是以#开始。
#ifdef 某宏
如果定义了某宏,执行这里
#endif
2
3
#ifndef 某宏
如果没有定义某宏,执行这里
#endif
2
3
#define定义宏
#define 宏名 宏内容
2
#undef指令解除宏定义
#undef 宏名
2