着色语言

前言

Godot 使用类似于 GLSL ES 3.0 的着色语言。支持大多数数据类型和函数,并且可能会随着时间的推移添加剩余的几种类型和函数。

如果您已经熟悉 GLSL,Godot 着色器迁移指南是一个帮助您从常规 GLSL 转换到 Godot 着色语言的资源。

数据类型

支持大多数GLSL ES 3.0数据类型:

类型

描述

void

Void数据类型, 只对不返回任何内容的函数有用.

bool

布尔数据类型,只能包含 truefalse

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一样, 不允许在标量和相同大小但不同类型的向量之间进行隐式转换. 也不允许铸造不同大小的类型. 转换必须通过构造函数明确完成.

示例:

  1. float a = 2; // invalid
  2. float a = 2.0; // valid
  3. float a = float(2); // valid

默认整数常量是有符号的, 所以转换为无符号总是需要强制类型转换:

  1. int a = 2; // valid
  2. uint a = 2; // invalid
  3. uint a = uint(2); // valid

成员

向量类型的单个标量成员通过 “x” , “y” , “z” 和 “w” 成员访问. 另外, 使用 “r” , “g” , “b” 和 “a” 也可以, 而且是等效的. 使用最适合你的需求的方法.

对于矩阵,使用 m[column][row] 索引语法来访问每个标量,或者 m[idx] 按行索引来访问一个向量。例如,为了访问 mat4 中一个对象的 y 位置,使用 m[3][1]

构建

向量类型的构造必须始终通过:

  1. // The required amount of scalars
  2. vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
  3. // Complementary vectors and/or scalars
  4. vec4 a = vec4(vec2(0.0, 1.0), vec2(2.0, 3.0));
  5. vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0);
  6. // A single scalar for the whole vector
  7. vec4 a = vec4(0.0);

构建矩阵类型需要与矩阵相同维度的向量. 你也可以使用 matx(float) 语法构建一个对角矩阵. 相应地, mat4(1.0) 是一个单位矩阵.

  1. mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
  2. mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
  3. mat4 identity = mat4(1.0);

矩阵也可以由另一维的矩阵建立. 有两条规则. 如果一个较大的矩阵是由一个较小的矩阵构建的, 那么额外的行和列将被设置为它们在同一矩阵中的值. 如果一个较小的矩阵是由一个较大的矩阵构建的, 则使用较大矩阵的顶部和左矩阵.

  1. mat3 basis = mat3(WORLD_MATRIX);
  2. mat4 m4 = mat4(basis);
  3. mat2 m2 = mat2(m4);

混写Swizzling

只要结果是另一种向量类型(或标量), 就可以以任何顺序获得组件的组合. 这一点展示起来比解释起来容易:

  1. vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
  2. vec3 b = a.rgb; // Creates a vec3 with vec4 components.
  3. vec3 b = a.ggg; // Also valid; creates a vec3 and fills it with a single vec4 component.
  4. vec3 b = a.bgr; // "b" will be vec3(2.0, 1.0, 0.0).
  5. vec3 b = a.xyz; // Also rgba, xyzw are equivalent.
  6. vec3 b = a.stp; // And stpq (for texture coordinates).
  7. float c = b.w; // Invalid, because "w" is not present in vec3 b.
  8. vec3 c = b.xrt; // Invalid, mixing different styles is forbidden.
  9. b.rrr = a.rgb; // Invalid, assignment with duplication.
  10. b.bgr = a.rgb; // Valid assignment. "b"'s "blue" component will be "a"'s "red" and vice versa.

精度

可以为数据类型添加精度修饰符;将它们用于 uniform、变量、参数、varying:

  1. lowp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // low precision, usually 8 bits per component mapped to 0-1
  2. mediump vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // medium precision, usually 16 bits or half float
  3. 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].

  1. void fragment() {
  2. float arr[3];
  3. }

它们可以在开始时进行初始化, 例如:

  1. float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor
  2. int int_arr[3] = int[] (2, 1, 0); // second constructor
  3. vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor
  4. bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count

您可以在一个表达式中声明多个数组(即使大小不同):

  1. float a[3] = float[3] (1.0, 0.5, 0.0),
  2. b[2] = { 1.0, 0.5 },
  3. c[] = { 0.7 },
  4. d = 0.0,
  5. e[5];

