Язык шейдеров

Введение

Godot использует язык шейдеров схожий с GLSL ES 3.0. Поддерживаются большинство типов данных и функций, а те что остались за бортом, скорее всего будет добавлены в будущем.

Если вы уже знакомы с GLSL, то Руководство по переходу на шейдеры Godot - это ресурс, который поможет вам перейти с обычного GLSL на язык шейдеров Godot.

Типы данных

Поддерживаются большинство типов данных из GLSL ES 3.0:

Тип

Описание

voіd

Пустой тип данных, используется только для функций которые ничего не возвращают.

bool

Булевый (логический) тип данных, может содержать только true или false.

bvеc2

Двух-компонентный вектор из булевых значений.

bvеc3

Трех-компонентный вектор из булевых значений.

bveс4

Четырех-компонентный вектор из булевых значений.

int

32 bit signed scalar integer.

ivеc2

Двух-компонентный вектор из целых чисел.

ivеc3

Трёх-компонентный вектор из целых чисел.

iveс4

Четырёх-компонентный вектор из целых чисел.

uіnt

Целое число без знака; не может содержать отрицательных значений.

uvеc2

Двух-компонентный вектор состоящий из беззнаковых чисел.

uvеc3

Трёх-компонентный вектор из беззнаковых целых чисел.

uvеc4

Четырёх-компонентный вектор из беззнаковых целых чисел.

float

32 bit floating-point scalar.

vеc2

Двух-компонентный вектор из чисел с плавающей точкой.

vеc3

Трёх-компонентный вектор из чисел с плавающей точкой.

vеc4

Четырёх-компонентный вектор из чисел с плавающей точкой.

mаt2

Матрица 2x2, с развёрткой по столбцу.

mаt3

Матрица 3x3, с развёрткой по столбцу.

mаt4

Матрица 4x4, с развёрткой по столбцу.

samplеr2D

Тип сэмплера, для связки 2D текстур, которые читаются через числа с плавающей точкой.

isamplеr2D

Тип сэмплера, для связки 2D текстур, которые читаются через целые числа.

usamplеr2D

Тип сэмплера, для связки 2D текстур, которые читаются через целые беззнаковые числа.

sampler2DArray

Тип сэмплера, для связки 2D текстур, которые читаются через числа с плавающей точкой.

isampler2DArray

Тип сэмплера, для связки 2D текстур, которые читаются через целые числа.

usampler2DArray

Тип сэмплера, для связки 2D текстур, которые читаются через целые беззнаковые числа.

sampler3D

Тип сэмплера, для связки 2D текстур, которые читаются через числа с плавающей точкой.

isampler3D

Тип сэмплера, для связки 2D текстур, которые читаются через целые числа.

usampler3D

Тип сэмплера, для связки 2D текстур, которые читаются через целые беззнаковые числа.

samplerCubе

Тип сэмплера, для связки кубических текстур, которые читаются через числа с плавающей точкой.

samplerCubeArray

Sampler type for binding Cubemap arrays, which are read as float. Only supported in Forward+ and Mobile, not Compatibility.

samplerExternalOES

External sampler type. Only supported in Compatibility/Android platform.

Предупреждение

Local variables are not initialized to a default value such as 0.0. If you use a variable without assigning it first, it will contain whatever value was already present at that memory location, and unpredictable visual glitches will appear. However, uniforms and varyings are initialized to a default value.

Комментарии

The shading language supports the same comment syntax as used in C# and C++, using // for single-line comments and /* */ for multi-line comments:

// Single-line comment.
int a = 2;  // Another single-line comment.

/*
Multi-line comment.
The comment ends when the ending delimiter is found
(here, it's on the line below).
*/
int b = 3;

