更新于 

GLSL ES语法

GLSL ES和GLSL是什么关系?

GLSL ES是在GLSL基础上 删除和简化一部分功能后形成的
尽管是基于OpenGL着色器语言专为 电子产品/嵌入式设备 打造的,
但现在 GLSL ES 也开始被用来完成一些通用任务,如 GPGPU(图像处理和数据运算)

基础语法

GLSL ES基本注意点
  • 大小写敏感
  • 语句以分号结尾
执行次序:

以唯一的main函数为入口。

注释
  • 单行注释 //
  • 多行注释 /**/
数据值类型
  • 数值类型
    • 整型数
    • 浮点数
  • 布尔值类型

GLSL变量命名不能以gl_、webgl_、_webgl_开头。

GLSL ES关键字
GLSL ES基本类型
GLSL ES类型转换函数
GLSL ES运算符

矢量和矩阵

类型声明
1
2
3
4
5
6
void main(){
vec3 position; //3维浮点矢量
ivec2 offset; //2维整型矢量
bvec4 bb; //4维布尔矢量
mat4 mvpMatrix; //4维浮点矩阵
}
赋值和构造

每种类型都对应一种 构造函数(constructor functions) 以供创建对应类型变量。
构造函数的名称和其创建变量的类型名称总是一致的。

1
2
3
4
vec3 position = vec3(1.0,0.0,0.5);
ivec2 offset = ivec2(1,2);
bvec4 bb = bvec4(false,false,true,false);
mat2 mvpMatrix = mat2(1.0,2.0,3.0,4.0);
矢量构造函数

GLSL ES构建矢量的方式相当灵活。

标准构建

1
vec3 v3 = vec3(1.0,0.3,0.5);

需要剔除多余元素的构建

1
2
vec3 v3 = vec3(1.0,0.3,0.5);
vec2 v2 = vec2(v3); //(1.0,0.3)

使用同一值填充全矢量元素的构建

1
vec4 v4 = vec4(1.0); //(1.0,1.0,1.0,1.0)

使用多个其他变量结构构建

1
2
3
4
vec3 v3 = vec3(1.0,0.3,0.5);
vec2 v2 = vec2(v3); //(1.0,0.3)
vec4 v4 = vec4(1.0); //(1.0,1.0,1.0,1.0)
vec4 v4b = vec4(v2,v4); //(1.0,0.3,1.0,1.0)

参数数量>1且小于应付的值数,报错

1
vec3 v33 = vec3(1.0,0.5); //Error
矩阵构造函数

GLSL中矩阵元素的存储是列主序的。

传入矩阵的每一个元素

1
2
3
4
5
6
7
8
9
10
11
12
/*
1.0 5.0 9.0 13.0
2.0 6.0 10.0 14.0
3.0 7.0 11.0 15.0
4.0 8.0 12.0 16.0
*/
mat4 m4 = mat4(
1.0,2.0,3.0,4.0,
5.0,6.0,7.0,8.0,
9.0,10.0,11.0,12.0,
13.0,14.0,15.0,16.0
);

传入一个或多个矢量

1
2
3
4
5
6
7
8
9
10
11
12
// 使用2个vec2构建mat2对象
vec2 v2_1 = vec2(1.0,3.0);
vec2 v2_2 = vec2(2.0,4.0);
// 1.0 2.0
// 3.0 4.0
mat2 m2_1 = mat2(v2_1,v2_2);

// 使用1个vec4构建mat2对象
vec4 v4 = vec4(1.0,3.0,2.0,4.0);
// 1.0 2.0
// 3.0 4.0
mat2 m2_2 = mat2(v4);

同时传入矢量和数值

1
2
3
4
5
// 使用两个浮点数和一个vec2对象创建mat2对象
vec2 v2 = vec2(2.0,4.0);
// 1.0 2.0
// 3.0 4.0
mat2 m2 = mat2(1.0,3.0,v2);

只传一个数值,生成一个对角线全为该数值的矩阵

1
2
3
 // 1.0 0.0
// 0.0 1.0
mat2 m2 = mat2(1.0);

传入数值数量大于1,又没有达到矩阵元素的数量,导致错误

1
mat4 m4 = mat4(1.0,2.0,3.0); //Error
访问元素
点运算符
点运算符可以访问的分量
点运算符可以访问的分量

