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;
}
1
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
1
2
3
#ifndef 某宏
如果没有定义某宏,执行这里
#endif
1
2
3
#define定义宏
#define 宏名 宏内容
1
2
#undef指令解除宏定义
#undef 宏名
1
2
上次更新: 2025/02/15, 13:42:25
最近更新
01
Git问题集合
01-29
02
安装 Nginx 服务器
01-25
03
安装 Docker 容器
01-25
更多文章>
×
×