要访问一个数组元素, 请使用索引语法:

  1. float arr[3];
  2. arr[0] = 1.0; // setter
  3. COLOR.r = arr[0]; // getter

数组有一个内置函数 .length()``(不要与内置的 ``length() 函数混淆). 它不接受任何参数, 作用是返回数组的大小.

  1. float arr[] = { 0.0, 1.0, 0.5, -1.0 };
  2. for (int i = 0; i < arr.length(); i++) {
  3. // ...
  4. }

备注

如果你使用一个低于0或大于数组大小的索引—着色器将崩溃并中断渲染. 为了防止这种情况, 请使用 length(), ifclamp() 函数来确保索引在0和数组的长度之间. 总是仔细测试和检查你的代码. 如果你传递了一个常量表达式或一个简单的数字, 编辑器会检查它的边界以防止这种崩溃.

常量

在变量声明前使用 const 关键字, 可以使该变量成为不可变的, 这意味着它不能被修改. 所有的基本类型, 除了采样器, 都可以被声明为常量. 访问和使用常量值的速度比使用uniform的速度略快. 常量必须在其声明时被初始化.

  1. const vec2 a = vec2(0.0, 1.0);
  2. vec2 b;
  3. a = b; // invalid
  4. b = a; // valid

常量不能被修改, 另外也不能有提示, 但可以在一个表达式中声明多个常量(如果它们具有相同的类型), 如

  1. const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);

与变量类似, 数组也可以用 const 来声明.

  1. const float arr[] = { 1.0, 0.5, 0.0 };
  2. arr[0] = 1.0; // invalid
  3. COLOR.r = arr[0]; // valid

常量可以在全局(在任何函数之外)或局部(在一个函数之内)进行声明. 当你想在整个着色器中访问一个不需要修改的值时, 全局常量很有用. 像uniform一样, 全局常量在所有着色器阶段之间共享, 但它们在着色器之外是不可访问的.

  1. shader_type spatial;
  2. const float PI = 3.14159265358979323846;

运算符

Godot 着色器语言支持与GLSL ES 3.0相同的操作符集. 下面是它们的优先级列表:

优先级

操作符

1(最高)

括号分组

()

2

单目

+, -, !, ~

3

乘除法

/, *, %

4

加减法

+, -

5

移位

<<, >>

6

关系比较

<, >, <=, >=

7

相等比较

==, !=

8

按位与

&

9

按位异或

^

10

按位或

|

11

逻辑与

&&

12(最低)

逻辑或

||

流控制

Godot 着色器语言支持最常见的控制流类型:

  1. // if and else
  2. if (cond) {
  3. } else {
  4. }
  5. // switch
  6. switch(i) { // signed integer expression
  7. case -1:
  8. break;
  9. case 0:
  10. return; // break or return
  11. case 1: // pass-through
  12. case 2:
  13. break;
  14. //...
  15. default: // optional
  16. break;
  17. }
  18. // for loops
  19. for (int i = 0; i < 10; i++) {
  20. }
  21. // while
  22. while (true) {
  23. }
  24. // do while
  25. do {
  26. } while(true);

请记住, 在现代GPU中, 无限循环可能存在, 并可能冻结你的应用程序(包括编辑器).Godot不能保护你不受影响, 所以要小心不要犯这个错误!

警告

导出 GLES2 项目到 HTML5 时,会使用 WebGL 1.0。WebGL 1.0 不支持动态循环,所以使用这一特性的着色器会失效。

丢弃

片段和灯光功能可以使用 discard 关键字. 如果使用, 则丢弃该片段并且不写入任何内容.

函数

可以在Godot着色器中定义函数. 它们使用以下语法:

  1. ret_type func_name(args) {
  2. return ret_type; // if returning a value
  3. }
  4. // a more specific example:
  5. int sum2(int a, int b) {
  6. return a + b;
  7. }

您只能使用上面定义的函数(编辑器中的较高位置)调用它们的函数.

函数参数可以有特殊的限定符:

  • in : 表示参数仅用于读取(默认).

  • out : 表示该参数只用于写入.

  • inout : 表示该参数以引用传递.

