Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Штрихування мови
Вступ
Godot використовує мову затінення, подібну до GLSL ES 3.0. Більшість типів даних і функцій підтримуються, а ті, що залишилися, ймовірно, будуть додані з часом.
Якщо ви вже знайомі з GLSL, Godot Shader Migration Guide — це ресурс, який допоможе вам перейти від звичайної GLSL до мови затінення Godot.
Типи даних
Підтримуються більшість типів даних GLSL ES 3.0:
Тип |
Опис |
|---|---|
void |
Тип даних Void, корисний лише для функцій, які нічого не повертають. |
bool |
Логічний тип даних, може містити лише |
bvec2 |
Двокомпонентний вектор логічних значень. |
bvec3 |
Трикомпонентний вектор булевих значень. |
bvec4 |
Чотирикомпонентний вектор логічних значень. |
int |
32-розрядне скалярне ціле число зі знаком. |
ivec2 |
Двокомпонентний вектор цілих чисел зі знаком. |
ivec3 |
Трикомпонентний вектор цілих чисел зі знаком. |
ivec4 |
Чотирикомпонентний вектор цілих чисел зі знаком. |
uint |
Беззнакове скалярне ціле число; не може містити від’ємних чисел. |
uvec2 |
Двокомпонентний вектор беззнакових цілих чисел. |
uvec3 |
Трикомпонентний вектор беззнакових цілих чисел. |
uvec4 |
Чотирикомпонентний вектор беззнакових цілих чисел. |
float |
32-розрядний скаляр з плаваючою комою. |
vec2 |
Двокомпонентний вектор значень з плаваючою комою. |
vec3 |
Трикомпонентний вектор значень з плаваючою комою. |
vec4 |
Чотирикомпонентний вектор значень з плаваючою комою. |
mat2 |
Матриця 2x2, у порядку основних стовпців. |
mat3 |
Матриця 3x3, у порядку основних стовпців. |
mat4 |
Матриця 4x4, у порядку основних стовпців. |
sampler2D |
Тип семплера для прив’язки 2D-текстур, які зчитуються як плаваючі. |
isampler2D |
Тип семплера для прив’язки 2D текстур, які читаються як ціле число зі знаком. |
usampler2D |
Тип семплера для прив’язки 2D-текстур, які читаються як ціле число без знаку. |
Sampler2DArray |
Тип семплера для зв’язування масивів двовимірних текстур, які зчитуються як float. |
Isampler2DArray |
Тип семплера для зв’язування 2D-масивів текстур, які читаються як ціле число зі знаком. |
Usampler2DArray |
Тип семплера для зв’язування 2D масивів текстур, які зчитуються як ціле число без знаку. |
Sampler3D |
Тип семплера для зв'язування 3D-текстур, які читаються як плаваючі. |
Isampler3D |
Тип семплера для зв'язування 3D-текстур, які читаються як ціле число зі знаком. |
Usampler3D |
Тип семплера для зв'язування 3D-текстур, які читаються як ціле число без знаку. |
samplerCube |
Тип семплера для прив’язки Cubemaps, які читаються як float. |
samplerCubeArray |
Тип семплера для зв’язування масивів Cubemap, які читаються як float. Підтримується лише у Forward+ і Mobile, а не в Compatibility. |
samplerExternalOES |
Зовнішній тип пробника. Підтримується лише на платформі сумісності/Android. |
Ці типи також можна розміщувати всередині arrays або structs, які також можна використовувати як параметри функцій або значення, що повертаються. Масиви можна використовувати як уніформи, а структури — ні.
Попередження
Локальні змінні не ініціалізуються значенням за замовчуванням, таким як 0.0. Якщо ви використовуєте змінну без її попереднього призначення, вона міститиме будь-яке значення, яке вже було в цьому місці пам’яті, і з’являться непередбачувані візуальні збої. Однак уніформи та варіації ініціалізуються значенням за замовчуванням.
Кастинг
Як і в 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 (4-й стовпець, 2-й рядок) ви використовуєте m[3][1] або 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);
Побудова типів матриць вимагає векторів тієї ж розмірності, що й матриця, інтерпретованих як стовпці. Ви також можете побудувати діагональну матрицю за допомогою синтаксису 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. 1. 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: [const] + [precision] + typename + identifier + [array size].
void fragment() {
float arr[3];
}
Їх можна ініціалізувати на початку так:
float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor
int int_arr[3] = int[] (2, 1, 0); // second constructor
vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor
bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count
Ви можете оголосити кілька масивів (навіть із різними розмірами) в одному виразі:
float a[3] = float[3] (1.0, 0.5, 0.0),
b[2] = { 1.0, 0.5 },
c[] = { 0.7 },
d = 0.0,
e[5];
Щоб отримати доступ до елемента масиву, використовуйте синтаксис індексування:
float arr[3];
arr[0] = 1.0; // setter
COLOR.r = arr[0]; // getter
Масиви також мають вбудовану функцію .length() (не плутати з вбудованою функцією length()). Він не приймає жодних параметрів і повертає розмір масиву.
float arr[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr.length(); i++) {
// ...
}
Примітка
Якщо ви використовуєте індекс, менший за 0 або більший за розмір масиву, шейдер вийде з ладу та порушить рендеринг. Щоб запобігти цьому, використовуйте функції length(), if або clamp(), щоб переконатися, що індекс знаходиться між 0 і довжиною масиву. Завжди ретельно перевіряйте свій код. Якщо ви передаєте постійний вираз або число, редактор перевірить його межі, щоб запобігти цьому збою.
Глобальні масиви
Ви можете оголошувати масиви в глобальному просторі як const або uniform:
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];
}
Примітка
Глобальні масиви використовують той самий синтаксис, що й локальні масиви, за винятком const або uniform, доданих до їхнього оголошення. Зауважте, що уніфіковані масиви не можуть мати значення за замовчуванням.
Константи
Використовуйте ключове слово const перед оголошенням змінної, щоб зробити цю змінну незмінною, що означає, що її неможливо змінити. Усі основні типи, крім семплерів, можуть бути оголошені як константи. Доступ і використання постійного значення відбувається трохи швидше, ніж використання уніформи. Константи повинні бути ініціалізовані при їх оголошенні.
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
Константи можна оголошувати як глобально (поза будь-якою функцією), так і локально (всередині функції). Глобальні константи корисні, коли ви хочете мати доступ до значення в шейдері, яке не потрібно змінювати. Як і уніформи, глобальні константи є спільними для всіх етапів шейдера, але вони недоступні поза шейдером.
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 (unsigned int) повинні мати суфікс 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;
}
Або використовуйте конструктор структур для тієї ж мети:
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 |
унарні |
+, -, !, ~ |
3 |
мультиплікативні |
/, *, % |
4 |
адитивні |
+, - |
5 |
зсув за бітами |
<<, >> |
6 |
реляційні |
<, >, <=, >= |
7 |
рівність |
==, != |
8 |
побітове І |
& |
9 |
побітове виключаюче АБО |
^ |
10 |
побітове включне АБО |
| |
11 |
логічне І |
&& |
12 (найнижчий) |
логічне включне АБО |
|| |
Примітка
Більшість операторів, які приймають вектори чи матриці (множення, ділення тощо), працюють покомпонентно, тобто функція застосовується до першого значення кожного вектора, а потім до другого значення кожного вектора тощо. Деякі приклади:
Операція |
Еквівалентна скалярна операція |
|---|---|
|
|
|
|
|
|
Специфікація мови GLSL говорить у розділі 5.10 Векторні та матричні операції:
За кількома винятками, операції є компонентними. Зазвичай, коли оператор працює з вектором або матрицею, він діє незалежно від кожного компонента вектора або матриці, покомпонентно. [...] Винятками є матриця, помножена на вектор, помножена на матрицю, і матриця, помножена на матрицю. Вони не працюють покомпонентно, а скоріше виконують правильне лінійне алгебраїчне множення.
Контроль потоку
Мова затінення 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);
Майте на увазі, що в сучасних графічних процесорах може існувати нескінченний цикл, який може заморозити вашу програму (включаючи редактор). 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 для отримання додаткової інформації.
Відкидання
Функції фрагментів, легких і спеціальних функцій (викликаних із фрагментів або легких) можуть використовувати ключове слово 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: означає, що аргумент призначений лише для запису.
введення: означає, що аргумент повністю передається за посиланням.
const: означає, що аргумент є константою і не може бути змінений, може поєднуватися з кваліфікатором in.
Приклад нижче:
void sum2(int a, int b, inout int result) {
result = a + b;
}
Підтримується перевантаження функцій. Ви можете визначити кілька функцій з однаковою назвою, але різними аргументами. Зауважте, що implicit casting у перевантажених викликах функцій заборонено, наприклад, від int до float (1 до 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);
}
варіювання
Щоб відправити дані з вершини до функції процесора фрагмента (або світла), використовуються variyings. Вони встановлюються для кожної примітивної вершини в процесорі вершин, а значення інтерполюється для кожного пікселя в процесорі фрагментів.
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
}
Варіант також може бути масивом:
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. Для цього ви можете призначити його у фрагменті і пізніше використати у функції 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;
}
Зауважте, що варіювання не може бути призначено в спеціальних функціях або функції легкого процесора, наприклад:
shader_type spatial;
varying float test;
void foo() {
test = 0.0; // Error.
}
void vertex() {
test = 0.0;
}
void light() {
test = 0.0; // Error too.
}
Це обмеження було введено, щоб запобігти неправильному використанню до ініціалізації.
Класифікатори інтерполяції
Певні значення інтерполюються під час конвеєра затінення. Ви можете змінити спосіб виконання цих інтерполяцій за допомогою кваліфікаторів інтерполяції.
shader_type spatial;
varying flat vec3 our_color;
void vertex() {
our_color = COLOR.rgb;
}
void fragment() {
ALBEDO = our_color;
}
Є два можливі кваліфікатори інтерполяції:
Кваліфікатор |
Опис |
|---|---|
flat |
Значення не інтерполюється. |
smooth |
Значення інтерполюється у правильному для перспективи режимі. Це значення за умовчанням. |
Уніформа
Передача значень шейдерам можлива за допомогою uniforms, які визначені в глобальній області шейдера поза функціями. Коли матеріалу пізніше буде призначено шейдер, уніформа з’явиться як параметри, які можна редагувати, в інспекторі матеріалу. Уніформу не можна написати з шейдера. Будь-який data type, крім void, може бути однорідним.
shader_type spatial;
uniform float some_value;
uniform vec3 colors[3];
Ви можете встановити форму в редакторі в інспекторі матеріалів. Крім того, ви можете встановити їх from code.
Одноманітні підказки
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;
Уніформі також можна призначити значення за замовчуванням:
shader_type spatial;
uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : source_color = vec4(1.0);
Зауважте, що при додаванні значення за замовчуванням і підказки значення за замовчуванням йде після підказки.
Нижче наведено повний список підказок щодо уніформи:
Тип |
Підказка |
Опис |
|---|---|---|
Vec3, vec4 |
source_color |
Використовується як колір. |
int |
hint_enum("String1", "String2") |
Відображає int як розкривний віджет у редакторі. |
int, float |
Hint_range(min, max[, step]) |
Обмежується значеннями в діапазоні (з min/max/кроком). |
sampler2D |
source_color |
Використовується як колір альбедо. |
sampler2D |
Hint_normal |
Використовується як normalmap. |
sampler2D |
Hint_default_white |
Як значення або колір альбедо, за замовчуванням непрозорий білий. |
sampler2D |
Hint_default_black |
Як значення або колір альбедо за замовчуванням непрозорий чорний. |
sampler2D |
Hint_default_transparent |
Як значення або колір альбедо, за замовчуванням прозорий чорний. |
sampler2D |
Hint_anisotropy |
Як схема потоку, за замовчуванням праворуч. |
sampler2D |
Hint_roughness[_r, _g, _b, _a, _normal, _gray] |
Використовується для обмежувача шорсткості під час імпорту (спроби зменшити дзеркальне згладжування). |
sampler2D |
Filter[_nearest, _linear][_mipmap][_anisotropic] |
Увімкнено фільтрацію заданої текстури. |
sampler2D |
repeat[_enable, _disable] |
Увімкнено повторення текстури. |
sampler2D |
hint_screen_texture |
Текстура — це текстура екрана. |
sampler2D |
hint_depth_texture |
Текстура — це текстура глибини. |
sampler2D |
hint_normal_roughness_texture |
Текстура — це звичайна текстура шорсткості (підтримується лише у Forward+). |
Використання hint_enum
Ви можете отримати доступ до значень int як читабельного спадного віджета за допомогою уніформи hint_enum:
uniform int noise_type : hint_enum("OpenSimplex2", "Cellular", "Perlin", "Value") = 0;
Ви можете призначити явні значення уніфікованому hint_enum за допомогою синтаксису двокрапки, подібного до GDScript:
uniform int character_speed: hint_enum("Slow:30", "Average:60", "Very Fast:200") = 60;
Значення буде збережено як ціле число, що відповідає індексу вибраного параметра (тобто 0, 1 або 2) або значенню, призначеному синтаксисом двокрапки (тобто 30, 60 або 200). Встановлюючи значення за допомогою set_shader_parameter(), ви повинні використовувати ціле значення, а не назву String.
Використання source_color
Будь-яка текстура, яка містить дані кольору sRGB, потребує підказки source_color для правильного відбору. Це пояснюється тим, що Godot відображає лінійний колірний простір, але деякі текстури містять колірні дані sRGB. Якщо цю підказку не використовувати, текстура виглядатиме розмитою.
Альбедо та кольорові текстури зазвичай мають мати підказку source_color. Нормальні, шорсткі, металеві та висотні текстури зазвичай не потребують підказки source_color.
Використання підказки source_color є обов’язковим у програмах візуалізації Forward+ і Mobile, а також у шейдерах canvas_item, коли HDR 2D увімкнено. Підказка source_color є необов’язковою для рендерера сумісності та для шейдерів canvas_item, якщо HDR 2D вимкнено. Однак рекомендується завжди використовувати підказку source_color, тому що вона працює, навіть якщо ви змінюєте рендерер або вимикаєте HDR 2D.
Одноманітні групи
Щоб згрупувати декілька одностроїв у розділі інспектора, ви можете використати ключове слово group_uniform таким чином:
group_uniforms MyGroup;
uniform sampler2D test;
Закрити групу можна за допомогою:
group_uniforms;
Синтаксис також підтримує підгрупи (необов’язково оголошувати базову групу перед цим):
group_uniforms MyGroup.MySubgroup;
Глобальна уніформа
Іноді потрібно змінити параметр у багатьох різних шейдерах одночасно. Зі звичайною уніформою це потребує багато роботи, оскільки всі ці шейдери потрібно відстежувати та налаштовувати уніформу для кожного з них. Глобальні уніформи дозволяють створювати та оновлювати уніформи, які будуть доступні в усіх шейдерах, у кожному типі шейдерів (canvas_item, spatial, particles, sky і fog).
Глобальна уніформа особливо корисна для наслідків навколишнього середовища, які впливають на багато об’єктів у сцені, як-от згинання листя, коли гравець знаходиться поруч, або рух об’єктів з вітром.
Примітка
Глобальні уніформи — це не те саме, що глобальний обсяг для окремого шейдера. У той час як звичайні уніформи визначаються поза функціями шейдера і, отже, є глобальною сферою шейдера, глобальні уніформи є глобальними для всіх шейдерів у всьому проекті (але всередині кожного шейдера вони також знаходяться в глобальній області).
Щоб створити глобальну уніформу, відкрийте Налаштування проекту, а потім перейдіть на вкладку Глобальні шейдери. Укажіть назву для форми (з урахуванням регістру) і тип, а потім натисніть Додати у верхньому правому куті діалогового вікна. Потім ви можете змінити значення, призначене уніформі, клацнувши значення у списку уніформи:
Додавання глобальної уніформи на вкладці Shader Globals у налаштуваннях проекту
Після створення глобальної форми ви можете використовувати її в шейдері таким чином:
shader_type canvas_item;
global uniform vec4 my_color;
void fragment() {
COLOR = my_color.rgb;
}
Зауважте, що глобальна уніформа повинна існувати в налаштуваннях проекту на момент збереження шейдера, інакше компіляція не вдасться. Хоча ви можете призначити значення за замовчуванням за допомогою global uniform vec4 my_color = ... у коді шейдера, воно буде проігноровано, оскільки глобальна уніформа все одно має бути визначена в налаштуваннях проекту.
Щоб змінити значення глобальної форми під час виконання, використовуйте метод RenderingServer.global_shader_parameter_set у сценарії:
RenderingServer.global_shader_parameter_set("my_color", Color(0.3, 0.6, 1.0))
Призначення глобальних уніфікованих значень можна виконувати скільки завгодно разів без впливу на продуктивність, оскільки дані налаштувань не вимагають синхронізації між ЦП і ГП.
Ви також можете додавати або видаляти глобальні уніформи під час виконання:
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")
Додавання або видалення глобальних уніфікованих значень під час виконання має втрату продуктивності, хоча вона не така виражена порівняно з отриманням глобальних уніфікованих значень зі сценарію (див. попередження нижче).
Попередження
Хоча ви можете запитувати значення глобальної уніформи під час виконання в сценарії за допомогою RenderingServer.global_shader_parameter_get("uniform_name"), це має значне зниження продуктивності, оскільки потік візуалізації повинен синхронізуватися з потоком, що викликає.
Тому не рекомендується безперервно зчитувати стандартні значення глобального шейдера в сценарії. Якщо вам потрібно прочитати значення в сценарії після їх встановлення, розгляньте можливість створення autoload, де ви зберігаєте значення, які вам потрібно запитати, одночасно встановлюючи їх як глобальні уніфіковані.
Поінстанційний уніформа
Примітка
Уніформи для кожного екземпляра доступні як у шейдерах canvas_item (2D), так і в spatial (3D).
Іноді потрібно змінити параметр на кожному вузлі за допомогою матеріалу. Як приклад, у лісі, повному дерев, коли ви хочете, щоб кожне дерево мало дещо інший колір, який можна редагувати вручну. Без уніформи для кожного екземпляра це вимагає створення унікального матеріалу для кожного дерева (кожне з дещо іншим відтінком). Це робить керування матеріалами більш складним, а також має накладні витрати на продуктивність через сцену, яка вимагає більше унікальних екземплярів матеріалу. Тут також можна використовувати кольори вершин, але вони потребуватимуть створення унікальних копій сітки для кожного окремого кольору, що також має накладні витрати на продуктивність.
Уніформи для кожного екземпляра встановлюються для кожного GeometryInstance3D, а не для кожного екземпляра Material. Візьміть це до уваги під час роботи з сітками, яким призначено кілька матеріалів, або налаштуваннями 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;
}
Після збереження шейдера ви можете змінити значення уніформи для кожного екземпляра за допомогою інспектора:
Встановлення значення уніформи для кожного екземпляра в розділі GeometryInstance3D інспектора
Уніфіковані значення для кожного екземпляра також можна встановити під час виконання за допомогою методу set_instance_shader_parameter на вузлі, який успадковує від GeometryInstance3D:
$MeshInstance3D.set_instance_shader_parameter("my_color", Color(0.3, 0.6, 1.0))
Під час використання уніформи для окремих інстанцій є деякі обмеження, про які вам слід знати:
Уніформи для кожного екземпляра не підтримують текстури чи масиви, лише звичайні скалярні та векторні типи. Як тимчасове рішення, ви можете передати масив текстур як звичайну уніформу, а потім передати індекс текстури, яку потрібно намалювати, використовуючи уніформу для кожного екземпляра.
Примітка
У версіях GLSL до версії 4.0 (тобто GLSL 3.3 і нижче) неможливо безпосередньо індексувати масив текстур, використовуючи уніформу для кожного екземпляра, оскільки масиви семплерів можна індексувати лише за допомогою константних виразів часу компіляції. Це впливає на шейдери, скомпільовані за допомогою рендерера сумісності.
Якщо це впливає на вас, скористайтеся оператором
switchдля вибору текстури:
uniform sampler2D texture_array[2];
instance uniform int texture_index;
void fragment() {
vec4 color;
switch (texture_index) {
case 0:
color = texture(texture_array[0], UV);
break;
case 1:
color = texture(texture_array[1], UV);
break;
}
COLOR = color;
}
Практичний максимальний ліміт становить 16 екземплярів уніформи на шейдер.
Якщо ваша сітка використовує кілька матеріалів, параметри для першого знайденого матеріалу сітки «переможуть» над наступними, якщо вони не мають однакові ім’я, індекс і тип. У цьому випадку всі параметри впливають правильно.
Якщо ви зіткнетеся з наведеною вище ситуацією, ви можете уникнути конфліктів, вказавши вручну індекс (0-15) екземпляра uniform за допомогою підказки
instance_index:
instance uniform vec4 my_color : source_color, instance_index(5);
Комплектація уніформи з коду
Ви можете встановити форму з GDScript за допомогою методу set_shader_parameter():
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() є назва уніформи в шейдері. Воно має точно збігатися з назвою уніформи в шейдері, інакше його не буде розпізнано.
GDScript використовує інші типи змінних, ніж GLSL, тому, коли передаються змінні з GDScript у шейдери, Godot автоматично перетворює тип. Нижче наведено таблицю відповідних типів:
Тип GLSL |
Тип GDScript |
Нотатки |
|---|---|---|
bool |
bool |
|
bvec2 |
int |
Побітово упаковане int, де біт 0 (LSB) відповідає x. Наприклад, bvec2 з (bx, by) можна створити таким чином: bvec2_input: int = (int(bx)) | (int(by) << 1)
|
bvec3 |
int |
Побітово упаковане int, де біт 0 (LSB) відповідає x. |
bvec4 |
int |
Побітово упаковане int, де біт 0 (LSB) відповідає x. |
int |
int |
|
ivec2 |
Vector2i |
|
ivec3 |
Vector3i |
|
ivec4 |
Vector4i |
|
uint |
int |
|
uvec2 |
Vector2i |
|
uvec3 |
Vector3i |
|
uvec4 |
Vector4i |
|
float |
float |
|
vec2 |
Vector2 |
|
vec3 |
Vector3, Color |
Коли використовується Колір, він інтерпретуватиметься як (r, g, b). |
vec4 |
vector4, Color, Rect2, Plane, Quaternion |
Коли використовується Колір, він інтерпретуватиметься як (r, g, b, a). Коли використовується Rect2, він інтерпретуватиметься як (position.x, position.y, size.x, size.y). Коли використовується площина, це буде інтерпретовано як (normal.x, normal.y, normal.z, d). |
mat2 |
transform2D |
|
mat3 |
Основа |
|
mat4 |
projection, Transform3D |
Коли використовується Transform3D, вектор w встановлюється на ідентичність. |
sampler2D |
texture2D |
|
isampler2D |
texture2D |
|
usampler2D |
texture2D |
|
Sampler2DArray |
texture2DArray |
|
Isampler2DArray |
texture2DArray |
|
Usampler2DArray |
texture2DArray |
|
Sampler3D |
texture3D |
|
Isampler3D |
texture3D |
|
Usampler3D |
texture3D |
|
samplerCube |
cubemap |
Перегляньте Зміна типу імпорту для отримання інструкцій щодо імпортування кубичних карт для використання в Godot. |
samplerCubeArray |
Cubemap Array |
Підтримується лише у Forward+ і Mobile, а не в Compatibility. |
samplerExternalOES |
ExternalTexture |
Підтримується лише на платформі сумісності/Android. |
Примітка
Будьте обережні, встановлюючи уніформи шейдера з GDScript, оскільки помилка не буде видана, якщо тип не збігається. Ваш шейдер просто демонструватиме невизначену поведінку. Зокрема, це включає встановлення GDScript int/float (64 біт) у мову шейдерів Godot int/float (32 біт). Це може призвести до небажаних наслідків у випадках, коли потрібна висока точність.
Єдині межі
Існує обмеження на загальний розмір уніформи шейдерів, які можна використовувати в одному шейдері. На більшості настільних платформ цей ліміт становить 65536 байт, або 4096 vec4 уніформи. На мобільних платформах обмеження зазвичай становить 16384 байти або 1024 vec4 уніформи. Векторні уніформи, менші за vec4, такі як vec2 або vec3, доповнюються до розміру vec4. Скалярні форми, такі як int або float, не доповнюються, а bool доповнюється до розміру int.
Масиви враховуються як загальний розмір їхнього вмісту. Якщо вам потрібен уніфікований масив, розмір якого перевищує це обмеження, подумайте про упаковку даних у текстуру, оскільки вміст текстури не зараховується до цього обмеження, а лише розмір уніфікованого зразка.
Вбудовані змінні
Доступна велика кількість вбудованих змінних, таких як UV, COLOR та VERTEX. Доступність змінних залежить від типу шейдера (spatial, canvas_item, particle тощо) та використовуваної функції (vertex, fragment, light, start, process, sky або fog). Список доступних вбудованих змінних дивіться на відповідних сторінках:
Вбудовані функції
Підтримується велика кількість вбудованих функцій, що відповідають GLSL ES 3.0. Дивіться сторінку Built-in functions для деталей.
Коментарі
Мова затінення підтримує той самий синтаксис коментарів, який використовується в C# і C++, використовуючи
//для однорядкових коментарів і/* */для багаторядкових коментарів:Крім того, ви можете використовувати коментарі до документації, які відображаються в інспекторі під час наведення курсора на параметр шейдера. Коментарі до документації наразі підтримуються лише тоді, коли вони розміщені безпосередньо над оголошенням
uniform. Ці коментарі до документації підтримують лише синтаксис багаторядкових коментарів (навіть якщо вони використовуються в одному рядку) і повинні використовувати дві початкові зірочки (/**) замість однієї (/*):Зірочки в наступних рядках не є обов’язковими, але рекомендовані відповідно до Керівництво по стилю шейдерів. Ці зірочки автоматично видаляються інспектором, тому вони не відображатимуться у спливаючій підказці.