Кроме того, вы можете использовать комментарии к документации, которые отображаются в инспекторе при наведении курсора на параметр шейдера. В настоящее время комментарии документации поддерживаются только при размещении непосредственно над объявлением uniform. Эти комментарии поддерживают только синтаксис многострочных комментариев и должны использовать две ведущие звездочки (/**), а не одну (/*):

/**
 * This is a documentation comment.
 * These lines will appear in the inspector when hovering the shader parameter
 * named "Something".
 * You can use [b]BBCode[/b] [i]formatting[/i] in the comment.
 */
uniform int something = 1;

Звёздочки в последующих строках не обязательны, но рекомендуются согласно Shaders style guide. Эти звёздочки автоматически удаляются инспектором, поэтому они не будут отображаться во всплывающей подсказке.

Приведение переменных

Так же как и в 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". Используйте то, что вы считаете лучшим для ваших задач.

For matrices, use the m[column][row] indexing syntax to access each scalar, or m[column] to access a vector by column index. For example, for accessing the y-component of the translation from a mat4 transform matrix (4th column, 2nd line) you use m[3][1] or m[3].y.

Создание

При создании векторного типа вы всегда должны передавать:

// 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);

Construction of matrix types requires vectors of the same dimension as the matrix, interpreted as columns. You can also build a diagonal matrix using matx(float) syntax. Accordingly, mat4(1.0) is an identity matrix.

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);

Матрицы также могут быть построены из матрицы другой размерности. Существует два правила:

1. If a larger matrix is constructed from a smaller matrix, the additional rows and columns are set to the values they would have in an identity matrix. 2. If a smaller matrix is constructed from a larger matrix, the top, left submatrix of the larger matrix is used.

mat3 basis = mat3(MODEL_MATRIX);
mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4);

Перестановка

Можно получить любую комбинацию компонентов в любом порядке, если в результате получается другой тип вектора (или скаляр). Это легче показать, чем объяснить:

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.

Точность

Можно добавить модификаторы точности к типам данных; используйте их для униформ, переменных, аргументов и вариаций:

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 (32 bit default)

Использование более низкой точности для некоторых операций может ускорить вычисление (за счет меньшей точности). Это редко требуется в функции вершинного процессора (где большую часть времени требуется полная точность), но часто полезно во фрагментном процессоре.

Некоторые архитектуры (в основном мобильные) могут значительно выиграть от этого, но есть и минусы, например, дополнительные накладные расходы на преобразование между точными значениями. За дополнительной информацией обратитесь к документации по целевой архитектуре. Во многих случаях мобильные драйверы вызывают непоследовательное или неожиданное поведение, поэтому лучше не указывать точность, если это не требуется.

Массивы

Массивы - это контейнеры для нескольких переменных одного типа.

Локальные массивы

Локальные массивы объявляются в функциях. Они могут использовать все разрешённые типы данных, кроме сэмплеров. Объявление массива следует синтаксису в стиле C: [константа] + [точность] + имя_типа + идентификатор + [размер массива].

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 и длиной массива. Всегда тщательно тестируйте и проверяйте свой код. Если вы передадите константное выражение или число, редактор проверит его границы, чтобы предотвратить этот сбой.

Глобальные массивы

You can declare arrays in global space as either const or uniform:

shader_type spatial;

const lowp vec3 v[1] = lowp vec3[1] ( vec3(0, 0, 1) );
uniform lowp vec3 w[1];

void fragment() {
  ALBEDO = v[0] + w[0];
}

Примечание

Global arrays use the same syntax as local arrays, except with a const or uniform added to their declaration. Note that uniform arrays can't have a default value.

Константы

Используйте ключевое слово 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 GOLDEN_RATIO = 1.618033988749894;

Константы типа float должны инициализироваться с помощью обозначения . после десятичной части или с помощью научной нотации. Также поддерживается необязательный постсуффикс f.

float a = 1.0;
float b = 1.0f; // same, using suffix for clarity
float c = 1e-1; // gives 0.1 by using the scientific notation

Константы типа uint (целое беззнаковое) должны иметь суффикс u, чтобы отличать их от знаковых целых чисел. Альтернативно это можно сделать с помощью встроенной функции преобразования uint(x).

uint a = 1u;
uint b = uint(1);

Структуры

Структуры - это составные типы, которые можно использовать для лучшей абстракции шейдерного кода. Вы можете объявить их в глобальной области видимости, например:

struct PointLight {
    vec3 position;
    vec3 color;
    float intensity;
};

После объявления их можно инстанцировать и инициализировать, например:

