与JavaScript中函数定义的方式不同,GLSL ES定义函数的方式更接近于C语言,其格式如下:
参数的type必须为GLSL ES的类型之一,或者像main()函数这样没有参数也是允许的。如果函数不返回值,那么函数中就不需要有return语句。但这种情况下,返回类型必须是void。你也可以将自己定义的结构体类型指定为返回类型,但是结构体的成员中不能有数组。
下面这段代码是一个函数,实现将RGBA颜色转化为亮度值。
float luma (vec4 color){
float r = color.r;
float g = color.g;
float b = color.b;
return 0.2126*r+0.7162*g+0.0722*b;
//以上4行代码也可以重写成一行:
return 0.2126*color.r+0.7162*color.g+0.0722*color.b;
}
声明了函数后,你就可以调用它了,其方法与JavaScript和C语言中的相同,通过函数名和参数序列来调用:
attribute vec4 a_Color; //传入(r,g,b,a)的值
void main(){
...
float brightness = luma(a_Color);
...
}
注意,如果调用函数时传入的参数类型与声明函数时指定的参数类型不一致,就会出错。比如,下面这段代码就会出错,因为函数声明时的参数是float类型,而调用是却传入了int类型的值。
float square(float value){
return value*value;
}
void main(){
...
float x2 = square(10); //错误:10是整数,应该用10.0
...
}
如你所见,函数正如你预期的那样运行了。但是,和C与JavaScript不同的是,你不能在一个函数内部调用它本身(也就是说,递归调用是不允许的)。这项限制的目的也是为了便于编译器对函数进行内联展开。
规范声明
如果函数定义在其调用之后,那么我们必须在进行调用之前声明该函数的规范。规范声明会预先告诉WebGL系统函数的参数、参数类型、返回值等等。这一点与JavaScript截然不同,后者不需要提前声明函数。下面这段代码对前一节示例中的luma()函数提前作了规范声明:
float luma(vec4); //规范声明
void main(){
...
float brightness = luma(color); //luma()在定义之前就被调用了
...
}
float luma(vec4 color){
return 0.2126*color.r+0.7162*color.g+0.0722*color.b;
}
参数限定词
在GLSL ES中,可以为函数参数指定限定字,以控制参数的行为。我们可以将函数参数定义成:
(1)传递给函数的
(2)将要在函数中被赋值的
(3)既是传递给函数的,也是将要在函数中被赋值的。
其中(2)和(3)都有点类似于C语言中的指针。表6.12显示了这些参数的限定字。
比如说,我们可以给luma()函数指定一个out参数,让其接收函数的计算结果。而在此之前,我们是通过返回值来想外部反馈计算结果的。
void luma(in vec3 color,out float brightness){
brightness = 0.2126*color.r+0.7162*color.g+0.0722*color.b;
}
修改之后,函数本身不返回值,所以函数的返回类型设为void。此外,我们还在第1个参数之前添加了限定词in,实际上这可以省略,因为in是默认的限定字。
然后,我们可以这样调用函数:
luma(color,brightness);
//效果和return的获取赋值brightness相同