Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

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

Введение

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

Целое число со знаком.

ivеc2

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

ivеc3

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

iveс4

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

uіnt

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

uvеc2

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

uvеc3

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

uvеc4

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

float

Число с плавающей точкой(дробное число).

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

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

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

Так же как и в 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[ряд][колонка] для доступа к отдельному скаляру, или m[индекс] для доступа к целому вектору по индексу ряда. Например, для доступа к позиции объекта по оси y в mat4 используйте 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);

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

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

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

Вы можете объявлять массивы в глобальном пространстве, например:

shader_type spatial;

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

void fragment() {
  ALBEDO = v[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;

Константы типа 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 (низший)

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

||

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

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

// `if` and `else`.
if (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 могут использовать ключевое слово discard. Если оно используется, фрагмент отбрасывается и ничего не записывается.

Имейте в виду, что использование 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;
}

Примечание

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

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-переменные

Передача значений в шейдеры возможна. Они являются глобальными для всего шейдера и называются uniform. Когда шейдер впоследствии будет назначен материалу, uniform-переменные появятся в нём как редактируемые параметры. Uniform-переменные не могут быть записаны изнутри шейдера.

shader_type spatial;

uniform float some_value;

uniform vec3 colors[3];

Вы можете задать uniform-переменные в редакторе в материале. Или вы можете задать их через GDScript:

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

Примечание

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

Любой тип GLSL, кроме void, может быть униформой. Кроме того, Godot предоставляет дополнительные подсказки для шейдеров, чтобы компилятор понимал, для чего используется униформа и как редактор должен позволять пользователям изменять её.

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;

Важно понимать, что текстуры, которые поставляются как цветные, требуют подсказки для правильного преобразования sRGB -> linear (т.е. ``source_color`), поскольку 3D-движок Godot рендерит в линейном цветовом пространстве. Если этого не сделать, текстура будет выглядеть размытой.

Примечание

2D-рендерер также рендерит в линейном цветовом пространстве, если включена настройка проекта Rendering > Viewport > HDR 2D, поэтому source_color также должен использоваться в шейдерах canvas_item. Если 2D HDR отключен, source_color будет продолжать корректно работать в шейдерах canvas_item, поэтому рекомендуется использовать его в любом случае.

Полный список подсказок ниже:

Тип

Подсказка

Описание

vec3, vec4

source_color

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

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+).

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

mat4 mat4

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

samplerCubeArray

CubemapArray

Примечание

Будьте осторожны при задании формы шейдера из GDScript, если тип не совпадает, ошибка не возникнет. Ваш шейдер просто будет демонстрировать неопределенное поведение.

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

shader_type spatial;

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

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

Если вам нужно, чтобы несколько униформ были сгруппированы в определённой категории инспектора, вы можете использовать ключевое слово group_uniform, например:

group_uniforms MyGroup;
uniform sampler2D test;

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

group_uniforms;

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

group_uniforms MyGroup.MySubgroup;

Глобальные uniforms

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

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

Чтобы создать глобальную униформу, откройте 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-переменная в любом случае должна быть определена в Настройках проекта.

Чтобы изменить значение глобальной uniform-переменной во время выполнения, используйте метод RenderingServer.global_shader_parameter_set в скрипте:

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

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

Вы также можете добавлять или удалять глобальные uniform-переменные во время выполнения:

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

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

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

Хотя вы можете запросить значение глобальной uniform-переменной во время выполнения скрипта, используя RenderingServer.global_shader_parameter_get("uniform_name"), это приводит к большим потерям производительности, поскольку поток рендеринга должен синхронизироваться с вызывающим потоком.

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

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

Примечание

Uniform-переменная для каждого экземпляра доступна только в 3D шейдерах spatial.

Иногда требуется изменить параметр на каждом узле с помощью материала. Например, в лесу, полном деревьев, когда вы хотите, чтобы каждое дерево имело немного другой цвет, который можно редактировать вручную. Без 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 run-time 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);

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

Доступно большое количество встроенных переменных, таких как 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 могут быть скалярными или векторными.

Функция

Описание / Возвращаемое значение

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)

Power (неопределено, если x < 0 или если x == 0 и y <= 0).

vec_type exp (vec_type x)

Экспонента.

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

ivec_type sign (ivec_type x)

Знак числа (возвращает 1.0 если положительно, -1.0 если отрицательно, 0.0 если ноль).

vec_type floor (vec_type x)

Округление до целого в меньшую сторону (floor(3.9)=3).

vec_type round (vec_type x)

Округление до ближайшего целого.

vec_type roundEven (vec_type x)

Округление до ближайшего чётного целого.

vec_type trunc (vec_type x)

Усечение (trunc(123.56) = 123).

vec_type ceil (vec_type x)

Округление до целого в большую сторону.

vec_type fract (vec_type x)

Дробная часть (возвращает x - floor(x)).

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)

Наименьшее значение между a и b.

vec_type max (vec_type a, vec_type b)

Наибольшее значение между a и b.

vec_type clamp (vec_type x, vec_type min, vec_type max)

Ограничение (диапазон) x между 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, bvec_type c)

Линейная интерполяция между a и b по c.

vec_type fma (vec_type a, vec_type b, vec_type c)

Выполняет операцию умножения и сложения: (a * b + 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)

vec_type smoothstep (float a, float b, vec_type c)

Эрмитова интерполяция между a и b по c.

bvec_type isnan (vec_type x)

Возвращает true, если скалярная или векторная компонента является NaN.

bvec_type isinf (vec_type x)

Возвращает true, если скалярный или векторный компонент является INF.

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)

If dot(Nref, I) < 0, return N, otherwise -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)

Логическое сравнение векторов < int/uint/float.

bvec_type greaterThan (vec_type x, vec_type y)

Логическое сравнение векторов > int/uint/float.

bvec_type lessThanEqual (vec_type x, vec_type y)

Логическое сравнение векторов <= int/uint/float.

bvec_type greaterThanEqual (vec_type x, vec_type y)

Логическое сравнение векторов >= int/uint/float.

bvec_type equal (vec_type x, vec_type y)

Логическое сравнение векторов == int/uint/float.

bvec_type notEqual (vec_type x, vec_type y)

Логическое сравнение векторов != int/uint/float.

bool any (bvec_type x)

true если любой компонент является true, иначе false.

bool all (bvec_type x)

true если все компоненты являются true, иначе false.

bvec_type not (bvec_type x)

Обратный логический вектор.

ivec2 textureSize (gsampler2D s, int lod)

ivec3 textureSize (gsampler2DArray s, int lod)

ivec3 textureSize (gsampler3D s, int lod)

ivec2 textureSize (samplerCube s, int lod)

ivec2 textureSize (samplerCubeArray s, int lod)

Получает размер текстуры.

LOD определяет, какой уровень mipmap будет использоваться. При значении LOD, равном 0, будет использоваться текстура с полным разрешением.

vec2 textureQueryLod (gsampler2D s, vec2 p)

vec3 textureQueryLod (gsampler2DArray s, vec2 p)

vec2 textureQueryLod (gsampler3D s, vec3 p)

vec2 textureQueryLod (samplerCube s, vec3 p)

Вычисляет уровень детализации, который будет использоваться для выборки из текстуры. Компонент x полученного значения - это массив mipmap, к которому будет осуществляться доступ. Компонент y - это вычисленный уровень детализации относительно базового уровня (независимо от уровней mipmap текстуры).

int textureQueryLevels (gsampler2D s)

int textureQueryLevels (gsampler2DArray s)

int textureQueryLevels (gsampler3D s)

int textureQueryLevels (samplerCube s)

Получает количество доступных уровней mipmap текстуры.

If the texture is unassigned to a sampler, 1 is returned (Godot always internally assigns a texture even to an empty sampler).

gvec4_type texture (gsampler2D s, vec2 p [, float bias])

gvec4_type texture (gsampler2DArray s, vec3 p [, float bias])

gvec4_type texture (gsampler3D s, vec3 p [, float bias])

vec4 texture (samplerCube s, vec3 p [, float bias])

vec4 texture (samplerCubeArray s, vec4 p [, float bias])

Выполняет чтение текстуры.

gvec4_type textureProj (gsampler2D s, vec3 p [, float bias])

gvec4_type textureProj (gsampler2D s, vec4 p [, float bias])

gvec4_type textureProj (gsampler3D s, vec4 p [, float bias])

Выполняет чтение текстуры с проекцией.

gvec4_type textureLod (gsampler2D s, vec2 p, float lod)

gvec4_type textureLod (gsampler2DArray s, vec3 p, float lod)

gvec4_type textureLod (gsampler3D s, vec3 p, float lod)

vec4 textureLod (samplerCube s, vec3 p, float lod)

vec4 textureLod (samplerCubeArray s, vec4 p, float lod)

Выполняет чтение текстуры с пользовательской mipmap.

LOD определяет, какой уровень mipmap будет использоваться. Значение LOD, равное 0.0, будет использовать текстуру с полным разрешением. Если текстура не имеет mipmap, то все значения LOD будут работать как 0.0.

gvec4_type textureProjLod (gsampler2D s, vec3 p, float lod)

gvec4_type textureProjLod (gsampler2D s, vec4 p, float lod)

gvec4_type textureProjLod (gsampler3D s, vec4 p, float lod)

Выполняет чтение текстуры с проекцией/LOD.

LOD определяет, какой уровень mipmap будет использоваться. Значение LOD, равное 0.0, будет использовать текстуру с полным разрешением. Если текстура не имеет mipmap, то все значения LOD будут работать как 0.0.

gvec4_type textureGrad (gsampler2D s, vec2 p, vec2 dPdx, vec2 dPdy)

gvec4_type textureGrad (gsampler2DArray s, vec3 p, vec2 dPdx, vec2 dPdy)

gvec4_type textureGrad (gsampler3D s, vec3 p, vec2 dPdx, vec2 dPdy)

vec4 textureGrad (samplerCube s, vec3 p, vec3 dPdx, vec3 dPdy)

vec4 textureGrad (samplerCubeArray s, vec3 p, vec3 dPdx, vec3 dPdy)

Выполняет чтение текстуры с явными градиентами.

gvec4_type textureProjGrad (gsampler2D s, vec3 p, vec2 dPdx, vec2 dPdy)

gvec4_type textureProjGrad (gsampler2D s, vec4 p, vec2 dPdx, vec2 dPdy)

gvec4_type textureProjGrad (gsampler3D s, vec4 p, vec3 dPdx, vec3 dPdy)

Выполняет чтение текстуры с проекцией/LOD и с явными градиентами.

gvec4_type texelFetch (gsampler2D s, ivec2 p, int lod)

gvec4_type texelFetch (gsampler2DArray s, ivec3 p, int lod)

gvec4_type texelFetch (gsampler3D s, ivec3 p, int lod)

Получает один тексель, используя целочисленные координаты.

LOD определяет, какой уровень mipmap будет использоваться. При значении LOD, равном 0, будет использоваться текстура с полным разрешением.

gvec4_type textureGather (gsampler2D s, vec2 p [, int comps])

gvec4_type textureGather (gsampler2DArray s, vec3 p [, int comps])

vec4 textureGather (samplerCube s, vec3 p [, int comps])

Собирает четыре текселя из текстуры. Используйте comps в диапазоне 0..3, чтобы определить, какой компонент (x, y, z, w) будет возвращён. Если comps не указан: 0 (или x-компонент) будет использован.

vec_type dFdx (vec_type p)

Производная в x с использованием локального дифферента. Внутренне может использовать либо dFdxCoarse, либо dFdxFine, но решение о том, что использовать, принимает драйвер GPU.

vec_type dFdxCoarse (vec_type p)

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

vec_type dFdxFine (vec_type p)

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

vec_type dFdy (vec_type p)

Производная в y с использованием локального дифференцирования. Внутренне может использовать либо dFdyCoarse, либо dFdyFine, но решение о том, что использовать, принимает драйвер GPU.

vec_type dFdyCoarse (vec_type p)

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

vec_type dFdyFine (vec_type p)

Вычисляет производную по оконной координате y с помощью локального дифференцирования на основе значения p для текущего фрагмента и его ближайшего соседа/соседей. Эта функция недоступна для профиля gl_compatibility.

vec_type fwidth (vec_type p)

Сумма абсолютных производных по x и y. Это эквивалентно использованию abs(dFdx(p)) + abs(dFdy(p)).

vec_type fwidthCoarse (vec_type p)

Сумма абсолютных производных по x и y. Это эквивалентно использованию abs(dFdxCoarse(p)) + abs(dFdyCoarse(p)). Эта функция недоступна в профиле gl_compatibility.

vec_type fwidthFine (vec_type p)

Сумма абсолютных производных по x и y. Это эквивалентно использованию abs(dFdxFine(p)) + abs(dFdyFine(p)). Эта функция недоступна в профиле gl_compatibility.

uint packHalf2x16 (vec2 v)

vec2 unpackHalf2x16 (uint v)

Преобразование двух 32-битных чисел с плавающей точкой в 16-битные и упаковка их в 32-битное беззнаковое целое число и наоборот.

uint packUnorm2x16 (vec2 v)

vec2 unpackUnorm2x16 (uint v)

Преобразование двух 32-битных чисел с плавающей точкой (в диапазоне 0..1) в 16-битные и упаковка их в 32-битное беззнаковое целое число и наоборот.

uint packSnorm2x16 (vec2 v)

vec2 unpackSnorm2x16 (uint v)

Преобразование двух 32-битных чисел с плавающей точкой (в диапазоне -1..1) в 16-битные и упаковка их в 32-битное беззнаковое целое число и наоборот.

uint packUnorm4x8 (vec4 v)

vec4 unpackUnorm4x8 (uint v)

Преобразование четырёх 32-битных чисел с плавающей точкой (в диапазоне 0..1) в 8-битные и их упаковка в 32-битное беззнаковое целое число, и наоборот.

uint packSnorm4x8 (vec4 v)

vec4 unpackSnorm4x8 (uint v)

Преобразование четырёх 32-битных чисел с плавающей точкой (в диапазоне -1..1) в 8-битные и их упаковка в 32-битное беззнаковое целое число, и наоборот.

ivec_type bitfieldExtract (ivec_type value, int offset, int bits)

uvec_type bitfieldExtract (uvec_type value, int offset, int bits)

Извлечение диапазона битов из целого числа.

ivec_type bitfieldInsert (ivec_type base, ivec_type insert, int offset, int bits)

uvec_type bitfieldInsert (uvec_type base, uvec_type insert, int offset, int bits)

Вставка диапазона битов в целое число.

ivec_type bitfieldReverse (ivec_type value)

uvec_type bitfieldReverse (uvec_type value)

Изменение порядка следования битов в целых числах.

ivec_type bitCount (ivec_type value)

uvec_type bitCount (uvec_type value)

Подсчитывает количество бит 1 в целых числах.

ivec_type findLSB (ivec_type value)

uvec_type findLSB (uvec_type value)

Находит индекс наименьшего значащего бита, установленного в 1, в целом числе.

ivec_type findMSB (ivec_type value)

uvec_type findMSB (uvec_type value)

Находит индекс старшего бита, установленного в 1, в целом числе.

void imulExtended (ivec_type x, ivec_type y, out ivec_type msb, out ivec_type lsb)

void umulExtended (uvec_type x, uvec_type y, out uvec_type msb, out uvec_type lsb)

Перемножает два 32-битных числа и выдает 64-битный результат. x - первое число. y - второе число. msb - будет содержать старшие значащие биты. lsb - содержит младшие значащие биты.

uvec_type uaddCarry (uvec_type x, uvec_type y, out uvec_type carry)

Складывает два беззнаковых целых числа и генерирует перенос.

uvec_type usubBorrow (uvec_type x, uvec_type y, out uvec_type borrow)

Вычитает два беззнаковых целых числа и генерирует заимствование.

vec_type ldexp (vec_type x, out ivec_type exp)

Сборка числа с плавающей точкой из значения и экспоненты.

Если это произведение слишком велико для представления в типе с плавающей точкой, результат будет не определён.

vec_type frexp (vec_type x, out ivec_type exp)

Разделяет число с плавающей точкой(x) на сигнификат (в диапазоне [0.5, 1.0]) и интегральную экспоненту.

Для x, равного нулю, сигнификат и экспонента равны нулю. Для x, равного бесконечности или NaN, результат будет не определён.