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 Floating-point scalar.
vec2 Two-component vector of floating-point values.
vec3 Three-component vector of floating-point values.
vec4 Four-component vector of floating-point values.
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.
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.

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 and varying 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 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

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 bit-wise AND &
9 bit-wise exclusive OR ^
10 bit-wise inclusive OR |
11 logical AND &&
12 (la más baja) logical inclusive OR ||

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

Varying can also be an 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;

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 behavior.

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) Arcsine
vec_type acos (vec_type x) Arccosine
vec_type atan (vec_type y_over_x) Arctangent
vec_type atan (vec_type y, vec_type x) Arctangent to convert vector to angle
vec_type sinh (vec_type x) Hyperbolic sine
vec_type cosh (vec_type x) Hyperbolic cosine
vec_type tanh (vec_type x) Hyperbolic tangent
vec_type asinh (vec_type x) Inverse hyperbolic sine
vec_type acosh (vec_type x) Inverse hyperbolic cosine
vec_type atanh (vec_type x) Inverse hyperbolic tangent
vec_type pow (vec_type x, vec_type y) Power (undefined if x < 0 or if x = 0 and y <= 0)
vec_type exp (vec_type x) Base-e exponential
vec_type exp2 (vec_type x) Base-2 exponential
vec_type log (vec_type x) Natural logarithm
vec_type log2 (vec_type x) Base-2 logarithm
vec_type sqrt (vec_type x) Square root
vec_type inversesqrt (vec_type x) Inverse square root
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) Round to the nearest even number
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) Fractional of x, with i as integer part
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) Clamp to min..max
vec_type mix (float a, float b, float c) Linear interpolate
vec_type mix (vec_type a, vec_type b, float c) Linear interpolate (scalar coefficient)
vec_type mix (vec_type a, vec_type b, vec_type c) Linear interpolate (vector coefficient)
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) Hermite interpolate
vec_type smoothstep (float a, float b, vec_type c) Hermite interpolate
bvec_type isnan (vec_type x) Returns true if scalar or vector component is NaN
bvec_type isinf (vec_type x) Returns true if scalar or vector component is 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) Vector length
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) Cross croduct
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) Matrix component multiplication
mat_type outerProduct (vec_type column, vec_type row) Matrix outer product
mat_type transpose (mat_type m) Transpose matrix
float determinant (mat_type m) Matrix determinant
mat_type inverse (mat_type m) Inverse matrix
bvec_type lessThan (vec_type x, vec_type y) Bool vector comparison on < int/uint/float vectors
bvec_type greaterThan (vec_type x, vec_type y) Bool vector comparison on > int/uint/float vectors
bvec_type lessThanEqual (vec_type x, vec_type y) Bool vector comparison on <= int/uint/float vectors
bvec_type greaterThanEqual (vec_type x, vec_type y) Bool vector comparison on >= int/uint/float vectors
bvec_type equal (vec_type x, vec_type y) Bool vector comparison on == int/uint/float vectors
bvec_type notEqual (vec_type x, vec_type y) Bool vector comparison on != int/uint/float vectors
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 cubemap 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 a cubemap 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) Fetch a single texel using integer coordinates
vec4_type texelFetch (sampler2DArray_type s, ivec3 uv, int lod) Fetch a single texel using integer coordinates
vec4_type texelFetch (sampler3D_type s, ivec3 uv, int lod) Fetch a single texel using integer coordinates
vec_type dFdx (vec_type p) Derivative in x using local differencing
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