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.

If you are already familiar with GLSL, the Godot Shader Migration Guide is a resource that will help you transition from regular GLSL to Godot’s shading language.

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 Boolean datatype, can only contain true or 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.
mat4 Matriz 4x4, ordenada por columnas.
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 Sampler type for binding 2D texture arrays, which are read as float.
isampler2DArray Sampler type for binding 2D texture arrays, which are read as signed integer.
usampler2DArray Sampler type for binding 2D texture arrays, which are read as unsigned integer.
sampler3D Sampler type for binding 3D textures, which are read as float.
isampler3D Sampler type for binding 3D textures, which are read as signed integer.
usampler3D Sampler type for binding 3D textures, which are read as unsigned integer.
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);

Matrices can also be built from a matrix of another dimension. There are two rules : If a larger matrix is constructed from a smaller matrix, the additional rows and columns are set to the values they would have in an identity matrix. If a smaller matrix is constructed from a larger matrix, the top, left submatrix of the larger matrix is used.

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.
float c = b.w; // Invalid, because "w" is not present in vec3 b.

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.

Ten en cuenta que algunas arquitecturas (principalmente móviles) se benefician mucho de esto, pero también están restringidas (conversiones entre precisiones tiene un costo). Lee la documentación relevante de la arquitectura destino para saber más al respecto. Honestamente, los drivers de móviles suelen tener bugs y para no tener problemas hay que intentar hacer shaders simples y no especificar precisión a menos que sea realmente necesario.

Arreglos

Arrays are containers for multiple variables of a similar type. Note: As of Godot 3.2, only local arrays have been implemented.

Local arrays

Local arrays are declared in functions. They can use all of the allowed datatypes, except samplers. The array declaration follows a C-style syntax: typename + identifier + [array size].

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

They can be initialized at the beginning like:

float arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor

vec2 arr_v2[2] = { vec2(0.0, 0.0), vec2(1.0, 1.0) }; // second constructor

bool bvec_arr[] = { false, false, true, true }; // third constructor - size is defined automatically from the argument count

You can declare multiple arrays (even with different sizes) in one expression:

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

To access an array element, use the indexing syntax:

float arr[3];

arr[0] = 1.0; // setter

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

Arrays also have a built-in function .length() (not to be confused with the built-in length() function). It doesn’t accept any parameters and will return the array’s size.

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

Note: If you use an index below 0 or greater than array size - the shader will crash and break rendering. To prevent this, use length(), if, or clamp() functions to ensure the index is between 0 and the array’s length. Always carefully test and check your code. If you pass a constant expression or a simple number, the editor will check its bounds to prevent this crash.

Constantes

Use the const keyword before the variable declaration to make that variable immutable, which means that it cannot be modified. All basic types, except samplers can be declared as constants. Accessing and using a constant value is slightly faster than using a uniform. Constants must be initialized at their declaration.

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

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

Constants cannot be modified and additionally cannot have hints, but multiple of them (if they have the same type) can be declared in a single expression e.g

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

Similar to variables, arrays can also be declared with const.

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

arr[0] = 1.0; // invalid

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

Constants can be declared both globally (outside of any function) or locally (inside a function). Global constants are useful when you want to have access to a value throughout your shader that does not need to be modified. Like uniforms, global constants are shared between all shader stages, but they are not accessible outside of the 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 exclusivo bit-wise ^
10 OR inclusivo bit-wise |
11 AND lógico &&
12 (la más baja) OR inclusivo lógico ||

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!

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

You can only use functions that have been defined above (higher in the editor) the function from which you are calling them.

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

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;

You can set uniforms in the editor in the material. Or you can set them through GDScript:

material.set_shader_param("some_value", some_value)

Nota

The first argument to set_shader_param is the name of the uniform in the shader. It must match exactly to the name of the uniform in the shader or else it will not be recognized.

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

It’s important to understand that textures that are supplied as color require hints for proper sRGB->linear conversion (i.e. hint_albedo), as Godot’s 3D engine renders in linear color space.

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 uses different variable types than GLSL does, so when passing variables from GDScript to shaders, Godot converts the type automatically. Below is a table of the corresponding types:

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

Nota

Be careful when setting shader uniforms from GDScript, no error will be thrown if the type does not match. Your shader will just exhibit undefined behaviour.

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

For a list of the functions that are not available in the GLES2 backend, please see the 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 vector a ángulo
vec_type sinh ( vec_type x ) Seno hiperbólico
vec_type cosh ( vec_type x ) Coseno hiperbolico
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
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 ) Ceil
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
vec_type mix ( float a, float b, float c ) Linear Interpolate
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 ) Linear Interpolate (Boolean-Vector Selection)
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 ) Scalar, or vector component being NaN
bvec_type isinf ( vec_type x ) Scalar, or vector component being INF
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 de un vector
float distance ( vec_type a, vec_type b ) Distance between vectors i.e length(a - b)
float dot ( vec_type a, vec_type b ) Producto punto
vec3 cross ( vec3 a, vec3 b ) Producto cruz
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 ) Si dot(Nref, I) < 0 retorna N, de otro modo –N
mat_type matrixCompMult ( mat_type x, mat_type y ) Matrix Component Multiplication
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 ) Any component is true
bool all ( bvec_type x ) All components are true
bvec_type not ( bvec_type x ) Invert boolean vector
ivec2 textureSize ( sampler2D_type s, int lod ) Get the size of a 2D texture
ivec3 textureSize ( sampler2DArray_type s, int lod ) Get the size of a 2D texture array
ivec3 textureSize ( sampler3D s, int lod ) Get the size of a 3D texture
ivec2 textureSize ( samplerCube s, int lod ) Get the size of a Cube texture
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] ) Perform a 2D texture array read
vec4_type texture ( sampler3D_type s, vec3 uv [, float bias] ) Perform a 3D texture read
vec4 texture ( samplerCube s, vec3 uv [, float bias] ) Perform an Cube texture read
vec4_type textureProj ( sampler2D_type s, vec3 uv [, float bias] ) Perform a 2D texture read with projection
vec4_type textureProj ( sampler2D_type s, vec4 uv [, float bias] ) Perform a 2D texture read with projection
vec4_type textureProj ( sampler3D_type s, vec4 uv [, float bias] ) Perform a 3D texture read with projection
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 ) Perform a 2D texture array read at custom mipmap
vec4_type textureLod ( sampler3D_type s, vec3 uv, float lod ) Perform a 3D texture read at custom mipmap
vec4 textureLod ( samplerCube s, vec3 uv, float lod ) Perform a 3D texture read at custom mipmap
vec4_type textureProjLod ( sampler2D_type s, vec3 uv, float lod ) Perform a 2D texture read with projection/lod
vec4_type textureProjLod ( sampler2D_type s, vec4 uv, float lod ) Perform a 2D texture read with projection/lod
vec4_type textureProjLod ( sampler3D_type s, vec4 uv, float lod ) Perform a 3D texture read with projection/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 ) Derivado en x mediante diferenciación local
vec_type dFdy ( vec_type p ) Derivative in y using local differencing
vec_type fwidth ( vec_type p ) Sum of absolute derivative in x and y