Язык шейдеров
Введение
Godot использует язык шейдеров схожий с GLSL ES 3.0. Поддерживаются большинство типов данных и функций, а те что остались за бортом, скорее всего будет добавлены в будущем.
Если вы уже знакомы с GLSL, то Руководство по переходу на шейдеры Godot - это ресурс, который поможет вам перейти с обычного GLSL на язык шейдеров Godot.
Типы данных
Поддерживаются большинство типов данных из GLSL ES 3.0:
Тип |
Описание |
|---|---|
voіd |
Пустой тип данных, используется только для функций которые ничего не возвращают. |
bool |
Булевый (логический) тип данных, может содержать только |
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.
Приведение переменных
Так же как и в 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 |
|---|---|
|
|
|
|
|
|
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] |
Используется для ограничителя шероховатости при импорте (попытки уменьшить сглаживание блика). |
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 в Настройках проекта
После создания глобальной 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
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:
Шейдеры Spatial (3D)
Шейдеры элементов холста (canvas_item, 2D)
Встроенные функции
A large number of built-in functions are supported, conforming to GLSL ES 3.0. See the Built-in functions page for details.
Комментарии
The shading language supports the same comment syntax as used in C# and C++, using
//for single-line comments and/* */for multi-line comments:Кроме того, вы можете использовать комментарии к документации, которые отображаются в инспекторе при наведении курсора на параметр шейдера. В настоящее время комментарии документации поддерживаются только при размещении непосредственно над объявлением
uniform. Эти комментарии поддерживают только синтаксис многострочных комментариев и должны использовать две ведущие звездочки (/**), а не одну (/*):Звёздочки в последующих строках не обязательны, но рекомендуются согласно Shaders style guide. Эти звёздочки автоматически удаляются инспектором, поэтому они не будут отображаться во всплывающей подсказке.