Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Linguagem de shading

Introdução

Godot usa uma linguagem de shading semelhante à GLSL ES 3.0. A maioria dos tipos de dados e funções são suportados e os poucos restantes provavelmente serão adicionados com o tempo.

Se você já está familiarizado com GLSL, o Godot Shader Migration Guide é um recurso que o ajudará na transição de GLSL regular para a linguagem de shaders do Godot.

Tipos de dados

A maioria dos tipos de dados de GLSL ES 3.0 são suportados:

Tipo

Descrição

void

Tipo de dados Void, útil apenas para funções que não retornam nada.

bool

Tipo de dados booleano, só podem conter true ou false.

bvec2

Vetor de dois componentes de booleanos.

bvec3

Vetor de três componentes de booleanos.

bvec4

Vetor de quatro componentes de booleanos.

int

Inteiro escalar com sinal de 32 bits.

ivec2

Vetor de dois componentes de inteiros com sinal.

ivec3

Vetor de três componentes de inteiros com sinal.

ivec4

Vetor de quatro componentes de inteiros com sinal.

uint

Inteiro escalar sem sinal; não pode conter números negativos.

uvec2

Vetor de dois componentes de inteiros sem sinal.

uvec3

Vetor de três componentes de inteiros sem sinal.

uvec4

Vetor de quatro componentes de inteiros sem sinal.

float

Escalar de ponto flutuante de 32 bits.

vec2

Vetor de dois componentes de valores de ponto flutuante.

vec3

Vetor de três componentes de valores de ponto flutuante.

vec4

Vetor de quatro componentes de valores de ponto flutuante.

mat2

Matriz 2x2, em ordem majoritária de coluna (column major order).

mat3

Matriz 3x3, em ordem majoritária de coluna (column major order).

mat4

Matriz 4x4, em ordem majoritária de coluna (column major order).

sampler2D

Tipo sampler para vincular texturas 2D, que são lidas como float.

isampler2D

Tipo sampler para vincular texturas 2D, que são lidas como inteiro com sinal.

usampler2D

Tipo sampler para vincular texturas 2D, que são lidas como inteiro sem sinal.

sampler2DArray

Tipo sampler para vincular arrays de texturas 2D, que são lidas como float.

isampler2DArray

Tipo sampler para vincular arrays de texturas 2D, que são lidas como inteiro com sinal.

usampler2DArray

Tipo sampler para vincular arrays de texturas 2D, que são lidas como inteiro sem sinal.

sampler3D

Tipo sampler para vincular texturas 3D, que são lidas como float.

isampler3D

Tipo sampler para vincular texturas 3D, que são lidas como inteiro com sinal.

usampler3D

Tipo sampler para vincular texturas 3D, que são lidas como inteiro sem sinal.

samplerCube

Tipo sampler para vincular Cubemaps, que são lidas como float.

samplerCubeArray

Tipo sampler para vincular arrays de Cubemaps, que são lidas como float. Suportado apenas em Forward+ e Mobile, não em Compatibility.

samplerExternalOES

Tipo sampler externo. Suportado apenas na plataforma Compatibility/Android.

Esses tipos também podem ser colocados dentro de arrays ou structs, que também são utilizáveis como parâmetros de função ou valores de retorno. Arrays podem ser usados como uniforms, mas structs não.

Aviso

Variáveis locais não são inicializadas com um valor padrão como 0.0. Se você usar uma variável sem atribuí-la primeiro, ela conterá o valor que já estivesse presente naquele local da memória, e falhas visuais imprevisíveis aparecerão. No entanto, uniforms e varyings são inicializados com um valor padrão.

Comentários

A linguagem de sombreamento suporta a mesma sintaxe de comentários usada em C# e C++, utilizando // para comentários de uma única linha e /* */ para comentários de múltiplas linhas:

// Single-line comment.
int a = 2;  // Another single-line comment.

/*
Multi-line comment.
The comment ends when the ending delimiter is found
(here, it's on the line below).
*/
int b = 3;

