Lenguaje de shading

Introducción

Godot utiliza un lenguaje de shading similar a GLSL ES 3.0. La mayoría de los tipos de datos y funciones están soportados y los pocos que no figuran serán agregados posteriormente.

Si ya estás familiarizado con el GLSL, la Godot Shader Migration Guide es un recurso que te ayudará a pasar del GLSL regular al lenguaje shader de Godot.

Tipos de datos

La mayoría de los tipos de datos de GLSL ES 3.0 están soportados:

Tipo Descripción
void Tipo de dato nulo, útil sólo para funciones que no retornan nada.
bool Tipo de dato booleano, puede contener solamente true o false.
bvec2 Vector de dos componentes booleanas.
bvec3 Vector de tres componentes booleanas.
bvec4 Vector de cuatro componentes booleanas.
int Escalar entero con signo.
ivec2 Vector de dos componentes de entero con signo.
ivec3 Vector de tres componentes de enteros con signo.
ivec4 Vector de cuatro componentes de enteros con signo.
uint Escalar entero sin signo, no puede contener números negativos.
uvec2 Vector de dos componentes de enteros sin signo.
uvec3 Vector de tres componentes de enteros sin signo.
uvec4 Vector de cuatro componentes de enteros sin signo.
float Escalar de punto flotante.
vec2 Vector de dos componentes de valores de punto flotante.
vec3 Vector de tres componentes de valores de punto flotante.
vec4 Vector de cuatro componentes de valores de punto flotante.
mat2 Matrix 2x2, ordenada por columna.
mat3 Matriz 3x3, ordenada por columnas (column-major order).
mat4 Matriz 4x4, ordenada por columnas (column-major order).
sampler2D Tipo Sampler para vincular texturas 2D, las cuales son leídas como float.
isampler2D Tipo Sampler para vincular texturas 2D, las cuales son leídas como enteros con signo.
usampler2D Tipo Sampler para vincular texturas 2D, las cuales son leídas como enteros sin signo.
sampler2DArray Tipo Sampler para vincular arrays de texturas 2D, las cuales son leídas como float.
isampler2DArray Tipo Sampler para vincular arrays de texturas 2D, las cuales son leídas como enteros con signo.
usampler2DArray Tipo Sampler para vincular arrays de texturas 2D, las cuales son leídas como enteros sin signo.
sampler3D Tipo Sampler para vincular texturas 3D, las cuales son leídas como float.
isampler3D Tipo Sampler para vincular texturas 3D, las cuales son leídas como enteros con signo.
usampler3D Tipo Sampler para vincular texturas 3D, las cuales son leídas como enteros sin signo.
samplerCube Tipo Sampler para vincular Cubemaps, los que son leídos como floats.

Casting

Como en GLSL ES 3.0, el casting implícito entre escalares y vectores del mismo tamaño pero distinto tipo no está permitido. Casting de tipos de distinto tamaño tampoco está permitido. La conversión debe ser explícita mediante el uso de constructores.

Ejemplo:

float a = 2; // invalid
float a = 2.0; // valid
float a = float(2); // valid

Las constantes enteras son con signo, siempre se necesitará casting para convertirlas a enteros sin signo:

int a = 2; // valid
uint a = 2; // invalid
uint a = uint(2); // valid

Miembros

Escalares individuales miembros de tipos vectores se acceden mediante los miembros "x", "y", "z" y "w". Alternativamente, "r", "g", "b" y "a" también funciona y es equivalente. Usa la que se ajuste mejor al caso.

Para matrices, se utiliza indexación m[fila][columna] para acceder a cada escalar, o m[índice] para acceder al vector por índice de fila. Por ejemplo, para acceder a la posición Y de un objeto en mat4, deberás usar la sintaxis m[3][1].

Construcción

La construcción de tipos vector deberá pasar siempre:

// 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);

La construcción de tipos matriz requiere pasar vectores de la misma dimensión de la matriz. Puedes también construir una matriz diagonal usando la sintaxis matx(float). De este modo, mat4(1.0) es una matriz identidad.

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

Las matrices también pueden construirse a partir de una matriz de otra dimensión. Hay dos reglas: Si se construye una matriz más grande a partir de una matriz más pequeña, las filas y columnas adicionales se fijan a los valores que tendrían en una matriz de identidad. Si se construye una matriz más pequeña a partir de una matriz más grande, se utiliza la submatriz superior izquierda de la matriz más grande.

mat3 basis = mat3(WORLD_MATRIX);
mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4);

Swizzling

