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. (редактор по умолчанию)

  • Используйте символ перевода строки в конце каждого файла. (editor default)

  • Используйте кодировку UTF-8 без маркера последовательности байтов. (редактор по умолчанию)

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

Отступ

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

Шейдерный препроцессор 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).

Локальные переменные

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