void fragment()
{
    PointLight light;
    light.position = vec3(0.0);
    light.color = vec3(1.0, 0.0, 0.0);
    light.intensity = 0.5;
}

Или используйте конструктор struct для той же цели:

PointLight light = PointLight(vec3(0.0), vec3(1.0, 0.0, 0.0), 0.5);

Структуры могут содержать другие структуры или массивы, вы также можете использовать их в качестве глобальных констант:

shader_type spatial;

...

struct Scene {
    PointLight lights[2];
};

const Scene scene = Scene(PointLight[2](PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0), PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0)));

void fragment()
{
    ALBEDO = scene.lights[0].color;
}

Вы также можете передавать их в функции:

shader_type canvas_item;

...

Scene construct_scene(PointLight light1, PointLight light2) {
    return Scene({light1, light2});
}

void fragment()
{
    COLOR.rgb = construct_scene(PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0), PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 1.0), 1.0)).lights[0].color;
}

Операторы

Язык шейдеров Godot поддерживает тот же набор операторов, что и GLSL ES 3.0. Ниже приведён их список в порядке старшинства:

Приоритет

Класс

Оператор

1 (высший)

группировка скобок

()

2

унарный (unary)

+, -, !, ~

3

мультипликативный (multiplicative)

/, *, %

4

аддитивный (additive)

+, -

5

побитовый сдвиг

<<, >>

6

реляционный (relational)

<, >, <=, >=

7

равенства (equality)

==, !=

8

побитовое И (AND)

&

9

побитовое исключающее ИЛИ

^

10

побитовое включающее ИЛИ

|

11

логическое И

&&

12 (низший)

логическое включающее ИЛИ

||

Примечание

Most operators that accept vectors or matrices (multiplication, division, etc) operate component-wise, meaning the function is applied to the first value of each vector and then on the second value of each vector, etc. Some examples:

Operation

Equivalent Scalar Operation

vec3(4, 5, 6) + 2

vec3(4 + 2, 5 + 2, 6 + 2)

vec2(3, 4) * vec2(10, 20)

vec2(3 * 10, 4 * 20)

mat2(vec2(1, 2), vec2(3, 4)) + 10

mat2(vec2(1 + 10, 2 + 10), vec2(3 + 10, 4 + 10))

The GLSL Language Specification says under section 5.10 Vector and Matrix Operations:

With a few exceptions, operations are component-wise. Usually, when an operator operates on a vector or matrix, it is operating independently on each component of the vector or matrix, in a component-wise fashion. [...] The exceptions are matrix multiplied by vector, vector multiplied by matrix, and matrix multiplied by matrix. These do not operate component-wise, but rather perform the correct linear algebraic multiply.

Контроль над потоком

Шейдерный язык Godot поддерживает наиболее распространённые типы управления потоком:

// `if`, `else if` and `else`.
if (cond) {

} else if (other_cond) {

} else {

}

// Ternary operator.
// This is an expression that behaves like `if`/`else` and returns the value.
// If `cond` evaluates to `true`, `result` will be `9`.
// Otherwise, `result` will be `5`.
int result = cond ? 9 : 5;

// `switch`.
switch (i) { // `i` should be a signed integer expression.
    case -1:
        break;
    case 0:
        return; // `break` or `return` to avoid running the next `case`.
    case 1: // Fallthrough (no `break` or `return`): will run the next `case`.
    case 2:
        break;
    //...
    default: // Only run if no `case` above matches. Optional.
        break;
}

// `for` loop. Best used when the number of elements to iterate on
// is known in advance.
for (int i = 0; i < 10; i++) {

}

// `while` loop. Best used when the number of elements to iterate on
// is not known in advance.
while (cond) {

}

// `do while`. Like `while`, but always runs at least once even if `cond`
// never evaluates to `true`.
do {

} while (cond);

Помните, что в современных GPU может существовать бесконечный цикл, который может заморозить ваше приложение (включая редактор). Godot не может защитить вас от этого, поэтому будьте осторожны и не допускайте такой ошибки!

Кроме того, сравнивая значения с плавающей точкой с числом, убедитесь, что они сравниваются с диапазоном, а не с точным числом.