Es posible obtener cualquier combinación de ellos en cualquier orden, siempre que el resultado sea igual a otro tipo vector (o escalar). Es más fácil mostrarlo que explicarlo:

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; // Order does not matter.
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.

Precisión

Es posible agregar modificadores de precisión a los tipos de datos, se pueden usar para uniforms, variables, argumentos y varyings:

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 (default)

El uso de baja precisión para algunas operaciones puede acelerar la matemática involucrada (al costo de, por supuesto, baja precisión). Esto es raramente necesitado en vertex shaders (donde precisión máxima es necesitada la mayor parte del tiempo), pero normalmente se necesita en los fragment.

Algunas arquitecturas (principalmente móviles) pueden beneficiarse significativamente de esto, pero existen desventajas como la sobrecarga adicional de conversión entre precisiones. Consulte la documentación de la arquitectura de destino para obtener más información. En muchos casos, los controladores móviles provocan un comportamiento incoherente o inesperado y es mejor evitar especificar la precisión a menos que sea necesario.

Arrays

Los arrays son contenedores de múltiples variables de un tipo similar. Nota: A partir de Godot 3.2, sólo se han implementado arrays locales y variables.

Arrays locales

Los arrays locales se declaran en funciones. Pueden utilizar todos los tipos de datos permitidos, excepto los samplers. La declaración de los arrays sigue una sintaxis de tipo C: [const] + [precision] + typename + identifier + [array size].

void fragment() {
    float arr[3];
}

Pueden ser inicializados al principio como:

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

Puedes declarar múltiples arrays (incluso con diferentes tamaños) en una expresión:

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

Para acceder a un elemento del array, utiliza la sintaxis de indexación:

float arr[3];

arr[0] = 1.0; // setter

COLOR.r = arr[0]; // getter

Los arrays también tienen una función integrada .length() (que no debe confundirse con la función integrada length()). No acepta ningún parámetro y devolverá el tamaño del array.

float arr[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr.length(); i++) {
    // ...
}

Nota

Si usas un índice por debajo de 0 o mayor que el tamaño de del array - el shader se bloqueará y romperá la renderización. Para evitarlo, usa las funciones length(), if, o clamp() para asegurarte de que el índice está entre 0 y la longitud del array. Siempre prueba y comprueba cuidadosamente tu código. Si pasas una expresión constante o un número simple, el editor comprobará sus límites para evitar este fallo.

Constantes

Use la palabra clave const antes de la declaración de la variable para hacerla inmutable, lo que significa que no puede ser modificada. Todos los tipos básicos, excepto las samplers pueden ser declarados como constantes. Acceder y utilizar un valor constante es ligeramente más rápido que utilizar un uniforme. Las constantes deben ser inicializadas en su declaración.

const vec2 a = vec2(0.0, 1.0);
vec2 b;

a = b; // invalid
b = a; // valid

Las constantes no pueden ser modificadas y además no pueden tener indicios, pero varias de ellas (si tienen el mismo tipo) pueden ser declaradas en una sola expresión, por ejemplo

const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);

Al igual que las variables, los arrays también pueden ser declarados con const.

const float arr[] = { 1.0, 0.5, 0.0 };

arr[0] = 1.0; // invalid

COLOR.r = arr[0]; // valid

Las constantes pueden ser declaradas tanto globalmente (fuera de cualquier función) como localmente (dentro de una función). Las constantes globales son útiles cuando se quiere tener acceso a un valor en todo el shader que no necesita ser modificado. Al igual que los uniformes, las constantes globales se comparten entre todas las etapas del shader, pero no son accesibles fuera del shader.

shader_type spatial;

const float PI = 3.14159265358979323846;

Operadores

El lenguaje de shading de Godot soporta el mismo conjunto de operadores de GLSL ES 3.0. Debajo está la lista de ellos en orden de precedencia:

Precedencia Clase Operador
1 (más alta) agrupamiento entre paréntesis ()
2 unario +, -, !, ~
3 multiplicativo /, *, %
4 aditivo +, -
5 Desplazamiento bit-wise <<, >>
6 relacional <, >, <=, >=
7 igualdad ==, !=
8 AND bit-wise &
9 OR bit-wise excluyente ^
10 OR bit-wise inclusivo |
11 AND lógico &&
12 (la más baja) OR lógico inclusivo ||

Control de Flujo

El lenguaje de shading de Godot soporta los tipos más comunes de control de flujo:

// if and else
if (cond) {

} else {

}