Adicionalmente, você pode usar comentários de documentação que são exibidos no inspetor ao passar o mouse sobre um parâmetro do shader. Os comentários de documentação atualmente só são suportados quando colocados imediatamente acima de uma declaração de uniform. Esses comentários de documentação suportam apenas a sintaxe de comentário de múltiplas linhas (mesmo que usados em uma única linha) e devem usar dois asteriscos iniciais (/**) em vez de apenas um (/*):

/**
 * This is a documentation comment.
 * These lines will appear in the inspector when hovering the shader parameter
 * named "Something".
 * You can use [b]BBCode[/b] [i]formatting[/i] in the comment.
 */
uniform int something = 1;

/** This is a single-line documentation comment. */
uniform float something_else = 1.0;

Os asteriscos nas linhas seguintes não são obrigatórios, mas são recomendados de acordo com o Guia de estilo de shaders. Esses asteriscos são removidos automaticamente pelo inspetor, de modo que não aparecerão na dica de ferramenta (tooltip).

Conversão

Assim como no GLSL ES 3.0, a conversão implícita (implicit casting) entre escalares e vetores do mesmo tamanho, mas de tipos diferentes, não é permitida. A conversão de tipos de tamanhos diferentes também não é permitida. A conversão deve ser feita explicitamente através de construtores.

Exemplo:

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

As constantes inteiras padrão possuem sinal, portanto, a conversão é sempre necessária para converter para sem sinal:

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

Membros

Os membros escalares individuais dos tipos vetoriais são acessados através dos membros "x", "y", "z" e "w". Alternativamente, usar "r", "g", "b" e "a" também funciona e é equivalente. Use o que melhor se adaptar às suas necessidades.

Para matrizes, use a sintaxe de indexação m[coluna][linha] para acessar cada escalar, ou m[coluna] para acessar um vetor pelo índice da coluna. Por exemplo, para acessar o componente y da translação de uma matriz de transformação mat4 (4ª coluna, 2ª linha), você usa m[3][1] ou m[3].y.

Construindo

A construção de tipos vetoriais deve sempre passar:

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

A construção de tipos de matriz requer vetores da mesma dimensão que a matriz, interpretados como colunas. Você também pode construir uma matriz diagonal usando a sintaxe matx(float). Consequentemente, mat4(1.0) é uma matriz de identidade.

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

Matrizes também podem ser construídas a partir de uma matriz de outra dimensão. Existem duas regras:

1. 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. 1. If a smaller matrix is constructed from a larger matrix, the top, left submatrix of the larger matrix is used.

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

Swizzling

É possível obter qualquer combinação de componentes em qualquer ordem, desde que o resultado seja outro tipo de vetor (ou escalar). Isso é mais fácil de mostrar do que de explicar:

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; // "b" will be vec3(2.0, 1.0, 0.0).
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. "b"'s "blue" component will be "a"'s "red" and vice versa.

Precisão

É possível adicionar modificadores de precisão aos tipos de dados; use-os para uniforms, variáveis, argumentos e 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 (32 bit default)

Usar uma precisão menor para algumas operações pode acelerar a matemática envolvida (ao custo de menos precisão). Isso raramente é necessário na função processadora de vértice (onde a precisão total é necessária na maioria das vezes), mas costuma ser útil no processador de fragmento.

Algumas arquiteturas (principalmente mobile) podem se beneficiar significativamente disso, mas há desvantagens, como o custo adicional de processamento da conversão entre precisões. Consulte a documentação da arquitetura alvo para obter mais informações. Em muitos caso, os drivers mobile causam comportamentos inconsistentes ou inesperados, e é melhor evitar especificar a precisão, a menos que seja necessário.

Arrays

Arrays são recipientes para múltiplas variáveis de um tipo semelhante.

Arrays locais

Arrays locais são declarados em funções. Eles podem usar todos os tipos de dados permitidos, exceto samplers. A declaração do array segue uma sintaxe no estilo C: [const] + [precision] + typename + identifier + [tamanho do array].

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

Eles podem ser inicializados no início desta forma:

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

Você pode declarar múltiplos arrays (mesmo com tamanhos diferentes) em uma única expressão:

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 acessar um elemento do array, use a sintaxe de indexação:

float arr[3];

arr[0] = 1.0; // setter

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

Os arrays também possuem uma função nativa .length() (não deve ser confundida com a função nativa length()). Ela não aceita parâmetros e retornará o tamanho do array.

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

Nota

Se você usar um índice abaixo de 0 ou maior que o tamanho do array - o shader irá travar e interromper a renderização. Para evitar isso, use as funções length(), if ou clamp() para garantir que o índice esteja entre 0 e o tamanho do array. Sempre teste e verifique seu código com cuidado. Se você passar uma expressão constante ou um número, o editor verificará seus limites para evitar esse travamento.

Arrays globais

You can declare arrays in global scope as either const or uniform:

shader_type spatial;

const lowp vec3 v[1] = lowp vec3[1] ( vec3(0, 0, 1) );
uniform lowp vec3 w[1];

void fragment() {
  ALBEDO = v[0] + w[0];
}

Nota

Os arrays globais usam a mesma sintaxe que os arrays locais, exceto por um const ou uniform adicionado à sua declaração. Observe que arrays uniform não podem ter um valor padrão.

Constantes

Use a palavra-chave const antes da declaração da variável para torná-la imutável, o que significa que ela não poderá ser modificada. Todos os tipos básicos, exceto samplers, podem ser declarados como constantes. Acessar e usar um valor constante é ligeiramente mais rápido do que usar um uniform. As constantes devem ser inicializadas em sua declaração.

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.

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

Semelhante às variáveis, os arrays também podem ser declarados com const.

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

arr[0] = 1.0; // invalid

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

As constantes podem ser declaradas tanto globalmente (fora de qualquer função) quanto localmente (dentro de uma função). Constantes globais são úteis quando você deseja ter acesso a um valor em todo o seu shader que não precisa ser modificado. Assim como os uniforms, as constantes globais são compartilhadas entre todos os estágios do shader, mas não são acessíveis fora dele.

shader_type spatial;

const float GOLDEN_RATIO = 1.618033988749894;

As constantes do tipo float devem ser inicializadas usando a notação de ponto . após a parte decimal ou usando a notação científica. O sufixo opcional f também é suportado.

float a = 1.0;
float b = 1.0f; // same, using suffix for clarity
float c = 1e-1; // gives 0.1 by using the scientific notation

As constantes do tipo uint (inteiro sem sinal) devem ter um sufixo u para diferenciá-las dos inteiros com sinal. Alternativamente, isso pode ser feito usando a função de conversão nativa uint(x).

uint a = 1u;
uint b = uint(1);

Structs (Estruturas)

Structs são tipos compostos que podem ser usados para uma melhor abstração do código do shader. Você pode declará-los no escopo global desta forma:

struct PointLight {
    vec3 position;
    vec3 color;
    float intensity;
};

Após a declaração, você pode instanciá-los e inicializá-los assim:

void fragment()
{
    PointLight light;
    light.position = vec3(0.0);
    light.color = vec3(1.0, 0.0, 0.0);
    light.intensity = 0.5;
}

Ou usar o construtor da struct para o mesmo propósito:

PointLight light = PointLight(vec3(0.0), vec3(1.0, 0.0, 0.0), 0.5);

Structs may contain other struct or array, you can also instantiate them as global constant:

shader_type spatial;

...

struct Scene {
    PointLight lights[2];
};

const Scene scene = Scene(PointLight[2](PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0), PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0)));

