着色语言
前言
Godot 使用类似于 GLSL ES 3.0 的着色语言。支持大多数数据类型和函数,并且可能会随着时间的推移添加剩余的几种类型和函数。
如果您已经熟悉 GLSL,Godot 着色器迁移指南是一个帮助您从常规 GLSL 转换到 Godot 着色语言的资源。
数据类型
支持大多数GLSL ES 3.0数据类型:
类型 | 描述 |
---|---|
void | Void数据类型, 只对不返回任何内容的函数有用. |
bool | 布尔数据类型,只能包含 |
bvec2 | 布尔值的两个分量向量. |
bvec3 | 布尔值的三分量向量. |
bvec4 | 布尔值的四分量向量. |
int | 带正负的符号标量整数. |
ivec2 | 有符号整数的双分量向量. |
ivec3 | 有符号整数的三分量向量. |
ivec4 | 有符号整数的四分量向量. |
uint | 无符号标量整数, 不能包含负数. |
uvec2 | 无符号整数的两分量向量. |
uvec3 | 无符号整数的三分量向量. |
uvec4 | 无符号整数的四分量向量. |
float | 浮点标量. |
vec2 | 浮点值的两分量向量. |
vec3 | 浮点值的三分量向量. |
vec4 | 浮点值的四分量向量. |
mat2 | 2x2矩阵, 按主要顺序排列. |
mat3 | 3x3矩阵, 在列的主要顺序. |
mat4 | 4x4矩阵, 按主要顺序排列. |
sampler2D | 用于绑定2D纹理的采样器类型, 以浮点形式读取. |
isampler2D | 用于绑定2D纹理的采样器类型, 它们被读取为有符号整数. |
usampler2D | 用于绑定2D纹理的采样器类型, 读取为无符号整数. |
sampler2DArray | 用于绑定2D纹理数组的采样器类型, 以浮点数形式读取. |
isampler2DArray | 用于绑定2D纹理数组的采样器类型, 以有符号整数形式读取. |
usampler2DArray | 用于绑定2D纹理数组的采样器类型, 以无符号整数形式读取. |
sampler3D | 用于绑定3D纹理的采样器类型, 以浮点形式读取. |
isampler3D | 用于绑定3D纹理的采样器类型, 以有符号整数形式读取. |
usampler3D | 用于绑定3D纹理的采样器类型, 以无符号整数形式读取. |
samplerCube | 用于绑定Cubemaps的采样器类型, 读取为浮点数. |
转换
就像GLSL ES 3.0一样, 不允许在标量和相同大小但不同类型的向量之间进行隐式转换. 也不允许铸造不同大小的类型. 转换必须通过构造函数明确完成.
示例:
float a = 2; // invalid
float a = 2.0; // valid
float a = float(2); // valid
默认整数常量是有符号的, 所以转换为无符号总是需要强制类型转换:
int a = 2; // valid
uint a = 2; // invalid
uint a = uint(2); // valid
成员
向量类型的单个标量成员通过 “x” , “y” , “z” 和 “w” 成员访问. 另外, 使用 “r” , “g” , “b” 和 “a” 也可以, 而且是等效的. 使用最适合你的需求的方法.
对于矩阵,使用 m[column][row]
索引语法来访问每个标量,或者 m[idx]
按行索引来访问一个向量。例如,为了访问 mat4 中一个对象的 y 位置,使用 m[3][1]
。
构建
向量类型的构造必须始终通过:
// The required amount of scalars
vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
// Complementary vectors and/or scalars
vec4 a = vec4(vec2(0.0, 1.0), vec2(2.0, 3.0));
vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0);
// A single scalar for the whole vector
vec4 a = vec4(0.0);
构建矩阵类型需要与矩阵相同维度的向量. 你也可以使用 matx(float)
语法构建一个对角矩阵. 相应地, mat4(1.0)
是一个单位矩阵.
mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
mat4 identity = mat4(1.0);
矩阵也可以由另一维的矩阵建立. 有两条规则. 如果一个较大的矩阵是由一个较小的矩阵构建的, 那么额外的行和列将被设置为它们在同一矩阵中的值. 如果一个较小的矩阵是由一个较大的矩阵构建的, 则使用较大矩阵的顶部和左矩阵.
mat3 basis = mat3(WORLD_MATRIX);
mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4);
混写Swizzling
只要结果是另一种向量类型(或标量), 就可以以任何顺序获得组件的组合. 这一点展示起来比解释起来容易:
vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
vec3 b = a.rgb; // Creates a vec3 with vec4 components.
vec3 b = a.ggg; // Also valid; creates a vec3 and fills it with a single vec4 component.
vec3 b = a.bgr; // "b" will be vec3(2.0, 1.0, 0.0).
vec3 b = a.xyz; // Also rgba, xyzw are equivalent.
vec3 b = a.stp; // And stpq (for texture coordinates).
float c = b.w; // Invalid, because "w" is not present in vec3 b.
vec3 c = b.xrt; // Invalid, mixing different styles is forbidden.
b.rrr = a.rgb; // Invalid, assignment with duplication.
b.bgr = a.rgb; // Valid assignment. "b"'s "blue" component will be "a"'s "red" and vice versa.
精度
可以为数据类型添加精度修饰符;将它们用于 uniform、变量、参数、varying:
lowp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // low precision, usually 8 bits per component mapped to 0-1
mediump vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // medium precision, usually 16 bits or half float
highp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // high precision, uses full float or integer range (default)
对某些操作使用较低的精度可以加快相关的数学运算(以较低的精度为代价). 这在顶点处理器功能中很少需要(大部分时间都需要全精度), 但在片段处理器中经常需要.
一些架构(主要是移动架构)可以从中受益匪浅, 但也有缺点, 比如在不同精度之间转换的额外开销. 请参考目标架构的文档以获得更多信息. 在许多情况下, 移动驱动会导致不一致或意外的行为, 除非有必要, 最好避免指定精度.
数组
数组是存放多个相似类型变量的容器. 注意: 从godot3.2开始, 只实现了局部数组和可变数组.
局部数组
局部数组在函数中声明. 它们可以使用所有允许的数据类型, 但采样器除外. 数组声明遵循C-style的语法. [const] + [precision] + typename + identifier + [array size]
.
void fragment() {
float arr[3];
}
它们可以在开始时进行初始化, 例如:
float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor
int int_arr[3] = int[] (2, 1, 0); // second constructor
vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor
bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count
您可以在一个表达式中声明多个数组(即使大小不同):
float a[3] = float[3] (1.0, 0.5, 0.0),
b[2] = { 1.0, 0.5 },
c[] = { 0.7 },
d = 0.0,
e[5];
要访问一个数组元素, 请使用索引语法:
float arr[3];
arr[0] = 1.0; // setter
COLOR.r = arr[0]; // getter
数组有一个内置函数 .length()``(不要与内置的 ``length()
函数混淆). 它不接受任何参数, 作用是返回数组的大小.
float arr[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr.length(); i++) {
// ...
}
备注
如果你使用一个低于0或大于数组大小的索引—着色器将崩溃并中断渲染. 为了防止这种情况, 请使用 length()
, if
或 clamp()
函数来确保索引在0和数组的长度之间. 总是仔细测试和检查你的代码. 如果你传递了一个常量表达式或一个简单的数字, 编辑器会检查它的边界以防止这种崩溃.
常量
在变量声明前使用 const
关键字, 可以使该变量成为不可变的, 这意味着它不能被修改. 所有的基本类型, 除了采样器, 都可以被声明为常量. 访问和使用常量值的速度比使用uniform的速度略快. 常量必须在其声明时被初始化.
const vec2 a = vec2(0.0, 1.0);
vec2 b;
a = b; // invalid
b = a; // valid
常量不能被修改, 另外也不能有提示, 但可以在一个表达式中声明多个常量(如果它们具有相同的类型), 如
const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);
与变量类似, 数组也可以用 const
来声明.
const float arr[] = { 1.0, 0.5, 0.0 };
arr[0] = 1.0; // invalid
COLOR.r = arr[0]; // valid
常量可以在全局(在任何函数之外)或局部(在一个函数之内)进行声明. 当你想在整个着色器中访问一个不需要修改的值时, 全局常量很有用. 像uniform一样, 全局常量在所有着色器阶段之间共享, 但它们在着色器之外是不可访问的.
shader_type spatial;
const float PI = 3.14159265358979323846;
运算符
Godot 着色器语言支持与GLSL ES 3.0相同的操作符集. 下面是它们的优先级列表:
优先级 | 类 | 操作符 |
1(最高) | 括号分组 | () |
2 | 单目 | +, -, !, ~ |
3 | 乘除法 | /, *, % |
4 | 加减法 | +, - |
5 | 移位 | <<, >> |
6 | 关系比较 | <, >, <=, >= |
7 | 相等比较 | ==, != |
8 | 按位与 | & |
9 | 按位异或 | ^ |
10 | 按位或 | | |
11 | 逻辑与 | && |
12(最低) | 逻辑或 | || |
流控制
Godot 着色器语言支持最常见的控制流类型:
// if and else
if (cond) {
} else {
}
// switch
switch(i) { // signed integer expression
case -1:
break;
case 0:
return; // break or return
case 1: // pass-through
case 2:
break;
//...
default: // optional
break;
}
// for loops
for (int i = 0; i < 10; i++) {
}
// while
while (true) {
}
// do while
do {
} while(true);
请记住, 在现代GPU中, 无限循环可能存在, 并可能冻结你的应用程序(包括编辑器).Godot不能保护你不受影响, 所以要小心不要犯这个错误!
警告
导出 GLES2 项目到 HTML5 时,会使用 WebGL 1.0。WebGL 1.0 不支持动态循环,所以使用这一特性的着色器会失效。
丢弃
片段和灯光功能可以使用 discard 关键字. 如果使用, 则丢弃该片段并且不写入任何内容.
函数
可以在Godot着色器中定义函数. 它们使用以下语法:
ret_type func_name(args) {
return ret_type; // if returning a value
}
// a more specific example:
int sum2(int a, int b) {
return a + b;
}
您只能使用上面定义的函数(编辑器中的较高位置)调用它们的函数.
函数参数可以有特殊的限定符:
in : 表示参数仅用于读取(默认).
out : 表示该参数只用于写入.
inout : 表示该参数以引用传递.
示例:
void sum2(int a, int b, inout int result) {
result = a + b;
}
Varying
要从顶点处理器函数往片段(或者灯光)处理器函数里发送数据,可以使用 varying。顶点处理器中的每一个图元顶点都是 varying 的,会为片段处理器中的每一个像素做插值。
shader_type spatial;
varying vec3 some_color;
void vertex() {
some_color = NORMAL; // Make the normal the color.
}
void fragment() {
ALBEDO = some_color;
}
void light() {
DIFFUSE_LIGHT = some_color * 100; // optionally
}
Varying 也可以是数组:
shader_type spatial;
varying float var_arr[3];
void vertex() {
var_arr[0] = 1.0;
var_arr[1] = 0.0;
}
void fragment() {
ALBEDO = vec3(var_arr[0], var_arr[1], var_arr[2]); // red color
}
也可以使用 varying 关键字将数据从片段处理器送往灯光处理器。在 fragment 函数中赋值,然后在 light 函数中使用即可。
shader_type spatial;
varying vec3 some_light;
void fragment() {
some_light = ALBEDO * 100.0; // Make a shining light.
}
void light() {
DIFFUSE_LIGHT = some_light;
}
注意,在自定义函数或灯光处理器中是不能为 varying 赋值的:
shader_type spatial;
varying float test;
void foo() {
test = 0.0; // Error.
}
void vertex() {
test = 0.0;
}
void light() {
test = 0.0; // Error too.
}
加入这一限制的目的是为了防止在初始化前进行错误的使用。
插值限定符
在着色管线期间内插某些值. 您可以使用 插值限定符 修改这些插值的完成方式.
shader_type spatial;
varying flat vec3 our_color;
void vertex() {
our_color = COLOR.rgb;
}
void fragment() {
ALBEDO = our_color;
}
有两种可能的插值限定符:
限定符 | 描述 |
---|---|
flat | 该值未插值. |
smooth | 该值以透视正确的方式进行插值. 这是默认值. |
Uniform
可以将值传递给着色器。这些值对整个着色器来说是全局的,被称为 uniform。当一个着色器后来被分配给一个材质时,uniform 将作为可编辑的参数出现在其中。uniform 不能从着色器内部写入。
shader_type spatial;
uniform float some_value;
你可以在编辑器中设置材质中的 uniform。或者你可以通过 GDScript 来设置它们:
material.set_shader_param("some_value", some_value)
备注
set_shader_param
的第一个参数是着色器中的 uniform 名称。它必须与着色器中的 uniform 名称完全一致,否则将无法被识别。
Any GLSL type except for void can be a uniform. Additionally, Godot provides optional shader hints to make the compiler understand for what the uniform is used, and how the editor should allow users to modify it.
shader_type spatial;
uniform vec4 color : hint_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : hint_color = vec4(1.0);
重要的是要明白,以颜色形式提供的纹理需要适当的 sRGB->线性转换的提示(即 hint_albedo
),因为 Godot 的 3D 引擎在线性颜色空间中渲染。
以下是完整的提示列表:
类型 | 提示 | 描述 |
---|---|---|
vec4 | hint_color | Used as color. |
int、float | hint_range(min,max [,step] ) | Restricted to values in a range (with min/max/step). |
sampler2D | hint_albedo | Used as albedo color, default white. |
sampler2D | hint_black_albedo | Used as albedo color, default black. |
sampler2D | hint_normal | Used as normalmap. |
sampler2D | hint_white | 作为值,默认为白色。 |
sampler2D | hint_black | 作为值,默认为黑色 |
sampler2D | hint_aniso | 作为 FlowMap,默认为右。 |
GDScript 使用的变量类型与 GLSL 不同,所以当把变量从 GDScript 传递到着色器时,Godot 会自动转换类型。以下是相应类型的表格:
GDScript 类型 | GLSL 类型 |
---|---|
bool | bool |
int | int |
float | float |
Vector2 | vec2 |
Vector3 | vec3 |
Color | vec4 |
Transform | mat4 |
Transform2D | mat4 |
备注
当从 GDScript 中设置着色器 uniform 时要小心,如果类型不匹配,不会产生错误。你的着色器只会表现出未定义的行为。
Uniform 也可以分配默认值:
shader_type spatial;
uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : hint_color = vec4(1.0);
内置变量
有大量类似 UV
、 COLOR
、 VERTEX
的内置变量可用。具体有哪些变量可用取决于着色器的类型( spatial
、 canvas_item
以及 particle
)和所在的函数( vertex
、 fragment
以及 light
)。可用的内置变量列表见对应的页面:
内置函数
支持大量的内置函数, 符合GLSL ES 3.0. 当使用 vec_type (float), vec_int_type, vec_uint_type, vec_bool_type 命名法时, 它可以是标量或向量.
备注
关于GLES2后端无法使用的功能列表, 请参见 GLES2和GLES3之间的差异文档 .
函数 | 描述 |
---|---|
vec_type radians (vec_type degrees度) | 将度数转换为弧度 |
vec_type degrees ( vec_type radians弧度) | 将弧度转换为度数 |
vec_type sin (vec_type x) | 正弦 |
vec_type cos (vec_type x) | 余弦 |
vec_type tan (vec_type x) | 正切 |
vec_type asin (vec_type x) | 反正弦 |
vec_type acos (vec_type x) | 反余弦 |
vec_type atan (vec_type y_over_x) | 反正切 |
vec_type atan (vec_type y, vec_type x) | 将向量转换为角度的反正切 |
vec_type sinh (vec_type x) | 双曲正弦 |
vec_type cosh (vec_type x) | 双曲余弦 |
vec_type tanh (vec_type x) | 双曲正切 |
vec_type asinh (vec_type x) | 反双曲正弦 |
vec_type acosh (vec_type x) | 反双曲余弦 |
vec_type atanh (vec_type x) | 反双曲正切 |
vec_type pow (vec_type x, vec_type y) | 幂( |
vec_type exp (vec_type x) | 基数-e指数 |
vec_type exp2 (vec_type x) | 基数-2指数 |
vec_type log (vec_type x) | 自然对数 |
vec_type log2 (vec_type x) | 基数-2对数 |
vec_type sqrt (vec_type x) | 平方根 |
vec_type inversesqrt (vec_type x) | 反平方根 |
vec_type abs (vec_type x) | 绝对 |
ivec_type abs (ivec_type x) | 绝对 |
vec_type sign ( vec_type ) | 符号 |
ivec_type sign (ivec_type x) | 符号 |
vec_type floor (vec_type x) | 向下取整 |
vec_type round (vec_type x) | 四舍五入 |
vec_type roundEven (vec_type x) | 四舍五入到最接近的偶数 |
vec_type trunc (vec_type x) | 截断 |
vec_type ceil (vec_type x) | 向上取整 |
vec_type fract (vec_type x) | 分数部分Fractional |
vec_type mod (vec_type x, vec_type y) | 余 |
vec_type mod (vec_type x , float y) | 余 |
vec_type modf (vec_type x, out vec_type i) |
|
vec_type min (vec_type a, vec_type b) | 最小值 |
vec_type max (vec_type a, vec_type b) | 最大值 |
vec_type clamp (vec_type x, vec_type min, vec_type max) | 限制在 |
float mix (float a, float b, float c) | 线性插值 |
vec_type mix (vec_type a, vec_type b, float c) | 线性插值(标量系数) |
vec_type mix (vec_type a, vec_type b, vec_type c) | 线性插值(向量系数) |
vec_type mix (vec_type a, vec_type b, bvec_type c) | 线性插值(布尔向量选择) |
vec_type step (vec_type a, vec_type b) |
|
vec_type step (float a, vec_type b) |
|
vec_type smoothstep (vec_type a, vec_type b, vec_type c) | Hermite 插值 |
vec_type smoothstep (float a, float b, vec_type c) | Hermite 插值 |
bvec_type isnan (vec_type x) | 如果标量或向量分量是 |
bvec_type isinf (vec_type x) | 如果标量或向量分量是 |
ivec_type floatBitsToInt (vec_type x) | Float->Int 位复制,无转换 |
uvec_type floatBitsToUint (vec_type x) | Float->UInt 位复制,无转换 |
vec_type intBitsToFloat (ivec_type x) | Int-> Float 位复制,无转换 |
vec_type uintBitsToFloat (uvec_type x) | UInt->Float 位复制,无转换 |
float length (vec_type x) | 向量长度 |
float distance (vec_type a, vec_type b) | 向量间距,即 |
float dot (vec_type a, vec_type b) | 点积 |
vec3 cross (vec3 a, vec3 b) | 叉积 |
vec_type normalize (vec_type x) | 标准化为单位长度 |
vec3 reflect (vec3 I, vec3 N) | 反射 |
vec3 refract (vec3 I, vec3 N, float eta) | 折射 |
vec_type faceforward (vec_type N, vec_type I, vec_type Nref) | 如果 |
mat_type matrixCompMult (mat_type x, mat_type y) | 矩阵分量乘法 |
mat_type outerProduct (vec_type column, vec_type row) | 矩阵外积 |
mat_type transpose (mat_type m) | 转置矩阵 |
float determinant (mat_type m) | 矩阵行列式 |
mat_type inverse (mat_type m) | 逆矩阵 |
bvec_type lessThan (vec_type x, vec_type y) | Bool vector 对比 < int/uint/float vectors |
bvec_type greaterThan (vec_type x, vec_type y) | Bool vector 对比 > int/uint/float vectors |
bvec_type lessThanEqual (vec_type x, vec_type y) | Bool vector 对比 <= int/uint/float vectors |
bvec_type greaterThanEqual (vec_type x, vec_type y) | Bool vector 对比 >= int/uint/float vectors |
bvec_type equal (vec_type x, vec_type y) | Bool vector 对比 == int/uint/float vectors |
bvec_type notEqual (vec_type x, vec_type y) | Bool vector 对比 != int/uint/float vectors |
bool any (bvec_type x) | 任何组件都是 |
bool all (bvec_type x) | 所有组件都是 |
bvec_type not (bvec_type x) | 反转布尔向量 |
ivec2 textureSize (sampler2D_type s, int lod) | 获取2D纹理的大小 |
ivec3 textureSize (sampler2DArray_type s, int lod) | 获取2D纹理数组的大小 |
ivec3 textureSize (sampler3D s, int lod) | 获取3D纹理的大小 |
ivec2 textureSize (samplerCube s, int lod) | 获取cubemap纹理的大小 |
vec4_type texture (sampler2D_type s, vec2 uv [, float bias]) | 执行2D纹理读取 |
vec4_type texture (sampler2DArray_type s, vec3 uv [, float bias]) | 执行2D纹理数组读取 |
vec4_type texture (sampler3D_type s, vec3 uv [, float bias]) | 执行3D纹理读取 |
vec4 texture (samplerCube s, vec3 uv [, float bias]) | 执行立方体贴图纹理读取 |
vec4_type textureProj (sampler2D_type s, vec3 uv [, float bias]) | 执行带投影的2D纹理读取 |
vec4_type textureProj (sampler2D_type s, vec4 uv [, float bias]) | 执行带投影的2D纹理读取 |
vec4_type textureProj (sampler3D_type s, vec4 uv [, float bias]) | 执行带投影的3D纹理读取 |
vec4_type textureLod (sampler2D_type s, vec2 uv, float lod) | 在自定义mipmap上执行2D纹理读取 |
vec4_type textureLod (sampler2DArray_type s, vec3 uv, float lod) | 执行在自定义mipmap处2D纹理阵列读取 |
vec4_type textureLod (sampler3D_type s, vec3 uv, float lod) | 执行在自定义mipmap上3D纹理读取 |
vec4 textureLod (samplerCube s, vec3 uv, float lod) | 执行在自定义mipmap上3D纹理读取 |
vec4_type textureProjLod (sampler2D_type s, vec3 uv, float lod) | 执行带投影/LOD的2D纹理读取 |
vec4_type textureProjLod (sampler2D_type s, vec4 uv, float lod) | 执行带投影/LOD的2D纹理读取 |
vec4_type textureProjLod (sampler3D_type s, vec4 uv, float lod) | 执行带投影/LOD的3D纹理读取 |
vec4_type texelFetch (sampler2D_type s, ivec2 uv, int lod) | 使用整数坐标获取单个纹素 |
vec4_type texelFetch (sampler2DArray_type s, ivec3 uv, int lod) | 使用整数坐标获取单个纹素 |
vec4_type texelFetch (sampler3D_type s, ivec3 uv, int lod) | 使用整数坐标获取单个纹素 |
vec_type dFdx (vec_type p) | 使用局部微分法进行 |
vec_type dFdy (vec_type p) | 使用局部微分 |
vec_type fwidth (vec_type p) |
|