// switch
switch(i) { // signed integer expression
    case -1:
        break;
    case 0:
        return; // break or return
    case 1: // pass-through
    case 2:
        break;
    //...
    default: // optional
        break;
}

// for loops
for (int i = 0; i < 10; i++) {

}

// while
while (true) {

}

// do while
do {

} while(true);

Ten en cuenta que, en las GPU modernas, puede producirse un bucle infinito que congele la aplicación (incluido el editor). Godot no puede protegerte de esto ¡así que ten cuidado de no cometer este error!

Advertencia

Cuando se exporta un proyecto GLES2 a HTML5, se utiliza WebGL 1.0. WebGL 1.0 no soporta bucles dinámicos, así que los shaders que los usan no funcionarán allí.

Descartando

Las funciones fragment y light pueden usar la palabra clave discard. Si es utilizada, el fragment será descartado y nada será escrito.

Funciones

Es posible definir cualquier función en un shader de Godot. Estas llevan la siguiente sintaxis:

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

Sólo puede utilizar las funciones que han sido definidas arriba (más arriba en el editor) la función desde la que las está llamando.

Los argumentos de las funciones pueden tener calificadores especiales:

  • in: Significa que el argumento es sólo para lectura (por defecto).
  • out: Significa que el argumento es sólo para escritura.
  • inout: Significa que el argumento es pasado por referencia.

Ejemplo:

void sum2(int a, int b, inout int result) {
    result = a + b;
}

Varyings

Para enviar datos desde la función vertex hacia la función fragment, se utilizan varyings. Se establecen para cada vértice primitivo en el procesador vertex, y el valor se interpola (y se corrige la perspectiva) al alcanzar cada píxel en el procesador fragment.

shader_type spatial;

varying vec3 some_color;
void vertex() {
    some_color = NORMAL; // Make the normal the color.
}

void fragment() {
    ALBEDO = some_color;
}

La variación también puede ser un array:

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
}

Calificadores de interpolación

Ciertos valores son interpolados durante el shading pipeline. Puedes modificar cómo se harán esas interpolaciones usando calificadores de interpolación.

shader_type spatial;

varying flat vec3 our_color;

void vertex() {
    our_color = COLOR.rgb;
}

void fragment() {
    ALBEDO = our_color;
}

Estos son los dos posibles calificadores de interpolación:

Calificador Descripción
flat El valor no es interpolado.
smooth El valor es interpolado en una manera corregida por perspectiva. Es el modo por defecto.

Uniforms

Es posible pasar valores a shaders. Son globales para todo el shader y se llaman uniforms. Cuando un shader es asignado a un material, los uniforms aparecerán editables como parámetros. Los uniforms no pueden ser escritos desde dentro de un shader.

shader_type spatial;

uniform float some_value;

Puedes poner los uniformes en el editor en el material. O puedes fijarlos a través de GDScript:

material.set_shader_param("some_value", some_value)

Nota

El primer argumento para set_shader_param es el nombre del uniforme en el shader. Debe coincidir exactamente con el nombre del uniforme en el shader o de lo contrario no será reconocido.

Cualquier tipo GLSL excepto void puede ser un uniform. Adicionalmente, Godot provee shader hints opcionales para hacer que el compilador entienda para qué se usará el uniform.

shader_type spatial;

uniform vec4 color : hint_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : hint_color = vec4(1.0);

Es importante entender que las texturas que se suministran como color requieren de pistas para una adecuada conversión sRGB->lineal (es decir, hint_albedo), ya que el motor 3D de Godot renderiza en el espacio de color lineal.

A continuación está la lista completa de hints:

Tipo Hint Descripción
vec4 hint_color Usado como color
int, float hint_range(min, max[, step]) Se utiliza como rango (con mín./máx./paso)
sampler2D hint_albedo Usado como color de albedo, blanco por defecto
sampler2D hint_black_albedo Usado como color de albedo, negro por defecto
sampler2D hint_normal Usado como normalmap
sampler2D hint_white Como valor, blanco por defecto.
sampler2D hint_black Como valor, negro por defecto
sampler2D hint_aniso Como flowmap, hacia la derecha por defecto.

GDScript usa tipos de variables diferentes a los que usa el GLSL, así que al pasar las variables de GDScript a los shaders, Godot convierte el tipo automáticamente. A continuación se muestra una tabla de los tipos correspondientes:

Tipo GDScript Tipo GLSL
bool bool
int int
float float
Vector2 vec2
Vector3 vec3
Color vec4
Transform mat4
Transform2D mat4

Nota