Сравнение типа if (value == 0.3) может не привести к значению true. Математика с плавающей точкой часто является приблизительной и может не соответствовать ожиданиям. Кроме того, она может вести себя по-разному в зависимости от аппаратного обеспечения.

Не делайте этого.

float value = 0.1 + 0.2;

// May not evaluate to `true`!
if (value == 0.3) {
    // ...
}

Вместо этого всегда выполняйте сравнение диапазонов со значением эпсилон. Чем больше число с плавающей точкой (и чем менее точное число с плавающей точкой), тем больше должно быть значение эпсилон.

const float EPSILON = 0.0001;
if (value >= 0.3 - EPSILON && value <= 0.3 + EPSILON) {
    // ...
}

Дополнительную информацию см. в разделе floating-point-gui.de.

Отбрасывание

Fragment, light, and custom functions (called from fragment or light) can use the discard keyword. If used, the fragment is discarded and nothing is written.

Имейте в виду, что использование discard приводит к снижению производительности, так как не позволяет препассу глубины быть эффективным на любых поверхностях, использующих этот шейдер. Кроме того, отброшенный пиксель всё равно должен быть отрисован в вершинном шейдере, что означает, что шейдер, использующий 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: Означает, что аргумент полностью передаётся по ссылке.

  • const: Означает, что аргумент является константой и не может быть изменён, может сочетаться с квалификатором in.

Пример ниже:

void sum2(int a, int b, inout int result) {
    result = a + b;
}

Function overloading is supported. You can define multiple functions with the same name, but different arguments. Note that implicit casting in overloaded function calls is not allowed, such as from int to float (1 to 1.0).

vec3 get_color(int t) {
    return vec3(1, 0, 0); // Red color.
}
vec3 get_color(float t) {
    return vec3(0, 1, 0); // Green color.
}
void fragment() {
    vec3 red = get_color(1);
    vec3 green = get_color(1.0);
}

Varуings

Для передачи данных от функций вершинного процессора к функции фрагментного процессора (или света) используются varyings. Они устанавливаются для каждой примитивной вершины в вершинном процессоре, и значение интерполируется для каждого пикселя во фрагментном процессоре.

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
}

Также можно передавать данные из фрагментного (fragment) процессора в процессоры light, используя ключевое слово 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;
}

Обратите внимание, что переменные не могут быть назначены в пользовательских функциях или функциях светового процессора (light), например:

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;
}

Существует два возможных квалификатора интерполяции:

Квалификатор

Описание

flаt

Значение не интерполируется.

smоoth

Значение интерполируется с учётом коррекции перспективы. Это используется по умолчанию.

Uniform-переменные

Passing values to shaders is possible with uniforms, which are defined in the global scope of the shader, outside of functions. When a shader is later assigned to a material, the uniforms will appear as editable parameters in the material's inspector. Uniforms can't be written from within the shader. Any data type except for void can be a uniform.

shader_type spatial;

uniform float some_value;

uniform vec3 colors[3];

Вы можете задать униформы в редакторе в инспекторе материалов. Также их можно задать из кода.

Uniform hints

Godot provides optional uniform hints to make the compiler understand what the uniform is used for, and how the editor should allow users to modify it.

shader_type spatial;

uniform vec4 color : source_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : source_color = vec4(1.0); // Default values go after the hint.
uniform sampler2D image : source_color;

Униформе также можно присвоить значения по умолчанию:

shader_type spatial;

uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : source_color = vec4(1.0);

Обратите внимание, что при добавлении значения по умолчанию и подсказки, значение по умолчанию идёт после подсказки.

Full list of uniform hints below:

Тип

Подсказка

Описание

vec3, vec4

source_color

Используется как цвет.

int

hint_enum("String1", "String2")

Displays int input as a dropdown widget in the editor.

int, float

hint_range(min, max[, step])

Ограничение на значения в диапазоне (мин/макс/шаг).

samplеr2D

source_color

Используется как цвет альбедо.

samplеr2D

hіnt_normal

Используется как карта нормалей.

samplеr2D

hint_default_white

В качестве значения или цвета альбедо, по умолчанию - непрозрачный белый.

samplеr2D

hint_default_black