void fragment()
{
    ALBEDO = scene.lights[0].color;
}

Você também pode passá-los para funções:

shader_type canvas_item;

...

Scene construct_scene(PointLight light1, PointLight light2) {
    return Scene({light1, light2});
}

void fragment()
{
    COLOR.rgb = construct_scene(PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0), PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 1.0), 1.0)).lights[0].color;
}

Operadores

A linguagem de sombreamento do Godot suporta o mesmo conjunto de operadores que o GLSL ES 3.0. Abaixo está a lista deles em ordem de precedência:

Precedência

Classe

Operador

1 (mais alto)

agrupamento entre parênteses

()

2

unário

+, -, !, ~

3

multiplicativo

/, *, %

4

aditivo

+, -

5

deslocamento de bits (bit-wise shift)

<<, >>

6

relacional

<, >, <=, >=

7

igualdade

==, !=

8

Operador bit a bit AND

&

9

OU exclusivo bit a bit (bit-wise exclusive OR)

^

10

OU inclusivo bit a bit (bit-wise inclusive OR)

|

11

AND lógico

&&

12 (mais baixo)

OU inclusivo lógico

||

Nota

A maioria dos operadores que aceitam vetores ou matrizes (multiplicação, divisão, etc.) operam componente por componente (component-wise), o que significa que a função é aplicada ao primeiro valor de cada vetor e depois ao segundo valor de cada vetor, etc. Alguns exemplos:

Operação

Operação Escalar Equivalente

vec3(4, 5, 6) + 2

vec3(4 + 2, 5 + 2, 6 + 2)

vec2(3, 4) * vec2(10, 20)

vec2(3 * 10, 4 * 20)

mat2(vec2(1, 2), vec2(3, 4)) + 10

mat2(vec2(1 + 10, 2 + 10), vec2(3 + 10, 4 + 10))

