目录
6.1 函数定义
如上面的语法所示,有效着色器是一系列全局声明和函数定义。 声明一个函数,如下例所示:
// prototype
returnType functionName (type0 arg0, type1 arg1, ..., typen argn);
而函数定义如下:
// definition
returnType functionName (type0 arg0, type1 arg1, ..., typen argn)
{
// do some computation
return returnValue;
}
其中returnType必须存在且包含类型。 每个typeN必须包含一个类型,并且可以选择包括参数限定符,const和精度限定符。
通过使用其名称后跟括号中的参数列表来调用函数。
允许数组作为参数,但不允许作为返回类型。 当数组声明为形式参数时,必须包含它们的大小。通过使用数组名称而不使用任何下标或括号将数组传递给函数,并且传入的数组参数的大小必须与形式参数声明中指定的大小匹配。
结构也被允许作为参数。 如果结构不包含数组,则返回类型也可以是结构。
有关声明和定义函数的语法的权威性参考,请参见第9节“着色语言语法”。
所有函数必须使用原型声明,或者在调用之前使用带有方法体的方式定义。例如:
float myfunc (float f, // f is an input parameter
out float g); // g is an output parameter
函数声明必须指定返回类型。 此类型可能void。 如果返回类型不是void,则函数定义中的任何return语句都必须包含return表达式,并且表达式的类型必须与返回类型匹配。 如果函数的返回类型指定为void,则任何此类return语句都不能包含return表达式。不接受输入参数的函数不需要在参数列表中使用void,因为原型(或定义)是必需的,因此当声明空参数列表“()”时没有歧义。 为方便起见,提供了作为参数列表的约定俗成的表达“(void)”
函数名称可以重载。 这允许相同的函数名称用于多个函数,只要参数列表类型不同即可。如果函数的名称和参数类型匹配,则它们的返回类型和参数限定符也必须匹配。 函数签名匹配仅基于参数类型,不使用限定符。 重载在内置函数中使用很多。 当解析重载函数(或实际上任何函数)时,寻求函数签名的精确匹配。 这包括数组大小的精确匹配。 不会对返回类型或输入参数类型进行升级或降级。 必须将所有预期的输入和输出组合定义为单独的功能。
例如,内置点乘函数具有以下原型:
float dot (float x, float y);
float dot (vec2 x, vec2 y);
float dot (vec3 x, vec3 y);
float dot (vec4 x, vec4 y);
用户定义的函数可以有多个声明,但只有一个定义。
函数main用作着色器的入口点。 顶点着色器和片段着色器都必须包含名为main的函数。 此函数不带参数,不返回任何值,并且必须声明为void类型:
void main()
{
...
}
函数main可以包含return的用法。 更多详细信息,请参见第6.4节“跳转”。
使用任何其他签名声明名为main的函数是错误的。
6.1.1 函数调用惯例
函数由value-return调用。 这意味着输入参数在调用时被复制到函数中,输出参数在函数退出之前被复制回调用者。 因为该函数适用于参数的本地副本,所以在函数中没有关于变量别名的问题。
要控制通过函数定义或声明复制和/或复制的参数:
- 关键字in用作限定符,表示要复制进的参数,但不能复制出去。
- 关键字out用作限定符,表示要复制出的参数,但不能复制进。这个应该尽可能的使用,以避免不必要地复制参数。
- 关键字inout用作限定符,表示要复制进和复制出去的参数。
- 声明没有以上的限定符的函数参数意味着与指定in相同。
所有参数在调用时按顺序从左到右进行计算。对in参数只是复制其形式参数,而out参数则是在函数返回时将值赋值给它,而inout参数则是在调用时复制其作为形式参数的值进行计算,在函数返回时则将值赋值给它。
输出参数复制回调用方的顺序未定义。
在函数中,仅输入参数是允许被写入的。 只是函数的副本被修改了。 这个问题可以通过使用const限定符声明参数来防止。
调用函数时,不计算为左值的表达式不能传递给声明为out或inout的参数。
如果函数未写入out参数,则函数返回时,实际参数的值表现为未定义的。
如果使用非void返回类型声明函数并且函数返回而不执行return语句,则返回的值是未定义的。
在函数的返回类型上只允许使用精度限定符来修饰。
结构声明不允许作为参数声明或返回类型声明的一部分。
函数原型:
精度修饰符 类型 方法名(const修饰符 参数修饰符 精度修饰符 类型 名称 数组修饰符)
类型:
基础类型,结构名称,或结构定义
const修饰符:
空
const
参数修饰符:
空
in
out
inout
名称:
空
自定义
数组修饰符:
空
[数组长度]
但是,const限定符不能与out或inout一起使用。以上用于函数声明(即原型)和函数定义。 因此,函数定义可以具有未命名的参数(但是在实现方法的时候是需要有参数名的)。
不允许静态和动态递归,(很多底层芯片没有实现)。