GLSL语言

183 阅读12分钟

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:值2float f=2>3?1.0:2.0;

5-逻辑运算符

运算符描述例子
&&true&&true=true;
falsetrue=true; truetrue=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);

image.png

  • smoothstep(3, -2, x);

image.png

  • smoothstep(1, 2, x)-smoothstep(2, 3, x)

image.png

  • smoothstep(1, 2, x)-smoothstep(3, 4, x)

image.png

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小。