A Especificação da Linguagem GLSL diz na seção 5.10 Operações de Vetores e Matrizes:

Com poucas exceções, as operações são componente por componente. Normalmente, quando um operador atua em um vetor ou matriz, ele está operando independentemente em cada componente do vetor ou matriz, de maneira componente por componente. [...] As exceções são matriz multiplicada por vetor, vetor multiplicado por matriz e matriz multiplicada por matriz. Estas não operam componente por componente, mas realizam a multiplicação algébrica linear correta.

Controle de fluxo

A linguagem de sombreamento do Godot suporta os tipos mais comuns de controle de fluxo:

// `if`, `else if` and `else`.
if (cond) {

} else if (other_cond) {

} else {

}

// Ternary operator.
// This is an expression that behaves like `if`/`else` and returns the value.
// If `cond` evaluates to `true`, `result` will be `9`.
// Otherwise, `result` will be `5`.
int result = cond ? 9 : 5;

// `switch`.
switch (i) { // `i` should be a signed integer expression.
    case -1:
        break;
    case 0:
        return; // `break` or `return` to avoid running the next `case`.
    case 1: // Fallthrough (no `break` or `return`): will run the next `case`.
    case 2:
        break;
    //...
    default: // Only run if no `case` above matches. Optional.
        break;
}

// `for` loop. Best used when the number of elements to iterate on
// is known in advance.
for (int i = 0; i < 10; i++) {

}

// `while` loop. Best used when the number of elements to iterate on
// is not known in advance.
while (cond) {

}

// `do while`. Like `while`, but always runs at least once even if `cond`
// never evaluates to `true`.
do {

} while (cond);

Tenha em mente que nas GPUs modernas, um loop infinito pode existir e pode congelar sua aplicação (incluindo o editor). O Godot não pode proteger você disso, então tome cuidado para não cometer esse erro!

Além disso, ao comparar valores de ponto flutuante com um número, certifique-se de compará-los com um intervalo (range) em vez de um número exato.

Uma comparação como if (value == 0.3) pode não ser avaliada como true. A matemática de ponto flutuante é frequentemente aproximada e pode desafiar as expectativas. Ela também pode se comportar de maneira diferente dependendo do hardware.

Não faça isso.

float value = 0.1 + 0.2;

// May not evaluate to `true`!
if (value == 0.3) {
    // ...
}

Em vez disso, sempre realize uma comparação de intervalo com um valor epsilon. Quanto maior o número de ponto flutuante (e menos preciso o número de ponto flutuante), maior deve ser o valor epsilon.

const float EPSILON = 0.0001;
if (value >= 0.3 - EPSILON && value <= 0.3 + EPSILON) {
    // ...
}

Veja floating-point-gui.de para mais informações.

Descartando

As funções fragment, light e personalizadas (chamadas a partir de fragment ou light) podem usar a palavra-chave discard. Se usada, o fragmento é descartado e nada é gravado.

Cuidado, pois o discard possui um custo de desempenho quando utilizado, já que impedirá que o pré-passo de profundidade (depth prepass) seja eficaz em quaisquer superfícies que utilizem o shader. Além disso, um pixel descartado ainda precisa ser renderizado no shader de vértice, o que significa que um shader que usa discard em todos os seus pixels ainda é mais custoso de renderizar em comparação com não renderizar nenhum objeto em primeiro lugar.

Funções

É possível definir funções em um shader do Godot. Elas usam a seguinte sintaxe:

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

Você só pode usar funções que tenham sido definidas acima (mais acima no editor) da função a partir da qual você as está chamando. Redefinir uma função que já foi definida acima (ou usar o nome de uma função embutida) causará um erro.

Os argumentos da função podem ter modificadores (qualifiers) especiais:

  • in: Significa que o argumento é apenas para leitura (padrão).

  • out: Significa que o argumento é apenas para escrita.

  • inout: Significa que o argumento é totalmente passado por referência.

  • const: Significa que o argumento é uma constante e não pode ser alterado, podendo ser combinado com o modificador in.

Exemplo abaixo:

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

A sobrecarga de funções é suportada. Você pode definir várias funções com o mesmo nome, mas com argumentos diferentes. Observe que a conversão implícita de tipos em chamadas de funções sobrecarregadas não é permitida, como de int para float (1 para 1.0).

vec3 get_color(int t) {
    return vec3(1, 0, 0); // Red color.
}
vec3 get_color(float t) {
    return vec3(0, 1, 0); // Green color.
}
void fragment() {
    vec3 red = get_color(1);
    vec3 green = get_color(1.0);
}

