Up to date

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

Guía de estilo de Shaders

Esta guía enumera las convenciones para escribir shaders elegantes. La meta es motivar la escritura de un código limpio y legible, y promover la consistencia entre proyectos, discusiones y tutoriales. Esto también puede ayudar al desarrollo de herramientas de autoformateo.

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.

Las guías de estilos no están hechas con la intención de ser tratadas como reglamento estricto. Algunas veces no serás capaz de utilizar algunos de los lineamientos indicados, cuando eso suceda utiliza tu mejor criterio y busca la opinión de otros desarrolladores.

En general, mantener tu código consistente en tus proyectos y entre todos los de tu equipo es más importante que seguir esta guía al pié de la letra.

Nota

El editor integrado de shaders de Godot usa muchas de estas convenciones por defecto. Deja que te ayude.

Aquí hay un ejemplo completo de shader basado en estas guías de estilo:

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

Formateando

Codificación y caracteres especiales

  • Utiliza caracteres de salto de línea (LF) para separar líneas, no CRLF o CR. (por defecto del editor)

  • Utiliza un carácter de salto de línea al final de cada archivo. (por defecto del editor)

  • Usa la codificación UTF-8 sin Marca de orden de bytes. *( por defecto del editor) *

  • Usa Tabs en lugar de espacios para la indentación. *(por defecto del editor) *

Indentación

Cada nivel de indentación deberá ser una tabulación mayor que el bloque que la contiene.

Bien:

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

Mal:

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

Use 2 niveles de Indentación para distinguir las lineas de continuación de bloques regulares de código.

Bien:

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

Mal:

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

Saltos de línea y líneas en blanco

Para una regla general de identación, sigue el estilo "1TBS" que recomienda poner el corchete asociado con una estructura de control en la misma línea. Siempre usa llaves para las sentencias, aunque sean de una sola línea. Esto hace más fácil el refactor y evita errores cuando se agrega más de una línea a una sentencia if o similar.

Bien:

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

Mal:

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

Lineas en blanco

Envuelva sus funciones y definiciones de clases por una linea vacía :

void do_something() {
    // ...
}

void fragment() {
    // ...
}

Utiliza una (y sólo una) linea vacía dentro de las funciones para separar secciones lógicas.

Longitud de línea

Mantiene líneas de código individuales por debajo de los 100 caracteres.

Si puedes, intenta mantener las líneas por debajo de 80 caracteres. Esto ayuda a leer el código en pequeñas pantallas y con dos shader abiertos uno al lado del otro en un editor de texto externo. Por ejemplo, al mirar una revisión diferencial.

Una declaración/instrucción por linea

Nunca combine varias declaraciones en una sola línea.

Bien:

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

Mal:

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

La única excepción a esta regla es el operador ternario:

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

Espaciado de comentarios

Comentarios normales deberían comenzar con un espacio, pero no el código comentado. Esto ayuda a diferenciar texto comentado de código deshabilitado.

Bien:

// This is a comment.
//return;

Mal:

//This is a comment.
// return;

No utilice la sintaxis de comentarios de varias líneas si su comentario puede caber en una sola línea:

/* This is another comment. */

Nota

En el editor de shader, para hacer un comentario del código seleccionado (o descomentarlo), presione Ctrl + K. Esta característica agrega o elimina // al comienzo de las líneas seleccionadas.

Espacio en blanco

Utiliza siempre un espacio entre los operadores y después de las comas. Evita los espacios adicionales en las llamadas a funciones.

Bien:

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

Mal:

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

No uses espacios para alinear la verticalidad de las expresiones:

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

Números de coma flotante

Siempre especifica al menos un dígito tanto para la parte entera como para la fraccionaria. Esto facilita la distinción de los números reales de los números enteros, así como la distinción de los números mayores de 1 de los menores de 1.

Bien:

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

Mal:

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

Accediendo a miembros de vectores

Usa r, g, b, y a cuando accedas a los miembros de un vector si contiene un color. Si el vector contiene algo más que un color, usa x, y, z, y w. Esto permite a los que leen tu código entender mejor lo que representan los datos subyacentes.

Bien:

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

Mal:

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

Convenciones para la definición de nombres

Estas convenciones de nombres siguen el estilo de Godot Engine. Romperlas hará que tu código choque con las convenciones de nomenclaturas incorporadas, lo que crea un código inconsistente.

Funciones y Variables

Usa snake_case para nombrar funciones y variables:

void some_function() {
     float some_variable = 0.5;
}

Constantes

Escribe constantes en CONSTANT_CASE, todas en mayusculas con el símbolo de subrayado (_) para separar palabras:

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:

Bien:

#define HEIGHTMAP_ENABLED

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

#ifdef HEIGHTMAP_ENABLED
    sample_heightmap(position);
#endif
}

Mal:

#define heightmap_enabled

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

    #ifdef heightmap_enabled
        sample_heightmap(position);
    #endif
}

Orden de código

Sugerimos organizar el código de los shaders de esta manera:

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

Optimizamos el orden para hacer más fácil leer el código desde arriba hacia abajo, para ayudar a que los desarrolladores que leen el código por primera vez entiendan cómo funciona y para evitar errores vinculados al orden de declaración de variables.

Este orden del código sigue dos reglas generales:

  1. Los metadatos y las propiedades primero, seguidos de los métodos.

  2. "Public" viene antes de "private". En el contexto del lenguaje de shaders, "public" se refiere a lo que es fácilmente ajustable por el usuario (uniformes).

Variables locales

Declara las variables locales tan cerca como puedas de su primer uso. Esto hace que sea más fácil de seguir el código sin tener que desplazar el código demasiado para encontrar dónde fue declarada la variable.