TIP1:这些可以通过名称访问的分量事实上还是通过参数顺序获取的。

TIP2:这些分量只是索引的访问的语法糖,因此可以随意交叠使用。

TIP3:访问超过矢量长度的分量会出错。

1
2
3
4
5
6
7
vec3 v3 = vec3(1.0,2.0,3.0);
float f;

f = v3.x; //1.0
f = v3.g; //2.0
f = v3.p; //3.0
f = v3.w; //Error:访问了超出矢量长度的分量
混合(swizzling)

混合是指将多个分量名共同置于点运算符后,
可以同时从矢量中抽取出多个分量。

1
2
3
4
5
vec3 v3 = vec3(1.0,2.0,3.0);
vec2 v2;
v2 = v3.xy; // (1.0,2,0)
v2 = v3.tp; // (2.0,3.0)
v2 - v3.gg; // (2.0,2.0)

聚合分量名可作为赋值表达式的左值

1
2
3
4
vec3 v3 = vec3(1.0,2.0,3.0);
vec2 v2;
v2.xy = v3.gb;
v2.s = v3.x;

混合的分量名必须属于同一个分量集合

1
2
3
vec3 v3 = vec3(1.0,2.0,3.0);
vec2 v2;
v2.xy = v3.ry; //Error
[]运算符

矩阵元素读取仍然按照列主序

1
2
3
4
5
6
7
mat4 m4 = mat4(
1.0,2.0,3.0,4.0,
5.0,6.0,7.0,8.0,
9.0,10.0,11.0,12.0,
13.0,14.0,15.0,16.0
);
vec4 v4 = m4[0]; //[1.0,2.0,3.0,4.0]

使用两个[]访问具体元素

1
2
3
4
5
6
7
mat4 m4 = mat4(
1.0,2.0,3.0,4.0,
5.0,6.0,7.0,8.0,
9.0,10.0,11.0,12.0,
13.0,14.0,15.0,16.0
);
float m24 = m4[1][3]; //8.0

使用[]和分量名访问具体元素

1
float m12 = m4[0].g; // 2.0
[]中索引值只能是常量索引值

常量索引值包括:

  • 整型字面量
  • const声明的值
  • 循环索引
  • 常量表达式
运算符

矢量和矩阵的比较运算符只限于==和!=,不等大小的比较需要通过内置函数。

矢量和浮点数的运算

相加运算

1
v3b = v3a + f;

运算结果

1
2
3
v3b.x = v3a.x + f;
v3b.y = v3a.y + f;
v3b.z = v3a.z + f;
矢量运算

矢量的每个分量都参与运算

1
v3c = v3a + v3b;

运算结果

1
2
3
v3c.x = v3a.x + v3b.x;
v3c.y = v3a.y + v3b.y;
v3c.z = v3a.z + v3b.z;
矩阵和浮点数运算

矩阵的每个分量都参与浮点数运算

1
m3b = m3a * f;

运算结果

1
2
3
4
5
6
7
8
9
10
11
m3b[0].x = m3a[0].x * f;
m3b[0].y = m3a[0].y * f;
m3b[0].z = m3a[0].z * f;
/////////////////////////
m3b[1].x = m3a[1].x * f;
m3b[1].y = m3a[1].y * f;
m3b[1].z = m3a[1].z * f;
/////////////////////////
m3b[2].x = m3a[2].x * f;
m3b[2].y = m3a[2].y * f;
m3b[2].z = m3a[2].z * f;
矩阵右乘矢量

结果为矢量

1
v3b = m3a * v3a;

运算结果

1
2
3
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;
矩阵左乘矢量

左乘结果与右乘不同

1
v3b = v3a * m3a;

运算结果

1
2
3
v3b.x = v3a.x * m3a[0].x + v3a.y * m3a[0].y + v3a.z * m3a[0].z; 
v3b.y = v3a.x * m3a[1].x + v3a.y * m3a[1].y + v3a.z * m3a[1].z;
v3b.z = v3a.x * m3a[2].x + v3a.y * m3a[2].y + v3a.z * m3a[2].z;
矩阵相乘

1
m3c = m3a * m3b;

运算结果