Variações

Para enviar dados do processador de vértice (vertex) para o de fragmento (fragment) ou de luz (light), são usadas varyings. Elas são definidas para cada vértice primitivo no processador de vértice, e o valor é interpolado para cada pixel no processador de fragmento.

shader_type spatial;

varying vec3 some_color;

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

void fragment() {
    ALBEDO = some_color;
}

void light() {
    DIFFUSE_LIGHT = some_color * 100; // optionally
}

Uma varying também pode ser um 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
}

Também é possível enviar dados do processador de fragmento para o de luz usando a palavra-chave varying. Para fazer isso, você pode atribuir o valor na função fragment e usá-lo posteriormente na função light.

shader_type spatial;

varying vec3 some_light;

void fragment() {
    some_light = ALBEDO * 100.0; // Make a shining light.
}

void light() {
    DIFFUSE_LIGHT = some_light;
}

Observe que uma varying não pode ser atribuída dentro de funções personalizadas ou em uma função do processador de luz, como:

shader_type spatial;

varying float test;

void foo() {
    test = 0.0; // Error.
}

void vertex() {
    test = 0.0;
}

void light() {
    test = 0.0; // Error too.
}

Essa limitação foi introduzida para evitar o uso incorreto antes da inicialização.

Modificadores de interpolação

Certos valores são interpolados durante o pipeline de sombreamento. Você pode modificar como essas interpolações são feitas usando modificadores de interpolação.

shader_type spatial;

varying flat vec3 our_color;

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

void fragment() {
    ALBEDO = our_color;
}

Existem dois modificadores de interpolação possíveis:

Qualificador

Descrição

flat

O valor não é interpolado.

suave

O valor é interpolado de maneira corrigida por perspectiva (perspective-correct). Este é o padrão.

Uniforms

Passar valores para os shaders é possível com uniforms, que são definidos no escopo global do shader, fora das funções. Quando um shader é posteriormente atribuído a um material, os uniforms aparecerão como parâmetros editáveis no inspetor do material. Os uniforms não podem ser escritos de dentro do shader. Qualquer tipo de dados, exceto void, pode ser um uniform.

shader_type spatial;

uniform float some_value;

uniform vec3 colors[3];

Você pode definir uniforms no editor, no inspetor do material. Alternativamente, você pode defini-los via código.

Dicas de uniforme (Uniform hints)

O Godot fornece dicas (hints) de uniform opcionais para fazer o compilador entender para que o uniform é usado e como o editor deve permitir que os usuários o modifiquem.

shader_type spatial;

uniform vec4 color : source_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : source_color = vec4(1.0); // Default values go after the hint.
uniform sampler2D image : source_color;

Os uniforms também podem receber valores padrão:

shader_type spatial;

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

Observe que ao adicionar um valor padrão e uma dica, o valor padrão fica depois da dica.

Lista completa de dicas de uniforms abaixo:

Tipo

Dica

Descrição

vec3, vec4

source_color

Usado como cor.

int

hint_enum("String1", "String2")

Exibe a entrada int como um widget de menu suspenso (dropdown) no editor.

int, float

hint_range(min, max[, step])

Restrito a valores dentro de um intervalo (com min/max/passo).

sampler2D

source_color

Usado como cor albedo.

sampler2D

hint_normal

Usado como normalmap.

sampler2D

hint_default_white

Como valor ou cor albedo, o padrão é branco opaco.

sampler2D

hint_default_black

Como valor ou cor albedo, o padrão é preto opaco.

sampler2D

hint_default_transparent

Como valor ou cor albedo, o padrão é preto transparente.

sampler2D

hint_anisotropy

Como flowmap, o padrão é para a direita.

sampler2D

hint_roughness[_r, _g, _b, _a, _normal, _gray]

Usado para limitador de rugosidade (roughness limiter) na importação (tenta reduzir o aliasing especular). _normal é um mapa de normais que guia o limitador de rugosidade, com a rugosidade aumentando em áreas que possuem detalhes de alta frequência.

sampler2D

filter[_nearest, _linear][_mipmap][_anisotropic]

Ativa a filtragem de textura especificada.

sampler2D

repeat[_enable, _disable]

Ativa a repetição de textura.

sampler2D

hint_screen_texture

A textura é a textura da tela (screen texture).

sampler2D

hint_depth_texture

A textura é a textura de profundidade (depth texture).

sampler2D

hint_normal_roughness_texture

