Up to date

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

Shaders style guide

This style guide lists conventions to write elegant shaders. The goal is to encourage writing clean, readable code and promote consistency across projects, discussions, and tutorials. Hopefully, this will also support the development of auto-formatting tools.

Since the Godot shader language is close to C-style languages and GLSL, this guide is inspired by Godot's own GLSL formatting. You can view an example of a GLSL file in Godot's source code here.

Посібники зі стилів не розглядаються як жорсткі правила. Часом ви не зможете застосувати деякі наведені нижче вказівки. Коли це станеться, використовуйте найкраще рішення та запитайте в колег-розробників їх думку.

Загалом, стабільне дотримання визначеного стилю у ваших проєктах, та серед вашої команди, важливіше, ніж дотримання цього посібника.

Примітка

Godot's built-in shader editor uses a lot of these conventions by default. Let it help you.

Here is a complete shader example based on these guidelines:

shader_type canvas_item;
// Screen-space shader to adjust a 2D scene's brightness, contrast
// and saturation. Taken from
// https://github.com/godotengine/godot-demo-projects/blob/master/2d/screen_space_shaders/shaders/BCS.gdshader

uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;
uniform float brightness = 0.8;
uniform float contrast = 1.5;
uniform float saturation = 1.8;

void fragment() {
    vec3 c = textureLod(screen_texture, SCREEN_UV, 0.0).rgb;

    c.rgb = mix(vec3(0.0), c.rgb, brightness);
    c.rgb = mix(vec3(0.5), c.rgb, contrast);
    c.rgb = mix(vec3(dot(vec3(1.0), c.rgb) * 0.33333), c.rgb, saturation);

    COLOR.rgb = c;
}

Форматування

Кодування та спеціальні символи

  • Використовуйте символи зміни рядка (LF) для переривання рядків, не CRLF, чи CR. (за замовчуванням редактора)

  • Використовуйте символи зміни рядка в кінці кожного файлу. (за замовчуванням редактора)

  • Використовуйте кодування UTF-8 без маркера порядку байтів. (за замовчування редактора)

  • Використовуйте для відступів Tab замість пробілів. (за замовчуванням редактора)

Відступ

Each indent level should be one tab greater than the block containing it.

Правильно:

void fragment() {
    COLOR = vec3(1.0, 1.0, 1.0);
}

Неправильно:

void fragment() {
        COLOR = vec3(1.0, 1.0, 1.0);
}

Використовуйте 2 відступи, щоб відрізнити рядки продовження від звичайних блоків коду.

Правильно:

vec2 st = vec2(
        atan(NORMAL.x, NORMAL.z),
        acos(NORMAL.y));

Неправильно:

vec2 st = vec2(
    atan(NORMAL.x, NORMAL.z),
    acos(NORMAL.y));

Розриви рядків та порожні рядки

Для загального правила відступу дотримуйтесь " Стиль 1TBS "<https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_ (OTBS)> _, який рекомендує розміщувати дужку, пов'язану з контрольним оператором, на одному рядок. Завжди використовуйте дужки для операторів, навіть якщо вони охоплюють лише один рядок. Це полегшує їх рефакторинг та дозволяє уникнути помилок при додаванні більше рядків до заяви `` if '' або подібного.

Правильно:

void fragment() {
    if (true) {
        // ...
    }
}

Неправильно:

void fragment()
{
    if (true)
        // ...
}

Порожні рядки

Surround function definitions with one (and only one) blank line:

void do_something() {
    // ...
}

void fragment() {
    // ...
}

Use one (and only one) blank line inside functions to separate logical sections.

Довжина рядка

Зберігайте в окремих рядках коду не більше 100 символів.

If you can, try to keep lines under 80 characters. This helps to read the code on small displays and with two shaders opened side-by-side in an external text editor. For example, when looking at a differential revision.

Одне твердження на рядок

Never combine multiple statements on a single line.

Правильно:

void fragment() {
    ALBEDO = vec3(1.0);
    EMISSION = vec3(1.0);
}

Неправильно:

void fragment() {
    ALBEDO = vec3(1.0); EMISSION = vec3(1.0);
}

Єдиним винятком із цього правила є потрійний оператор:

void fragment() {
     bool should_be_white = true;
     ALBEDO = should_be_white ? vec3(1.0) : vec3(0.0);
 }

Пробіл коментаря

Регулярні коментарі повинні починатися з пробілу, але не код, який ви закоментовуєте. Це допомагає відрізняти текстові коментарі від відключеного коду.

Правильно:

// This is a comment.
//return;

Неправильно:

//This is a comment.
// return;

Don't use multiline comment syntax if your comment can fit on a single line:

/* This is another comment. */

Примітка

In the shader editor, to make the selected code a comment (or uncomment it), press Ctrl + K. This feature adds or removes // at the start of the selected lines.

Вільний простір

Always use one space around operators and after commas. Also, avoid extraneous spaces in function calls.

Правильно:

COLOR.r = 5.0;
COLOR.r = COLOR.g + 0.1;
COLOR.b = some_function(1.0, 2.0);

Неправильно:

COLOR.r=5.0;
COLOR.r = COLOR.g+0.1;
COLOR.b = some_function (1.0,2.0);

Не використовуйте пробіли для вирівнювання виразів по вертикалі:

ALBEDO.r   = 1.0;
EMISSION.r = 1.0;

Floating-point numbers

Always specify at least one digit for both the integer and fractional part. This makes it easier to distinguish floating-point numbers from integers, as well as distinguishing numbers greater than 1 from those lower than 1.

Правильно:

void fragment() {
    ALBEDO.rgb = vec3(5.0, 0.1, 0.2);
}

Неправильно:

void fragment() {
    ALBEDO.rgb = vec3(5., .1, .2);
}

Accessing vector members

Use r, g, b, and a when accessing a vector's members if it contains a color. If the vector contains anything else than a color, use x, y, z, and w. This allows those reading your code to better understand what the underlying data represents.

Правильно:

COLOR.rgb = vec3(5.0, 0.1, 0.2);

Неправильно:

COLOR.xyz = vec3(5.0, 0.1, 0.2);

Конвенції іменування

Ці конвенції іменування відповідають стилю Godot Engine. Якщо їх порушити, ваш код зіткнеться із вбудованими конвенціями іменування, що призведе до непослідовного коду.

Функції та змінні

Використовуйте snake_case (всі слова з маленької букви відокремлені підкресленням) для назв функцій та змінних:

void some_function() {
     float some_variable = 0.5;
}

Константи

Записуйте константи в стилі CONSTANT_CASE (всі букви написані з великим регістром, слова відокремлені підкресленням (_)):

const float GOLDEN_RATIO = 1.618;

Preprocessor directives

Shader preprocessor directives should be written in CONSTANT__CASE. Directives should be written without any indentation before them, even if nested within a function.

To preserve the natural flow of indentation when shader errors are printed to the console, extra indentation should not be added within #if, #ifdef or #ifndef blocks:

Правильно:

#define HEIGHTMAP_ENABLED

void fragment() {
    vec2 position = vec2(1.0, 2.0);

#ifdef HEIGHTMAP_ENABLED
    sample_heightmap(position);
#endif
}

Неправильно:

#define heightmap_enabled

void fragment() {
    vec2 position = vec2(1.0, 2.0);

    #ifdef heightmap_enabled
        sample_heightmap(position);
    #endif
}

Порядок коду

We suggest to organize shader code this way:

01. shader type declaration
02. render mode declaration
03. // docstring

04. uniforms
05. constants
06. varyings

07. other functions
08. vertex() function
09. fragment() function
10. light() function

Ми оптимізували порядок, щоб полегшити читання коду зверху вниз, щоб допомогти розробникам, які вперше читають код, зрозуміти, як він працює, та уникнути помилок, пов'язаних із порядком оголошення змінних.

This code order follows two rules of thumb:

  1. Metadata and properties first, followed by methods.

  2. "Public" comes before "private". In a shader language's context, "public" refers to what's easily adjustable by the user (uniforms).

Локальні змінні

Оголошуйте локальні змінні якомога ближче до їх першого використання. Це полегшує відстеження коду, без необхідності прокручувати занадто багато, щоб знайти де оголошено змінну.