示例:

  1. void sum2(int a, int b, inout int result) {
  2. result = a + b;
  3. }

Varying

要从顶点处理器函数往片段(或者灯光)处理器函数里发送数据,可以使用 varying顶点处理器中的每一个图元顶点都是 varying 的,会为片段处理器中的每一个像素做插值。

  1. shader_type spatial;
  2. varying vec3 some_color;
  3. void vertex() {
  4. some_color = NORMAL; // Make the normal the color.
  5. }
  6. void fragment() {
  7. ALBEDO = some_color;
  8. }
  9. void light() {
  10. DIFFUSE_LIGHT = some_color * 100; // optionally
  11. }

Varying 也可以是数组:

  1. shader_type spatial;
  2. varying float var_arr[3];
  3. void vertex() {
  4. var_arr[0] = 1.0;
  5. var_arr[1] = 0.0;
  6. }
  7. void fragment() {
  8. ALBEDO = vec3(var_arr[0], var_arr[1], var_arr[2]); // red color
  9. }

也可以使用 varying 关键字将数据从片段处理器送往灯光处理器。在 fragment 函数中赋值,然后在 light 函数中使用即可。

  1. shader_type spatial;
  2. varying vec3 some_light;
  3. void fragment() {
  4. some_light = ALBEDO * 100.0; // Make a shining light.
  5. }
  6. void light() {
  7. DIFFUSE_LIGHT = some_light;
  8. }

注意,在自定义函数或灯光处理器中是不能为 varying 赋值的:

  1. shader_type spatial;
  2. varying float test;
  3. void foo() {
  4. test = 0.0; // Error.
  5. }
  6. void vertex() {
  7. test = 0.0;
  8. }
  9. void light() {
  10. test = 0.0; // Error too.
  11. }

加入这一限制的目的是为了防止在初始化前进行错误的使用。

插值限定符

在着色管线期间内插某些值. 您可以使用 插值限定符 修改这些插值的完成方式.

  1. shader_type spatial;
  2. varying flat vec3 our_color;
  3. void vertex() {
  4. our_color = COLOR.rgb;
  5. }
  6. void fragment() {
  7. ALBEDO = our_color;
  8. }

有两种可能的插值限定符:

限定符

描述

flat

该值未插值.

smooth

该值以透视正确的方式进行插值. 这是默认值.

Uniform

可以将值传递给着色器。这些值对整个着色器来说是全局的,被称为 uniform。当一个着色器后来被分配给一个材质时,uniform 将作为可编辑的参数出现在其中。uniform 不能从着色器内部写入。

  1. shader_type spatial;
  2. uniform float some_value;

你可以在编辑器中设置材质中的 uniform。或者你可以通过 GDScript 来设置它们:

  1. 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.

  1. shader_type spatial;
  2. uniform vec4 color : hint_color;
  3. uniform float amount : hint_range(0, 1);
  4. 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 也可以分配默认值:

  1. shader_type spatial;
  2. uniform vec4 some_vector = vec4(0.0);
  3. uniform vec4 some_color : hint_color = vec4(1.0);

内置变量

有大量类似 UVCOLORVERTEX 的内置变量可用。具体有哪些变量可用取决于着色器的类型( spatialcanvas_item 以及 particle )和所在的函数( vertexfragment 以及 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)

幂(x < 0 或 x = 0 且 y <= 0 时未定义)

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)

x 为小数部分, 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)

限制在 min..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)

b[i] < a[i] ? 0.0 : 1.0

vec_type step (float a, vec_type b)

b[i] < a ? 0.0 : 1.0

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)

如果标量或向量分量是 NaN 则返回 true

bvec_type isinf (vec_type x)

如果标量或向量分量是 INF , 则返回 true

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)

向量间距,即 length(a - 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)

如果 dot(Nref, I) <0, 则返回N, 否则返回-N

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)

任何组件都是 true

bool all (bvec_type x)

所有组件都是 true

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)

使用局部微分法进行 x 的微分

vec_type dFdy (vec_type p)

使用局部微分 y 的微分

vec_type fwidth (vec_type p)

xy 的绝对导数之和