A textura é a textura de rugosidade normal (suportada apenas no Forward+).

Usando hint_enum

Você pode acessar valores int como um widget de menu suspenso legível usando o uniform hint_enum:

uniform int noise_type : hint_enum("OpenSimplex2", "Cellular", "Perlin", "Value") = 0;

Você pode atribuir valores explícitos ao uniform hint_enum usando a sintaxe de dois pontos semelhante ao GDScript:

uniform int character_speed: hint_enum("Slow:30", "Average:60", "Very Fast:200") = 60;

O valor será armazenado como um inteiro, correspondente ao índice da opção selecionada (ou seja, 0, 1 ou 2) ou ao valor atribuído pela sintaxe de dois pontos (ou seja, 30, 60 ou 200). Ao definir o valor com set_shader_parameter(), você deve usar o valor inteiro, não o nome em String.

Usando source_color

Qualquer textura que contenha dados de cor sRGB requer uma dica source_color para ser amostrada corretamente. Isso ocorre porque o Godot renderiza em espaço de cor linear, mas algumas texturas contêm dados de cor sRGB. Se essa dica não for usada, a textura parecerá desbotada.

Texturas de albedo e de cor normalmente devem ter a dica source_color. Texturas de normais, rugosidade (roughness), metálico (metallic) e altura (height) normalmente não precisam da dica source_color.

O uso da dica source_color é obrigatório nos renderizadores Forward+ e Mobile, e em shaders canvas_item quando o HDR 2D está ativado. A dica source_color é opcional para o renderizador Compatibility e para shaders canvas_item se o HDR 2D estiver desativado. No entanto, é recomendado sempre usar a dica source_color, porque ela funciona mesmo se você mudar de renderizador ou desativar o HDR 2D.

Grupos de uniformes

Para agrupar múltiplos uniforms em uma seção no inspetor, você pode usar a palavra-chave group_uniform da seguinte forma:

group_uniforms MyGroup;
uniform sampler2D test;

Você pode fechar o grupo usando:

group_uniforms;

A sintaxe também suporta subgrupos (não é obrigatório declarar o grupo base antes deste):

group_uniforms MyGroup.MySubgroup;

Uniformes globais

Às vezes, você quer modificar um parâmetro em muitos shaders diferentes ao mesmo tempo. Com um uniform regular, isso exige muito trabalho, pois todos esses shaders precisam ser rastreados e o uniform precisa ser definido para cada um deles. Os uniforms globais permitem criar e atualizar uniforms que estarão disponíveis em todos os shaders, em todos os tipos de shader (canvas_item, spatial, particles, sky e fog).

Os uniforms globais são especialmente úteis para efeitos ambientais que afetam muitos objetos em uma cena, como fazer a folhagem se curvar quando o jogador está por perto, ou fazer os objetos se moverem com o vento.

Nota

Os uniforms globais não são a mesma coisa que o escopo global de um shader individual. Enquanto os uniforms regulares são definidos fora das funções do shader e, portanto, estão no escopo global daquele shader, os uniforms globais são globais para todos os shaders de todo o projeto (mas, dentro de cada shader, também ficam no escopo global).

Para criar um uniform global, abra as Configurações do Projeto e vá para a aba Shader Globals. Especifique um nome para o uniform (case-sensitive) e um tipo, depois clique em Add no canto superior direito da janela de diálogo. Você poderá então editar o valor atribuído ao uniform clicando no valor na lista de uniforms:

Adicionando um uniform global na aba Shader Globals das Configurações do Projeto

Adicionando um uniform global na aba Shader Globals das Configurações do Projeto

Depois de criar um uniform global, você pode usá-lo em um shader da seguinte forma:

shader_type canvas_item;

global uniform vec4 my_color;

void fragment() {
    COLOR = my_color.rgb;
}

Observe que o uniform global deve existir nas Configurações do Projeto no momento em que o shader é salvo, caso contrário a compilação falhará. Embora você possa atribuir um valor padrão usando global uniform vec4 my_color = ... no código do shader, ele será ignorado, pois o uniform global sempre deve ser definido nas Configurações do Projeto de qualquer maneira.

Para alterar o valor de um uniform global em tempo de execução, use o método RenderingServer.global_shader_parameter_set em um script:

RenderingServer.global_shader_parameter_set("my_color", Color(0.3, 0.6, 1.0))

A atribuição de valores de uniforms globais pode ser feita quantas vezes for desejado sem impactar o desempenho, pois a definição dos dados não requer sincronização entre a CPU e a GPU.