Tengan cuidado al establecer los uniformes shader de GDScript, no se arrojará ningún error si el tipo no coincide. Su shader sólo mostrará un comportamiento indefinido.

A los uniforms se les pueden asignar valores por defecto:

shader_type spatial;

uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : hint_color = vec4(1.0);

Funciones integradas

Se soporta un gran número de funciones propias de GLSL ES 3.0. Cuando se utiliza la nomenclatura vec_type (float), vec_int_type, vec_uint_type, vec_bool_type, puede ser escalar o vector.

Nota

Para una lista de las funciones que no están disponibles en el backend GLES2, por favor vea la Differences between GLES2 and GLES3 doc.

Función Descripción
vec_type radians (vec_type degrees) Convierte de grados a radianes
vec_type degrees (vec_type radians) Convierte de radianes a grados
vec_type sin (vec_type x) Seno
vec_type cos (vec_type x) Coseno
vec_type tan (vec_type x) Tangente
vec_type asin (vec_type x) Arcoseno
vec_type acos (vec_type x) Arcocoseno
vec_type atan (vec_type y_over_x) Arcotangente
vec_type atan (vec_type y, vec_type x) Arcotangente para convertir un vector a ángulo
vec_type sinh (vec_type x) Seno hiperbólico
vec_type cosh (vec_type x) Coseno hiperbólico
vec_type tanh (vec_type x) Tangente hiperbólica
vec_type asinh (vec_type x) Seno hiperbólico inverso
vec_type acosh (vec_type x) Coseno hiperbólico inverso
vec_type atanh (vec_type x) Tangente hiperbólica inversa
vec_type pow (vec_type x, vec_type y) Potencia (sin definir si x < 0 o si x = 0 y y <= 0)
vec_type exp (vec_type x) Exponencial base e
vec_type exp2 (vec_type x) Exponencial base 2
vec_type log (vec_type x) Logaritmo natural
vec_type log2 (vec_type x) Logartimo base 2
vec_type sqrt (vec_type x) Raíz cuadrada
vec_type inversesqrt (vec_type x) Raíz cuadrada inversa
vec_type abs (vec_type x) Absoluto
ivec_type abs (ivec_type x) Absoluto
vec_type sign (vec_type x) Signo
ivec_type sign (ivec_type x) Signo
vec_type floor (vec_type x) Redondeo hacia abajo
vec_type round (vec_type x) Redondeo
vec_type roundEven (vec_type x) Redondeo hacia el número par más cercano
vec_type trunc (vec_type x) Truncamiento
vec_type ceil (vec_type x) Redondeo hacia arriba
vec_type fract (vec_type x) Parte fraccionaria
vec_type mod (vec_type x, vec_type y) Remanente
vec_type mod (vec_type x , float y) Remanente
vec_type modf (vec_type x, out vec_type i) Parte fraccionaria de x, con i como parte entera
vec_type min (vec_type a, vec_type b) Mínimo
vec_type max (vec_type a, vec_type b) Máximo
vec_type clamp (vec_type x, vec_type min, vec_type max) Restringe a min..max
float mix (float a, float b, float c) Interpolación lineal
vec_type mix (vec_type a, vec_type b, float c) Interpolación lineal (coeficiente escalar)
vec_type mix (vec_type a, vec_type b, vec_type c) Interpolación lineal (coeficiente vectorial)
vec_type mix (vec_type a, vec_type b, bvec_type c) Interpolación lineal (selección booleana vectorial)
vec_type step (vec_type a, vec_type b) b[i] < a[i] ? 0.0 : 1.0
vec_type step (float a, vec_type b) b[i] < a ? 0.0 : 1.0
vec_type smoothstep (vec_type a, vec_type b, vec_type c) Interpolación de Hermite
vec_type smoothstep (float a, float b, vec_type c) Interpolación de Hermite
bvec_type isnan (vec_type x) Devuelve true si el escalar o componente de un vector es NaN (no es un número)
bvec_type isinf (vec_type x) Devuelve true si el escalar o componente de un vector es INF (infinito)
ivec_type floatBitsToInt (vec_type x) Copia por bits Float->Int, sin conversión
uvec_type floatBitsToUint (vec_type x) Copia por bits Float->UInt, sin conversión
vec_type intBitsToFloat (ivec_type x) Copia por bit Int->Float, sin conversión
vec_type uintBitsToFloat (uvec_type x) Copia por bit UInt->Float, sin conversión
float length (vec_type x) Longitud del vector
float distance (vec_type a, vec_type b) Distancia entre vectores, lo mismo que length(a - b)
float dot (vec_type a, vec_type b) Producto punto
vec3 cross (vec3 a, vec3 b) Producto vectorial
vec_type normalize (vec_type x) Normalizado a la longitud de unidad
vec3 reflect (vec3 I, vec3 N) Reflejo
vec3 refract (vec3 I, vec3 N, float eta) Refracción
vec_type faceforward (vec_type N, vec_type I, vec_type Nref) If dot(Nref, I) < 0, return N, otherwise –N
mat_type matrixCompMult (mat_type x, mat_type y) Multiplicación de matrices
mat_type outerProduct (vec_type column, vec_type row) Producto tensorial
mat_type transpose (mat_type m) Matriz transpuesta
float determinant (mat_type m) Matriz determinante
mat_type inverse (mat_type m) Matriz inversa
bvec_type lessThan (vec_type x, vec_type y) Comparación booleana vectorial < entre vectores int/uint/float
bvec_type greaterThan (vec_type x, vec_type y) Comparación booleana vectorial > entre vectores int/uint/float
bvec_type lessThanEqual (vec_type x, vec_type y) Comparación booleana vectorial <= entre vectores int/uint/float
bvec_type greaterThanEqual (vec_type x, vec_type y) Comparación booleana vectorial >= entre vectores int/uint/float
bvec_type equal (vec_type x, vec_type y) Comparación booleana vectorial == entre vectores int/uint/float
bvec_type notEqual (vec_type x, vec_type y) Comparación booleana vectorial != entre vectores int/uint/float
bool any (bvec_type x) Cualquier componente es true
bool all (bvec_type x) Todos los componentes son true
bvec_type not (bvec_type x) Invierte un vector booleano
ivec2 textureSize (sampler2D_type s, int lod) Obtiene el tamaño de una textura 2D
ivec3 textureSize (sampler2DArray_type s, int lod) Obtiene el tamaño de un array de texturas 2D
ivec3 textureSize (sampler3D s, int lod) Obtiene el tamaño de una textura 3D
ivec2 textureSize (samplerCube s, int lod) Obtiene el tamaño de una textura cubemap
vec4_type texture (sampler2D_type s, vec2 uv [, float bias]) Realiza una lectura de textura 2D
vec4_type texture (sampler2DArray_type s, vec3 uv [, float bias]) Realiza una lectura de textura 2D
vec4_type texture (sampler3D_type s, vec3 uv [, float bias]) Realiza una lectura de textura 3D
vec4 texture (samplerCube s, vec3 uv [, float bias]) Realiza una lectura de textura cubemap
vec4_type textureProj (sampler2D_type s, vec3 uv [, float bias]) Realiza una lectura de textura 2D con proyección
vec4_type textureProj (sampler2D_type s, vec4 uv [, float bias]) Realiza una lectura de textura 2D con proyección
vec4_type textureProj (sampler3D_type s, vec4 uv [, float bias]) Realiza una lectura de textura 3D con proyección
vec4_type textureLod (sampler2D_type s, vec2 uv, float lod) Realiza una lectura de textura 2D con mipmap personalizado
vec4_type textureLod (sampler2DArray_type s, vec3 uv, float lod) Realiza una lectura de textura 2D con mipmap personalizado
vec4_type textureLod (sampler3D_type s, vec3 uv, float lod) Realiza una lectura de textura 3D con mipmap personalizado
vec4 textureLod (samplerCube s, vec3 uv, float lod) Realiza una lectura de textura 3D con mipmap personalizado
vec4_type textureProjLod (sampler2D_type s, vec3 uv, float lod) Realiza una lectura de textura 2D con proyección/LOD
vec4_type textureProjLod (sampler2D_type s, vec4 uv, float lod) Realiza una lectura de textura 2D con proyección/LOD
vec4_type textureProjLod (sampler3D_type s, vec4 uv, float lod) Realiza una lectura de textura 3D con proyección/LOD
vec4_type texelFetch (sampler2D_type s, ivec2 uv, int lod) Obtiene un texel usando coordenadas enteras
vec4_type texelFetch (sampler2DArray_type s, ivec3 uv, int lod) Obtiene un texel usando coordenadas enteras
vec4_type texelFetch (sampler3D_type s, ivec3 uv, int lod) Obtiene un texel usando coordenadas enteras
vec_type dFdx (vec_type p) Derivada en x mediante diferenciación local
vec_type dFdy (vec_type p) Derivada en y mediante diferenciación local
vec_type fwidth (vec_type p) Suma del derivado absoluto en x e y