第十四节—GLSL语法

本文为L_Ares个人写作,如需转载请表明原文出处。

GLSL语法和C语言的相似,但是GLSL中是没有指针的。

另外,GLSL一般情况下都是没有语法提示的,所以最好不要写复杂的GLSL语言,除非你有业务需求,不然的话,很容易写错,而且找错也是很难的事情。

一、变量和变量类型

1. 整型 : int。代表至少包含16位的有符号的整数。可以是8进制,10进制,16进制。
uint。无符号整型。例如 : uint uValue = 188u;
2. 浮点型 : float。OpenGL3.0以上的版本只有单精度浮点型,没有双精度。
例如 :float a = 3.14159f;

3. 布尔型 : bool。只有truefalse。例如 : bool a = true;

4. 向量 : 如图1.1

1.1.png

构造方法 : 拿float类型的向量举例,其他的类型一致。

例如 : vec4 v1 = vec4(0,1,2,3);

也就是说,向量的初始化不是直接给个括号就行,要加上vec,具体是vec几,就看你用几分量的向量了。

在向量中的元素可以通过(x,y,z,w)或(r,g,b,a)或(s,t,p,q)来取值,也可以利用它赋值,并且可以多个一起赋值,例如:

vec4 v1 = vec4(1.0,2.0,3.0,4.0);
vec4 v2 = vec4(5.0,6.0,7.0,8.0);
v2.r = v1.s;
v2.g = v1.x;
//这两个是可以的,但是没意义。
v2.rg = v1.xy;
v2.rg = v1.st;
//但是不能搞乱顺序,比如下面是不行的。
v2.rg = v1.xt;
//或者直接赋值
v1.rgb = vec3(1.0,2.0,3.0);
//也可以一次性对所有的分量进行操作
v1.xyzw = v2.xyzw + vec4(1.0,1.0,1.0,1.0);

向量也支持2个或者2个以上的元素进行交换操作(swizzle)。例如:

v1.rgba = v2.bgra;
v2.bgra = v1.rgba;

5. 矩阵 : 矩阵只有浮点类型元素。其他的都不支持。

OpenGL中的矩阵是列主顺序的,如果初始化的时候只给矩阵初始化一个值,则会构造成对角矩阵,其余的元素全部为0。

如图1.2

1.2.png

构造方法 : 拿mat4来看,其他类型一致。

例如 :

//构造一个单元矩阵
mat4 m1 = mat4(1.0,0.0,0.0,0.0,
               0.0,1.0,0.0,0.0,
               0.0,0.0,1.0,0.0,
               0.0,0.0,0.0,1.0);

//也可以这么构造单元矩阵
mat4 m2 = mat4(1.0);

和向量一样,想要初始化矩阵,要使用矩阵的构造函数:matx,其中x是图1.2中的类型,根据需求自行选取。

6. 数组 : GLSL中只可以使用一维数组,数组的类型可以是所有基本类型或者结构体。

声明数组的方式 :

(1). 假设已经定义了结构体myStruct

myStruct myStructArray[];

(2). 向量数组

vec4 myVecArray[6];

(3). 数组赋值

vec4 tempVecArray[] = myVecArray;

(4). 基本数据类型数组,拿float类型举例

float floatArray[8];

构造函数 : float a[4] = float[](1.0,2.0,3.0,4.0);其他的类型类似。

上述方式和类似的方式都是可以声明一个数组的,但是不可以声明一个数组的数组。另外,GLSL中有可以获得数组长度的内建函数函数length(),比如:floatArray.length()可以获得数组的长度为8。

访问数组中的元素可以 : floatArray[0],这样,范围是[0,size - 1],如果越界,那么会发生编译错误,和C语言是一样的。

7. 结构体 : GLSL的结构体可以组合基本类型和数组来形成用户自定义的类型。在定义结构体的时候,你可以直接定义一个结构体实例,或者在后面定义结构体实例,这都是可以的。

定义一个结构体myTemp并声明一个它的变量tempOne : 这里注意,固定大小的数组是合法的。


struct myTemp
{
    float pointSize;
    vec4 colors;
    vec3 position[3];
}tempOne;

结构体可以使用=进行赋值,可以使用==!=分别表示结构体相等,结构体不相等,只有两个结构体的所有元素都一致的时候,两个结构体才是相等的s,例如:

myTemp tempTwo;
tempOne = tempTwo;
tempOne == tempTwo;

访问结构体的变量使用点语法,比如:tempOne.colors

GLSL的结构体不支持嵌套定义,只有预先声明的结构体才可以嵌套其中。例如,上面我们定义了结构体myTemp,下面我们再定义一个结构体theTemp

struct theTemp
{
    float pointSize;
    myTemp temp;
    //注意,这里是不合法的,不可以这么写。结构体只有预先定义过的才可以嵌套。
    struct cantUse{
        float a;
        float b;
    }test;
    //这也是个结构体,但是我们是在这个结构体的下面定义的,所以也是不合法的。
    subTemp sub;
}

struct subTemp
{
    float c;
    float d;
}tex;

8. 变量存储限定符 :

如图1.3所示

1.3.png

二、函数

1. 函数的修饰符

in :没有指定时,默认限定修饰符。复制进函数中,函数内部可以改变内部的它,但是函数不能对其本身进行修改。

inout :复制进函数,并且可以在函数中进行修改,并且返回的时候也是复制出去。

out :只在返回的时候复制,可以进行修改。

函数的修饰符不一定都要用到,看需求使用。

2. 主函数

在GLSL的每一个shader中,都必须有一个main函数,main函数的void参数是可选的,但是返回值是void是必须的。例如:

//括号里面的参数void是可选的,可写可不写,但是最前面的返回值void是必须要有的。
void main (void)
{
...
}

3. 普通函数

在GLSL中,函数必须是全局声明的,不可以在函数中声明或者定义函数。

函数必须有返回类型,参数是可选的。

参数的修饰符,也就是1中所说的几个,都是可选的。

结构体和数组都可以作为函数的参数传递,但是必须限制其大小,在调用传参的时候,只需要传入它们的名字就可以。

全部用上,一起举例:

vec4 myFunc(inout float num, out vec4 v1,mat4 m2,vec4 arr[10])
{
    return v1;
}

比较正常的用法:

vec4 diffuse(vec3 normal , vec3 light , vec4 baseColor)
{
    return baseColor * dot(normal,light);
}

另外,GLSL中,是没有递归函数的。

GLSL的函数是可以重载的,函数可以同名,其参数类型或者参数的数量不同即可。

三、控制流

GLSL提供了类似C语言的whiledo...while的循环方式,没有for循环。continue会跳到下一次循环,break是结束循环。用法几乎一致,直接参考C语言就行。

GLSL也提供了控制语句if/else,比如:

if(color.a < 0.2){
    color *= color.a
}else{
    color = vec4(1.0,1.0,1.0,1.0);
}

但是!!!尽量少在GLSL中写这种逻辑代码,减少逻辑判断在GLSL完成的次数,减少在GLSL中使用循环的情况。

另外在片元着色器(fragmentShader)中,有一种特殊的控制流 : discard

使用discard会退出片元着色器,不执行后面的片元操作,片元也不会写入帧缓冲区。

if (color.a < 0.2) discard;

你可能感兴趣的:(第十四节—GLSL语法)