Você também pode adicionar ou remover uniformes globais em tempo de execução:

RenderingServer.global_shader_parameter_add("my_color", RenderingServer.GLOBAL_VAR_TYPE_COLOR, Color(0.3, 0.6, 1.0))
RenderingServer.global_shader_parameter_remove("my_color")

Adicionar ou remover uniforms globais em tempo de execução possui um custo de desempenho, embora não seja tão acentuado quanto obter os valores de uniforms globais a partir de um script (veja o aviso abaixo).

Aviso

Embora você possa consultar o valor de um uniform global em tempo de execução em um script usando RenderingServer.global_shader_parameter_get("nome_do_uniform"), isso gera uma grande penalidade de desempenho, pois a thread de renderização precisa se sincronizar com a thread que fez a chamada.

Portanto, não é recomendado ler valores de uniforms de shader globais continuamente em um script. Se precisar ler os valores em um script após defini-los, considere a criação de um autoload onde você armazena os valores que precisa consultar ao mesmo tempo em que os define como uniforms globais.

Uniforms por instância (Per-instance uniforms)

Nota

Os uniforms por instância estão disponíveis tanto em shaders canvas_item (2D) quanto spatial (3D).

Às vezes, você quer modificar um parâmetro em cada nó que utiliza o material. Como exemplo, em uma floresta cheia de árvores, você pode querer que cada árvore tenha uma cor ligeiramente diferente que seja editável manualmente. Sem os uniforms por instância, isso exigiria a criação de um material exclusivo para cada árvore (cada um com uma tonalidade ligeiramente diferente). Isso torna o gerenciamento de materiais mais complexo e também gera um custo de desempenho devido à cena exigir mais instâncias de materiais exclusivos. Cores de vértices (vertex colors) também poderiam ser usadas aqui, mas exigiriam a criação de cópias exclusivas da malha (mesh) para cada cor diferente, o que também gera um custo extra de desempenho.

Os uniforms por instância são definidos em cada GeometryInstance3D, e não em cada instância de Material. Leve isso em consideração ao trabalhar com malhas que possuem múltiplos materiais atribuídos a elas ou com configurações de MultiMesh.

shader_type spatial;

// Provide a hint to edit as a color. Optionally, a default value can be provided.
// If no default value is provided, the type's default is used (e.g. opaque black for colors).
instance uniform vec4 my_color : source_color = vec4(1.0, 0.5, 0.0, 1.0);

void fragment() {
    ALBEDO = my_color.rgb;
}

Após salvar o shader, você pode alterar o valor do uniform por instância usando o inspetor:

Definindo o valor de um uniform por instância na seção GeometryInstance3D do inspetor

Definindo o valor de um uniform por instância na seção GeometryInstance3D do inspetor

Os valores de uniforms por instância também podem ser definidos em tempo de execução usando o método set_instance_shader_parameter em um nó que herda de GeometryInstance3D:

$MeshInstance3D.set_instance_shader_parameter("my_color", Color(0.3, 0.6, 1.0))

Ao usar uniforms por instância, existem algumas restrições das quais você deve estar ciente:

  • Os uniforms por instância não suportam texturas ou arrays, apenas tipos escalares e vetoriais comuns. Como alternativa, você pode passar um array de texturas como um uniform regular e, em seguida, passar o índice da textura a ser desenhada usando um uniform por instância.

Nota

Em versões do GLSL anteriores à 4.0 (ou seja, GLSL 3.3 e inferiores), você não pode indexar diretamente um array de texturas usando um uniform por instância, pois os arrays de samplers só podem ser indexados por expressões constantes em tempo de compilação. Isso afeta shaders compilados com o renderizador Compatibility.

Se você for afetado por isso, use a instrução switch para selecionar la textura:

uniform sampler2D texture_array[2];
instance uniform int texture_index;

void fragment() {
    vec4 color;
    switch (texture_index) {
        case 0:
            color = texture(texture_array[0], UV);
            break;
        case 1:
            color = texture(texture_array[1], UV);
            break;
    }

    COLOR = color;
}
  • Existe um limite máximo prático de 16 uniforms de instância por shader.

  • Se sua malha usa múltiplos materiais, os parâmetros do primeiro material de malha encontrado "vencerão" sobre os subsequentes, a menos que tenham o mesmo nome, índice e tipo. Neste caso, todos os parâmetros são afetados corretamente.

  • Se você se deparar com a situação acima, poderá evitar conflitos especificando manualmente o índice (0-15) do uniform de instância usando a dica instance_index:

