Up to date

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

Guia de estilo de shaders

Este guia de estilo lista convenções para escrever de forma elegante os shaders. O objetivo é encorajar a escrita de código limpo e legível e promover a consistência entre projetos, discussões e tutoriais. Esperamos que isto também apoie o desenvolvimento de ferramentas de formatação automática.

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.

Guias de estilo não são regras. Às vezes, você talvez não poderá aplicar algumas das diretrizes abaixo. Quando isso acontecer, use seu melhor julgamento, e peça opiniões de outros desenvolvedores.

Em geral, manter seu código consistente em seus projetos e em seu time é mais importante que seguir este guia à risca.

Nota

O editor de shader integrado de Godot usa muitas dessas convenções por padrão. Deixa que te ajude.

Aqui está um exemplo completo de shader baseado nestas diretrizes:

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;
}

Formatação

Codificação e caracteres especiais

  • Use caracteres line feed (LF) para quebrar linhas, não CRLF ou CR. (padrão do editor)

  • Use um caractere line feed no final de cada arquivo. (padrão do editor)

  • Use a codificação UTF-8 sem uma marca de ordem de byte. (padrão do editor)

  • Use Tabs ao invés de espaços para indentação. (padrão do editor)

Recuo

Cada nível de indentação deve ser uma unidade maior que a do bloco que o contém.

Bom:

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

Ruim:

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

Use 2 níveis de indentação para distinguir linhas contínuas de blocos de código regulares.

Bom:

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

Ruim:

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

Quebras de linha e linhas em branco

Para uma regra geral de recuo, siga the "1TBS Style" <https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)> _ que recomenda colocar a chave associada a uma instrução de controle na mesma linha. Sempre use colchetes para as declarações, mesmo se elas ocuparem apenas uma linha. Isso os torna mais fáceis de refatorar e evita erros ao adicionar mais linhas a uma instrução `` if`` ou similar.

Bom:

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

Ruim:

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

Linhas em branco

Envolva as definições de função com uma (e apenas uma) linha em branco:

void do_something() {
    // ...
}

void fragment() {
    // ...
}

Use uma (e apenas uma) linha em branco dentro das funções para separar seções lógicas.

Tamanho de linha

Mantenha linhas individuais de código abaixo de 100 caracteres.

Se puder, tente manter as linhas com menos de 80 caracteres. Isso ajuda a ler o código em telas pequenas e com dois shaders abertos lado a lado em um editor de texto externo. Por exemplo, ao olhar para uma revisão diferencial.

Uma declaração por linha

Never combine multiple statements on a single line.

Bom:

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

Ruim:

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

A única exceção a essa regra é o operador ternário:

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

Espaçamento de comentários

Comentários normais devem começar com um espaço, ao contrário de código que você desativa usando um comentário. Isso ajuda a diferenciar comentários em texto de código desativado.

Bom:

// This is a comment.
//return;

Ruim:

//This is a comment.
// return;

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

/* This is another comment. */

Nota

No editor de shader, para fazer o código selecionado um comentário (ou descomentar), pressione Ctrl + K. Este recurso adiciona ou remove // no início das linhas selecionadas.

Espaço em branco

Use sempre um espaço ao redor dos operadores e depois das vírgulas. Além disso, evite espaços estranhos em chamadas de função.

Bom:

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

Ruim:

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

Não use espaços para alinhas expressões verticalmente:

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

Números de ponto flutuante (real)

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.

Bom:

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

Ruim:

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

Acessando membros do vetor

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.

Bom:

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

Ruim:

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

Convenções de nomes

Estas convenções de nomeação seguem o estilo do Godot Engine. Quebrá-las fará com que o seu código fique diferente das convenções de nomeação embutidas, o que deixa o seu código inconsistente.

Funções e variáveis

Use snake_case para nomear funções e variáveis:

void some_function() {
     float some_variable = 0.5;
}

Constantes

Utilize CONSTANT_CASE, com todas as letras maiúsculas e um sublinhado para separas as palavras:

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:

Bom:

#define HEIGHTMAP_ENABLED

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

#ifdef HEIGHTMAP_ENABLED
    sample_heightmap(position);
#endif
}

Ruim:

#define heightmap_enabled

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

    #ifdef heightmap_enabled
        sample_heightmap(position);
    #endif
}

Ordem do código

Sugerimos organizar o código do shader desta forma:

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

Otimizamos essa ordem pra deixar o código mais fácil de ler de cima pra baixo, para ajudar desenvolvedores lendo o código pela primeira vez a entender como ele funciona, e para evitar erros referentes à ordem da declaração de variáveis.

Essa ordem de código segue duas regras gerais:

  1. Metadados e propriedades primeiro, seguidos por métodos.

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

Variáveis locais

Declare variáveis locais o mais pŕoximo possível de seu primeiro uso. Isto torna mais fácil seguir o código, sem ter que rolar muito para encontrar onde a variável foi declarada.