1
2
3
4
5
6
7
8
9
10
11
m3c[0].x = m3a[0].x * m3b[0].x + m3a[1].x * m3b[0].y + m3a[2].x * m3b[0].z;
m3c[0].y = m3a[0].y * m3b[0].x + m3a[1].y * m3b[0].y + m3a[2].y * m3b[0].z;
m3c[0].z = m3a[0].z * m3b[0].x + m3a[1].z * m3b[0].y + m3a[2].z * m3b[0].z;
////////////////
m3c[1].x = m3a[0].x * m3b[1].x + m3a[1].x * m3b[1].y + m3a[2].x * m3b[1].z;
m3c[1].y = m3a[0].y * m3b[1].x + m3a[1].y * m3b[1].y + m3a[2].y * m3b[1].z;
m3c[1].z = m3a[0].z * m3b[1].x + m3a[1].z * m3b[1].y + m3a[2].z * m3b[1].z;
////////////////
m3c[2].x = m3a[0].x * m3b[2].x + m3a[1].x * m3b[2].y + m3a[2].x * m3b[2].z;
m3c[2].y = m3a[0].y * m3b[2].x + m3a[1].y * m3b[2].y + m3a[2].y * m3b[2].z;
m3c[2].z = m3a[0].z * m3b[2].x + m3a[1].z * m3b[2].y + m3a[2].z * m3b[2].z;

结构体

结构体(struct)

注意:结构体成员不能包含数组

1
2
3
4
5
6
 // 定义结构体类型
struct light {
vec4 color;
vec3 position;
};
light light1,light2;

可以在定义结构体的同时声明该类型的变量。

1
2
3
4
struct light { 
vec4 color;
vec3 position;
} light1;
结构体构造

结构体构造函数的名称与结构体名称 一致
参数顺序必须与结构体中定义的成员顺序 一致

1
2
3
4
5
6
7
8
9
 // 定义结构体类型
struct light {
vec4 color;
vec3 position;
};
light l1 = light(
vec4(0.0,1.0,0.0,1.0), //color
vec3(8.0,3.0,0.0) //position
);
结构体成员的访问

<结构体对象>.<成员名称>

1
2
3
4
5
6
7
8
9
10
struct light { 
vec4 color;
vec3 position;
};
light l1 = light(
vec4(0.0,1.0,0.0,1.0), //color
vec3(8.0,3.0,0.0) //position
);
vec4 color = l1.color; //(0.0,1.0,0.0,1.0)
vec3 position = l1.position; //(8.0,3.0,0.0)
结构体运算符

结构体相等的条件是所有成员都相等。

数组

GLSL ES中数组的注意点
  • 只支持 一维数组
  • 数组的长度必须是 大于0的整型常量表达式(integral constant expression)
  • 索引值 从0开始
  • 索引值只能是 整型常量表达式uniform变量
  • 数组不能在声明时 被一次性的初始化,必须显式 对每个元素进行初始化
  • 数组元素可以参与其自身类型支持的任意运算

数组的声明

1
2
float floatArray[4];
vec3 vec3Array[2];

数组长度必须是大于0的整型常量表达式

1
2
int size = 4;
vec4 vec4Array[size]; //error

使用索引访问数组元素

1
2
3
4
5
6
int intArray[4];
intArray[0] = 1;
intArray[1] = 2;
intArray[2] = 3;
intArray[3] = 4;
int num = intArray[1]; //2

必须显式对数组每个元素进行初始化

1
2
3
vec4 vec4Array[4];
vec4Array[2] = vec4(0.0,0.0,0.0,1.0);
vec4Array[1] = vec4(1.0,0.0,0.0,1.0);

数组的运算

1
2
3
4
5
6
7
float floatArray[4];
floatArray[1] = 1.0;
float f = floatArray[1] * 3.14; //3.14

vec2 vec2Array[3];
vec2Array[0] = vec2(0.5,0.9);
vec2 v2 = floatArray[1] * vec2Array[0]; //(0.5,0.9)

取样器(sampler)

取样器类型
  • sampler2D
  • samplerCube
取样器变量

只能是 uniform 变量。

1
uniform sampler2D u_Sampler;

sampler唯一能够接收的变量就是 纹理单元编号
必须使用 gl.uniform1i() 将纹理单元编号从WebGL传入 shader 中。

1
gl.uniform1i(u_Sampler,0);

sampler变量只能进行赋值(=)、相等(==)、不等(!=)操作。

sampler类型内置最小数量
sampler类型内置最小数量

运算符优先级