В качестве значения или цвета альбедо, по умолчанию - непрозрачный чёрный.

samplеr2D

hint_default_transparent

В качестве значения или цвета альбедо, по умолчанию - прозрачный черный.

samplеr2D

hint_anisotropy

Как карта потока, по умолчанию справа.

samplеr2D

hint_roughness[_r, _g, _b, _a, _normal, _gray]

Используется для ограничителя шероховатости при импорте (попытки уменьшить сглаживание блика). _normal - это карта нормалей, которая направляет работу ограничителя шероховатости, при этом шероховатость увеличивается в областях с высокочастотными деталями.

samplеr2D

filter[_nearest, _linear][_mipmap][_anisotropic]

Включает фильтрацию заданных текстур.

samplеr2D

repeat[_enable, _disable]

Включает повтор текстуры.

samplеr2D

hint_screen_texture

Texture - это текстура экрана.

samplеr2D

hint_depth_texture

Текстура глубины.

samplеr2D

hint_normal_roughness_texture

Texture - обычная текстура шероховатости (поддерживается только в Forward+).

Using hint_enum

You can access int values as a readable dropdown widget using the hint_enum uniform:

uniform int noise_type : hint_enum("OpenSimplex2", "Cellular", "Perlin", "Value") = 0;

You can assign explicit values to the hint_enum uniform using colon syntax similar to GDScript:

uniform int character_speed: hint_enum("Slow:30", "Average:60", "Very Fast:200") = 60;

The value will be stored as an integer, corresponding to the index of the selected option (i.e. 0, 1, or 2) or the value assigned by colon syntax (i.e. 30, 60, or 200). When setting the value with set_shader_parameter(), you must use the integer value, not the String name.

Using source_color

Any texture which contains sRGB color data requires a source_color hint in order to be correctly sampled. This is because Godot renders in linear color space, but some textures contain sRGB color data. If this hint is not used, the texture will appear washed out.

Albedo and color textures should typically have a source_color hint. Normal, roughness, metallic, and height textures typically do not need a source_color hint.

Using source_color hint is required in the Forward+ and Mobile renderers, and in canvas_item shaders when HDR 2D is enabled. The source_color hint is optional for the Compatibility renderer, and for canvas_item shaders if HDR 2D is disabled. However, it is recommended to always use the source_color hint, because it works even if you change renderers or disable HDR 2D.

Uniform groups

To group multiple uniforms in a section in the inspector, you can use a group_uniform keyword like this:

group_uniforms MyGroup;
uniform sampler2D test;

Вы можете закрыть группу, используя:

group_uniforms;

Синтаксис также поддерживает подгруппы (перед этим необязательно объявлять базовую группу):

group_uniforms MyGroup.MySubgroup;

Глобальные uniforms

Иногда требуется изменить параметр сразу в нескольких шейдерах. При использовании обычной униформы это требует много работы, поскольку необходимо отслеживать все шейдеры и устанавливать униформу для каждого из них. Глобальные униформы позволяют создавать и обновлять униформы, которые будут доступны во всех шейдерах, в каждом типе шейдеров (canvas_item, spatial, particles, sky и fog).

Глобальные униформы особенно полезны для эффектов окружающей среды, которые влияют на многие объекты в сцене, например, когда листва изгибается, когда рядом находится игрок, или когда объекты движутся под действием ветра.

Примечание

Global uniforms are not the same as global scope for an individual shader. While regular uniforms are defined outside of shader functions and are therefore the global scope of the shader, global uniforms are global to all shaders in the entire project (but within each shader, are also in the global scope).

Чтобы создать глобальную униформу, откройте Project Settings и перейдите на вкладку Shader Globals. Укажите имя униформы (с учетом регистра) и тип, затем нажмите Add в правом верхнем углу диалога. Затем вы можете изменить значение, присвоенное униформе, щёлкнув значение в списке:

Добавление глобальной униформы на вкладке Shader Globals в Настройках проекта

Добавление глобальной униформы на вкладке Shader Globals в Настройках проекта

После создания глобальной uniform-переменной, вы можете использовать её в шейдере следующим образом:

shader_type canvas_item;

global uniform vec4 my_color;

