Glsl语法
第一章 GLSL 基本规范
1.末尾必须要有分号
2.main函数为主函数
3.注释语法和js 一样
4.基本数据类型:
-
数字型
- 浮点型 float,如1.0
- 整型 int,如1
-
布尔型 bool
- true
- false
第二章 变量
1-声明变量 (类型 名 = 值;)
float f=1.0;
int i=1;
bool b=true;
2-变量命名规范
- 只能包括a-z,A-Z,0-9,_
- 变量名首字母不能是数字
- 不能是GLSL 关键字,如attribute,vec4,bool
- 不能是GLSL 保留字,如cast,class,long
- 不能以下单词开头:gl_, webgl_, webgl
3-变量的赋值
变量使用等号=赋值,=两侧的数据类型需要一致。
int i=8; // ✔
int i=8.0; // ✖
4-变量的类型转换
- 浮点转整数:int(float)
- 布尔转整数:int(bool)
- 整数转浮点:float(int)
- 布尔转浮点:float(bool)
- 整数转布尔:bool(int)
- 浮点转布尔:bool(float)
第三章 向量
1-向量类型
- vec2、vec3、vec4:分量是浮点数
- ivec2、ivec3、ivec4:分量是整数
- bvec2、bvec3、bvec4:分量是布尔值
2-向量的创建(注:= 两侧的数据类型必须一致)
vec3 v3 = vec3(1.0, 0.0, 0.5); // (1.0, 0.0, 0.5)
vec2 v2 = vec2(v3); // (1.0, 0.0)
vec4 v4 = vec4(1.0); // (1.0,1.0,1.0,1.0)
vec4 v4b=vec4(v2,v4); // (1.0,0.0,1.0,1.0)
3-向量分量的访问
向量分量的访问方式:
- 通过分量属性访问
v4.x, v4.y, v4.z, v4.w // 齐次坐标
v4.r, v4.g, v4.b, v4.a // 色值
v4.s, v4.t, v4.p, v4.q // 纹理坐标
vec4 v4 = vec4(1.0,2.0,3.0,4.0);
v4.xy //(1.0,2.0)
v4.yx //(2.0,1.0)
v4.xw //(1.0,4.0)
v4[0], v4[1], v4[2], v4[3]
可以用=号为向量赋值:
v4.x=1.0
v4[0]=1.0
v4.xy=vec2(1.0,2.0)
第四章 矩阵
1-矩阵的类型
GLSL ES 支持2、3、4维矩阵:
- mat2
- mat3
- mat4
矩阵中的元素都是浮点型。
2-矩阵的建立
- 浮点
mat4 m=mat4(
1,5,9,13,
2,6,10,14,
3,7,11,15,
4,8,12,16
);
- 向量
vec4 v4_1=vec4(1,2,3,4);
vec4 v4_2=vec4(5,6,7,8);
vec4 v4_3=vec4(9,10,11,12);
vec4 v4_4=vec4(13,14,15,16);
mat4 m=mat4(v4_1,v4_2,v4_3,v4_4);
- 浮点+向量
vec4 v4_1=vec4(1,5,9,13);
vec4 v4_2=vec4(2,6,10,14);
mat4 m=mat4(
v4_1,
v4_2,
3,7,11,15,
4,8,12,16
);
- 单个浮点数
mat4 m=mat4(1);
/*
[
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]
*/
注:如矩阵中的参数数量大于1,小于矩阵元素数量,会报错
mat4 m4 = mat4(1.0,2.0);
/*报错*/
3-矩阵的访问
mat4 m=mat4(
1,5,9,13,
2,6,10,14,
3,7,11,15,
4,8,12,16
);
m[0]
/*
1,5,9,13,
*/
m[0][0]
/* 1 */
m[0].[0] //1
m[0].x //1
m[0].r //1
m[0].s //1
注:[] 中的索引值只能通过以下2种方式定义:
- 整形字面量,如0,1,2,3
m[0]
m[1]
- 用const 修饰的变量
const int y=0;
m[y];
注:以下写法是错误的
int y=0;
m[y];
- 循环索引
mat4 n=mat4(
1,5,9,13,
2,6,10,14,
3,7,11,15,
4,8,12,16
);
mat4 m=mat4(1);
void main(){
for(int i=0;i<4;i++){
m[i]=n[i];
}
……
}
- 前面三项组成的表达式
const int y=0;
m[y+1];
第五章 运算符
1-算术运算符
运算符 | 描述 | 例子 |
---|---|---|
+ | 加法 | x = y + 2 |
- | 减法 | x = y - 2 |
* | 乘法 | x = y * 2 |
/ | 除法 | x = y / 2 |
++ | 自增 | x = ++y |
x = y++ | ||
-- | 自减 | x = --y |
x = y-- |
2-赋值运算符
运算符 | 描述 | 例子 |
---|---|---|
= | 赋值等于 | x = y |
+= | 加等于 | x += y |
-= | 减等于 | x -= y |
*= | 乘等于 | x *= y |
/= | 除等于 | x /= y |
3-比较运算符
运算符 | 描述 | 比较 |
---|---|---|
== | 等于 | x == 8 |
!= | 不等于 | x != 8 |
大于 | x > 8 | |
< | 小于 | x < 8 |
>= | 大于或等于 | x >= 8 |
<= | 小于或等于 | x <= 8 |
4-条件运算符
语法 | 例子 |
---|---|
布尔 ?值1:值2 | float f=2>3?1.0:2.0; |
5-逻辑运算符
运算符 | 描述 | 例子 | |||
---|---|---|---|---|---|
&& | 和 | true&&true=true; | |||
或 | false | true=true; true | true=true; | ||
! | 非 | !false=true; | |||
^^ | 异或 | false^^true=true; true^^true=false; |
第六章 向量运算
1-向量和单独数字的运算
vec4 v=vec4(1,2,3,4);
v+=1.0;
v-=1.0;
v*=2.0;
v/=2.0;
vec4 是浮点型向量,写在vec4() 中的整数可以被vec4() 方法转换成浮点数。
vec4 向量在做四则运算时,其用于运算的数字类型应该是浮点型。
比如,下面的写法会报错:
v+=1;
整型向量ivec2、ivec3、ivec4 的运算原理同上。
2-向量和向量的运算
1.加减乘除
vec4 p=vec4(1,2,3,4);
vec4 v=vec4(2,4,6,8);
v+=p;
v-=p;
v*=p;
v/=p;
2.其它方法
- distance(p0,p1) 向量距离
- dot(p0,p1) 点积
- cross(p0,p1) 叉乘
3-向量和矩阵的运算
矩阵只能与向量进行乘法运算。
向量乘以矩阵和矩阵乘以向量的结果是不一样的,但数据类型都是向量。
- 矩阵乘以向量
mat4 m=mat4(
1,5,9,13,
2,6,10,14,
3,7,11,15,
4,8,12,16
);
vec4 p=vec4(1,2,3,4);
vec4 v=m*p;
/*
[30, 70, 110, 150]
*/
以v.x为例说一下其算法:
v.x=m[0][0]*v.x+m[1][0]*v.y+m[2][0]*v.z+m[3][0]*v.w
v.x=1*1+2*2+3*3+4*4
v.x=1+4+9+16
v.x=30
-
向量乘以矩阵
将上面的代码改一下:
vec4 v=p*m;
/*
[90, 100, 110, 120]
*/
以v.x为例说一下其算法:
v.x=m[0][0]*v.x+m[1][0]*v.y+m[2][0]*v.z+m[3][0]*v.w
v.x=1*1+2*5+3*9+4*13
v.x=90
第七章 矩阵运算
矩阵可以与以下数据进行各种运算:
- 单独数字
- 向量
- 矩阵
1-矩阵和单独数字的运算
mat4 m=mat4(
1,5,9,13,
2,6,10,14,
3,7,11,15,
4,8,12,16
);
m+=1.0;
m-=1.0;
m*=2.0;
m/=2.0;
2-矩阵和矩阵的运算
矩阵和矩阵之间可以进行加减乘除。
以下面的两个矩阵为例:
mat4 m=mat4(
2,16,8,8,
4,8,8,8,
8,4,8,8,
16,8,8,8
);
mat4 n=mat4(
1,4,1,2,
2,4,2,1,
4,4,1,2,
8,4,2,1
);
1.矩阵加法、减法、除法:相同索引位置的元素相加相减相除
m+=n;
/*
3, 20, 9, 10,
6, 12, 10, 9,
12, 8, 9, 10,
24, 12, 10, 9,
*/
3.矩阵乘法:列*行=列
m*=n;
/*
58, 68, 64, 64,
52, 80, 72, 72,
64, 116, 88, 88,
64, 176, 120, 120,
*/
m[0][0]=m[0][0]*n[0][0]+m[1][0]*n[0][1]+m[2][0]*n[0][2]+m[3][0]*n[0][3]
m[0][0]=2*1+4*4+8*1+16*2
m[0][0]=58
m[1][0]=m[0][0]*n[1][0]+m[1][0]*n[1][1]+m[2][0]*n[1][2]+m[3][0]*n[1][3]
m[1][0]=2*2+4*4+8*2+16*1
m[1][0]=52
m[2][0]=m[0][0]*n[2][0]+m[1][0]*n[2][1]+m[2][0]*n[2][2]+m[3][0]*n[2][3]
m[2][0]=2*4+4*4+8*1+16*2
m[2][0]=64
m[3][0]=m[0][0]*n[3][0]+m[1][0]*n[3][1]+m[2][0]*n[3][2]+m[3][0]*n[3][3]
m[3][0]=2*8+4*4+8*2+16*1
m[3][0]=64
第八章 struct构造函数
1.struct 的建立
struct Light{
vec4 color;
vec3 pos;
};
上面的struct 类似于js 的function,color和pos 既是结构体的属性,也使其形参。
2.struct 的实例化
Light l1=Light(
vec4(255,255,0,255),
vec3(1,2,3)
);
上面的vec4()和vec3()数据是结构体的实参,分别对应color属性和pos属性。
3.访问struct 实例对象中的属性
gl_FragColor=l1.color/255.0;
第九章 数组
数组具有以下特性:
- 属于类型数组
- 只支持一维数组
- 不支持pop()、push() 等操作
1.在建立某个类型的数组时,在数据类型后面加[],[]中写数组的长度:
vec4 vs[2];
vs[0]=vec4(1,2,3,4);
vs[1]=vec4(5,6,7,8);
2.数组的长度要按照以下2种方式定义:
- 整形字面量
vec4 vs[2];
- const 限定字修饰的整形变量
const int size=2;
vec4 vs[size];
- 不能是函数的参数
3.数组需要显示的通过索引位置一个元素、一个元素的赋值。
vs[0]=vec4(1,2,3,4);
vs[1]=vec4(5,6,7,8);
4.数组中的元素需要用整形的索引值访问
gl_FragColor=vs[1]/255.0;
第十章 程序流程控制
1-if 判断
float dist=distance(gl_PointCoord,vec2(0.5,0.5));
if(dist>=0.0&&dist<0.125){
gl_FragColor=m[0]/255.0;
}else if(dist>=0.125&&dist<0.25){
gl_FragColor=m[1]/255.0;
}else if(dist>=0.25&&dist<0.375){
gl_FragColor=m[2]/255.0;
}else if(dist>=0.375&&dist<0.5){
gl_FragColor=m[3]/255.0;
}else{
discard;
}
if(a==float(b)){
//这种写法可以写,注意在else{}后面无分号
}
if 语句写太多会降低着色器执行速度,暂时还没有switch 语句;
2-for 循环
for(初始化表达式; 条件表达式; 循环表达式){
循环体;
}
for循环的基本规则如下:
- 循环变量只能有一个,只能是int或float 类型。
- 在循环体中也可以使用break或continue,其功能和js一样。
循环变量是整形:
float dist=distance(gl_PointCoord,vec2(0.5,0.5));
for(int i=0;i<4;i++){
float r1=0.125*float(i);
float r2=r1+0.125;
if(dist>=r1&&dist<r2){
gl_FragColor=m[i]/255.0;
break;
}else if(i==3){
discard;
}
}
循环变量是整形浮点型:
for(float f=0.0;f<=4.0;f++){
float r1=0.125*f;
float r2=r1+0.125;
if(dist>=r1&&dist<r2){
gl_FragColor=m[int(f)]/255.0;
break;
}else if(f==3.0){
discard;
}
}
第十一章 函数
1-函数的建立
函数类型 函数名(形参){
函数内容;
return 返回值;
}
2-示例
<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
float getLum(vec3 color){
return dot(color,vec3(0.2126,0.7162,0.0722));
}
void main(){
vec3 color=vec3(255,255,0);
float lum=getLum(color);
vec4 v=vec4(vec3(lum),255);
gl_FragColor=v/255.0;
}
</script>
3-函数的声明
我们也可以将函数体放到其调用方法的后面,不过在调用之前得提前声明函数。
其声明方式如下:
函数类型 函数名(形参类型);
函数名(实参);
函数类型 函数名(形参){
函数内容;
return 返回值;
}
4-参数限定词
- in 参数深拷贝,可读写,不影响原始数据,默认限定词
precision mediump float;
void setColor(in vec3 color){
color.x=0.0;
}
void main(){
vec3 color=vec3(255);
setColor(color);
vec4 v=vec4(color,255);
gl_FragColor=v/255.0;
}
在上面的主函数体中,我先声明了一个颜色color,我要通过setColor() 方法修改颜色。
当setColor() 方法中的形参color 被in 限定时,就相当于对实参进行了深拷贝,无论我在setColor() 方法里对color 做了什么,都不会影响原始的color 数据。
那么如果我想在setColor() 方法里修改原始的color数据,那就得使用out 限定。
- out 参数浅拷贝,可读写,影响原始数据。
void setColor(out vec3 color){
color.x=0.0;
}
- const in 常量限定词,只读。
比如,下面的写法就是错的:
void setColor(in vec3 color){
color.x=0.0;
}
我们只能读取color 数据:
void setColor(in vec3 color){
float r=color.r;
}
- inout 功能类似于out,用得不多,知道即可。
注:GLSL ES 还有许多内置方法:官方文档
第十二章 变量的作用域
可以通过函数或{} 建立块级作用域,块级作用域内建立的变量就是局部变量。
局部变量只在块级作用域有效。
在函数之外建立的变量就是全局变量。
在代码块内可以直接获取其父级定义域的变量。
变量不存在“变量提升”现象,变量在使用时,需提前声明。
变量不能重复声明。
通过attribute、uniform、varying 限定字声明的变量都是全局变量。
const 可以声明常量,常量是只读的。
第十三章 精度限定词
精度限定词可以提高着色程序的运行效率,削减内存开支。
1-精度的分类
webgl 提供了三种精度:
- highp 高精度
- mediump 中精度
- lowp 低精度
一般中精度用得会比较多,因为高精度太耗性能,而且有时候片元着色器会不支持,而低精度又太low。
2-精度的定义方法
我可以为某个变量设置精度,也可以为某种数据类型设置精度。
设置某个变量的精度:
mediump float size;
highp vec4 position;
lowp vec4 color;
设置某种数据类型的精度:
precision mediump float;
precision highp int;
注:
着色器中,除了片元着色器的float 数据没默认精度,其余的数据都是有默认精度的。
因此,我们在片元着色器里要提前声明好浮点型数据的精度
第十四章 glsl内置函数
step函数
step(a, b);当b > a时, 返回1;当b < a时,返回0。
-
应用:转换坐标系:下面的st为每个点的坐标
- 初始:左下角为零点:step(vec2(0.1),st)
- 转换:右上角为零点:step(vec2(0.1),1.0-st)
smoothstep函数
smoothstep(a, b,x);当b > a时, 返回1;当b < a时,返回0。
-
应用:平滑
-
smoothstep(-2, 3, x);
- smoothstep(3, -2, x);
- smoothstep(1, 2, x)-smoothstep(2, 3, x)
- smoothstep(1, 2, x)-smoothstep(3, 4, x)
distance函数
distance(a, b);返回a和b之间的距离
- 应用:求st到0.5的距离
- distance(st, vec2(0.5));
length函数
length(a); 返回向量a的长度,比如vec2 a=vec2(3,2); length(a)= 根号下(33 +22)
- 应用:求st到0.5的距离
- vec2 toCENTER=vec2(0.5)-st;
- length(toCENTER);
ceil函数
ceil(a);取整,去掉小数部分
- ceil(0.5); //0
mod函数
mod(a,b);取a/b的余数
fract函数 fract(a); 返回小数部分,原理 fract(x)=x-floor(x)
abs函数 abs(-1);取绝对值
cross函数 cross(vec3(1.,1.,0.0),vec3(2.,4.,0.0));叉乘 参数和返回值都是vec3
dot函数 dot(vec2(1.,0.0),vec2(2.,0.0));点乘 参数是vec2 ,返回值时float;
clamp函数 clamp(a x y);返回中间大小的值 例如 clamp(5 1 4) 返回的是4 ;传参时x必须比y小。
clamp函数 clamp(a x y);返回中间大小的值 例如 clamp(5 1 4) 返回的是4 ;传参时x必须比y小。