instance uniform vec4 my_color : source_color, instance_index(5);

Definindo uniformes a partir de código

Você pode definir uniforms a partir do GDScript usando o método set_shader_parameter():

material.set_shader_parameter("some_value", some_value)

material.set_shader_parameter("colors", [Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)])

Nota

O primeiro argumento de set_shader_parameter() é o nome do uniform no shader. Ele deve corresponder exatamente ao nome do uniform no shader, caso contrário não será reconhecido.

O GDScript usa tipos de variáveis diferentes do GLSL, portanto, ao passar variáveis do GDScript para os shaders, o Godot converte o tipo automaticamente. Abaixo está uma tabela dos tipos correspondentes:

Tipo GLSL

Tipo GDScript

Notas

bool

bool

bvec2

int

Inteiro empacotado bit a bit (bitwise packed int) onde o bit 0 (LSB) corresponde a x.

Por exemplo, um bvec2 de (bx, by) poderia ser criado da seguinte maneira:

bvec2_input: int = (int(bx)) | (int(by) << 1)

bvec3

int

Inteiro empacotado bit a bit (bitwise packed int) onde o bit 0 (LSB) corresponde a x.

bvec4

int

Inteiro empacotado bit a bit (bitwise packed int) onde o bit 0 (LSB) corresponde a x.

int

int

ivec2

Vector2i

ivec3

Vector3i

ivec4

Vector4i

uint

int

uvec2

Vector2i

uvec3

Vector3i

uvec4

Vector4i

float

float

vec2

Vector2

vec3

Vector3, Color

Quando Color for usado, será interpretado como (r, g, b).

vec4

Vector4, Color, Rect2, Plane, Quaternion

Quando Color for usado, será interpretado como (r, g, b, a).

Quando Rect2 for usado, será interpretado como (position.x, position.y, size.x, size.y).

Quando Plane for usado, será interpretado como (normal.x, normal.y, normal.z, d).

mat2

Transform2D

mat3

Fundamento

mat4

Projection, Transform3D

Quando um Transform3D for usado, o vetor w é definido como a identidade.

sampler2D

Texture2D

isampler2D

Texture2D

usampler2D

Texture2D

sampler2DArray

Texture2DArray

isampler2DArray

Texture2DArray

usampler2DArray

Texture2DArray

sampler3D

Texture3D

isampler3D

Texture3D

usampler3D

Texture3D

samplerCube

Cubemap

Veja Alterando o tipo de importação para instruções sobre como importar cubemaps para uso no Godot.

samplerCubeArray

CubemapArray

Compatível apenas com Forward+ e Mobile, não com Compatibility.

samplerExternalOES

ExternalTexture

Compatível apenas com a plataforma Compatibility/Android.

Nota

Tenha cuidado ao definir uniforms de shader a partir do GDScript, pois nenhum erro será gerado se o tipo não coincidir. Seu shader simplesmente exibirá um comportamento indefinido. Especificamente, isso inclui definir um int/float do GDScript (64 bits) em um int/float da linguagem de shader do Godot (32 bits). Isso pode levar a consequências indesejadas em casos onde alta precisão é necessária.

Limites de uniformes

Existe um limite para o tamanho total dos uniforms de shader que você pode usar em um único shader. Na maioria das plataformas desktop, esse limite é de 65536 bytes, ou 4096 uniforms vec4. Em plataformas móveis, o limite é tipicamente de 16384 bytes, ou 1024 uniforms vec4. Uniforms vetoriais menores que um vec4, como vec2 ou vec3, são preenchidos (padded) até o tamanho de um vec4. Uniforms escalares, como int ou float, não são preenchidos, e bool é preenchido até o tamanho de um int.

Os arrays contam como o tamanho total de seus conteúdos. Se você precisar de um array uniform que seja maior do que esse limite, considere empacotar os dados em uma textura, já que os conteúdos de uma textura não contam para esse limite, apenas o tamanho do uniform sampler.

Variáveis embutidas

Um grande número de variáveis embutidas está disponível, como UV, COLOR e VERTEX. Quais variáveis estão disponíveis depende do tipo de shader (spatial, canvas_item, particle, etc.) e da função usada (vertex, fragment, light, start, process, sky ou fog). Para uma lista das variáveis embutidas disponíveis, consulte as páginas correspondentes:

Funções embutidas

Um grande número de funções embutidas é suportado, em conformidade com o GLSL ES 3.0. Consulte a página Built-in functions para mais detalhes.