void fragment() {
    COLOR = my_color.rgb;
}

Обратите внимание, что глобальная uniform-переменная должна существовать в Настройках проекта на момент сохранения шейдера, иначе компиляция будет неудачной. Хотя вы можете присвоить значение по умолчанию, используя global uniform vec4 my_color = ... в коде шейдера, оно будет проигнорировано, так как глобальная uniform-переменная в любом случае должна быть определена в Настройках проекта.

To change the value of a global uniform at runtime, use the RenderingServer.global_shader_parameter_set method in a script:

RenderingServer.global_shader_parameter_set("my_color", Color(0.3, 0.6, 1.0))

Присваивать глобальные uniform-значения можно сколько угодно раз без ущерба для производительности, поскольку установка данных не требует синхронизации между CPU и GPU.

You can also add or remove global uniforms at runtime:

RenderingServer.global_shader_parameter_add("my_color", RenderingServer.GLOBAL_VAR_TYPE_COLOR, Color(0.3, 0.6, 1.0))
RenderingServer.global_shader_parameter_remove("my_color")

Adding or removing global uniforms at runtime has a performance cost, although it's not as pronounced compared to getting global uniform values from a script (see the warning below).

Предупреждение

While you can query the value of a global uniform at runtime in a script using RenderingServer.global_shader_parameter_get("uniform_name"), this has a large performance penalty as the rendering thread needs to synchronize with the calling thread.

Поэтому не рекомендуется постоянно считывать значения глобальной шейдерной uniform-переменной в скрипте. Если вам нужно читать значения в скрипте после их установки, подумайте о создании autoload, где вы храните значения, которые вам нужно запросить, одновременно устанавливая их в качестве глобальных uniform-переменных.

Uniform-переменные на экземпляр

Примечание

Per-instance uniforms are available in both canvas_item (2D) and spatial (3D) shaders.

Иногда требуется изменить параметр на каждом узле с помощью материала. Например, в лесу, полном деревьев, когда вы хотите, чтобы каждое дерево имело немного другой цвет, который можно редактировать вручную. Без uniform-переменных для каждого экземпляра это требует создания уникального материала для каждого дерева (каждый из которых имеет немного другой оттенок). Это усложняет управление материалами, а также увеличивает производительность, поскольку сцена требует больше уникальных экземпляров материалов. Здесь также можно использовать цвета вершин, но это потребует создания уникальных копий сетки для каждого цвета, что также приведёт к снижению производительности.

Uniform-переменная задаётся для каждого экземпляра GeometryInstance3D, а не для каждого экземпляра материала. Учитывайте это при работе с сетками, которым назначено несколько материалов, или при настройке MultiMesh.

shader_type spatial;

// Provide a hint to edit as a color. Optionally, a default value can be provided.
// If no default value is provided, the type's default is used (e.g. opaque black for colors).
instance uniform vec4 my_color : source_color = vec4(1.0, 0.5, 0.0, 1.0);

void fragment() {
    ALBEDO = my_color.rgb;
}

После сохранения шейдера вы можете изменить значение uniform-переменной для каждого экземпляра с помощью инспектора:

Установка значения uniform-переменной для каждого экземпляра в разделе инспектора GeometryInstance3D

Установка значения uniform-переменной для каждого экземпляра в разделе инспектора GeometryInstance3D

Per-instance uniform values can also be set at runtime using set_instance_shader_parameter method on a node that inherits from GeometryInstance3D:

$MeshInstance3D.set_instance_shader_parameter("my_color", Color(0.3, 0.6, 1.0))

При использовании uniform-переменной для каждого экземпляра есть некоторые ограничения, о которых вы должны знать:

  • Uniform-переменные на экземпляр не поддерживают текстуры, только обычные скалярные и векторные типы. В качестве обходного пути вы можете передать массив текстур как обычную uniform-переменную, а затем передать индекс текстуры, которая будет отрисована, используя uniform-переменную для экземпляра.

  • Максимальный практический предел - 16 экземпляров uniform-переменной на шейдер.

  • Если в сетке используется несколько материалов, параметры первого найденного материала сетки "победят" последующие, если только они не имеют одинаковое имя, индекс и тип. В этом случае все параметры будут затронуты корректно.

  • Если вы столкнулись с описанной выше ситуацией, вы можете избежать столкновений, указав вручную индекс (0-15) uniform-переменной экземпляра с помощью подсказки instance_index:

instance uniform vec4 my_color : source_color, instance_index(5);

Setting uniforms from code

You can set uniforms from GDScript using the set_shader_parameter() method:

material.set_shader_parameter("some_value", some_value)

material.set_shader_parameter("colors", [Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)])

Примечание

The first argument to set_shader_parameter() is the name of the uniform in the shader. It must match exactly to the name of the uniform in the shader or else it will not be recognized.

GDScript использует переменные, отличающиеся от GLSL, поэтому при передаче переменных из GDScript в шейдер Godot автоматически преобразует тип. В таблице ниже указаны соответствующие типы:

Типы GLSL

Типы GDScript

Примечания

bool

bool

bvеc2

int

Побитовая упаковка int, где бит 0 (LSB) соответствует x.

Например, bvec2 из (bx, by) можно создать следующим образом:

bvec2_input: int = (int(bx)) | (int(by) << 1)

bvеc3

int

Побитовая упаковка int, где бит 0 (LSB) соответствует x.

bveс4

int

Побитовая упаковка int, где бит 0 (LSB) соответствует x.

int

int

ivеc2

Vector2i

ivеc3

Vector3i

iveс4

Vector4i

uіnt

int

uvеc2

Vector2i

uvеc3

Vector3i

uvеc4

Vector4i

float

float

vеc2

Vector2

vеc3

Vector3, Color

Когда используется Color (цвет), он интерпретируется как (r, g, b).

vеc4

Vector4, Color, Rect2, Plane, Quaternion

Когда используется Color (цвет), он интерпретируется как (r, g, b, a).

Если используется Rect2 (прямоугольник), он будет интерпретирован как (position.x, position.y, size.x, size.y).

Когда используется Plane (плоскость), это будет интерпретироваться как (normal.x, normal.y, normal.z, d).

mаt2

Transform2D

mаt3

Basis

mаt4

Projection, Transform3D

При использовании Transform3D вектор w устанавливается на идентичность.

samplеr2D

Texture2D

isamplеr2D

Texture2D

usamplеr2D

Texture2D

sampler2DArray

Texture2DArray

isampler2DArray

Texture2DArray

usampler2DArray

Texture2DArray

sampler3D

Texture3D

isampler3D

Texture3D

usampler3D

Texture3D

samplerCubе

Cubemap

See Изменение типа импорта for instructions on importing cubemaps for use in Godot.

samplerCubeArray

CubemapArray

Only supported in Forward+ and Mobile, not Compatibility.

samplerExternalOES

ExternalTexture

Only supported in Compatibility/Android platform.

Примечание

Be careful when setting shader uniforms from GDScript, since no error will be thrown if the type does not match. Your shader will just exhibit undefined behavior. Specifically, this includes setting a GDScript int/float (64 bit) into a Godot shader language int/float (32 bit). This may lead to unintended consequences in cases where high precision is required.

Uniform limits

There is a limit to the total size of shader uniforms that you can use in a single shader. On most desktop platforms, this limit is 65536 bytes, or 4096 vec4 uniforms. On mobile platforms, the limit is typically 16384 bytes, or 1024 vec4 uniforms. Vector uniforms smaller than a vec4, such as vec2 or vec3, are padded to the size of a vec4. Scalar uniforms such as int or float are not padded, and bool is padded to the size of an int.

Arrays count as the total size of their contents. If you need a uniform array that is larger than this limit, consider packing the data into a texture instead, since the contents of a texture do not count towards this limit, only the size of the sampler uniform.

Встроенные переменные

A large number of built-in variables are available, like UV, COLOR and VERTEX. What variables are available depends on the type of shader (spatial, canvas_item, particle, etc) and the function used (vertex, fragment, light, start, process, ``sky, or fog). For a list of the built-in variables that are available, please see the corresponding pages:

Встроенные функции

A large number of built-in functions are supported, conforming to GLSL ES 3.0. See the Built-in functions page for details.