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.

Seu primeiro shader 2D

Introdução

Shaders são programas especiais que executam na GPU e são usados para renderizar gráficos. Toda renderização moderna é feita com shaders. Para uma descrição mais detalhada do que são shaders, veja O que são shaders.

Este tutorial focará nos aspectos práticos da escrita de programas de shader, guiando você pelo processo de criação de um shader com funções de vértice e de fragmento. Este tutorial é voltado para iniciantes absolutos em shaders.

Nota

Se você tem experiência escrevendo shaders e está apenas procurando uma visão geral de como os shaders funcionam no Godot, veja a Referência de Sombreamento.

Configurar

Os Shaders de CanvasItem são usados para desenhar todos os objetos 2D no Godot, enquanto os shaders Espaciais (Spatial) são usados para desenhar todos os objetos 3D.

Para usar um shader, ele deve estar anexado dentro de um Material, que por sua vez deve estar anexado a um objeto. Materiais são um tipo de Recurso (Resource). Para desenhar múltiplos objetos com o mesmo material, o material deve estar anexado a cada objeto.

Todos os objetos derivados de um CanvasItem possuem uma propriedade de material. Isso inclui todos os elementos de GUI, Sprite2Ds, TileMapLayers, MeshInstance2Ds etc. Eles também têm a opção de herdar o material do seu pai. Isso pode ser útil se você tiver um grande número de nós e quiser que todos usem o mesmo material.

Para começar, crie um nó Sprite2D. Você pode usar qualquer CanvasItem, desde que ele esteja desenhando na tela (canvas), portanto, para este tutorial, usaremos um Sprite2D, pois é o CanvasItem mais fácil para começar a desenhar.

No Inspector (Inspetor), clique ao lado de "Texture" onde diz "[empty]" e selecione "Load" (Carregar), depois selecione "icon.svg". Para novos projetos, este é o ícone do Godot. Agora você deve ver o ícone na viewport.

Em seguida, olhe mais abaixo no Inspector, sob a seção CanvasItem, clique ao lado de "Material" e selecione "New ShaderMaterial" (Novo ShaderMaterial). Isso cria um novo recurso de Material. Clique na esfera que aparece. Atualmente, o Godot não sabe se você está escrevendo um Shader de CanvasItem ou um Shader Espacial (Spatial) e exibe uma prévia da saída de shaders espaciais. Portanto, o que você está vendo é a saída do Shader Espacial padrão.

Nota

Materiais que herdam do recurso Material, como o StandardMaterial3D e o ParticleProcessMaterial, podem ser convertidos em um ShaderMaterial e suas propriedades existentes serão convertidas em um shader de texto correspondente. Para fazer isso, clique com o botão direito no material na aba FileSystem (Sistema de Arquivos) e escolha Convert to ShaderMaterial (Converter para ShaderMaterial). Você também pode fazer isso clicando com o botão direito em qualquer propriedade que possua uma referência ao material no inspetor.

Clique ao lado de "Shader" e selecione "New Shader" (Novo Shader). Por fim, clique no shader que você acabou de criar e o editor de shader se abrirá. Agora você está pronto para começar a escrever seu primeiro shader.

Seu primeiro shader CanvasItem

No Godot, todos os shaders começam com uma linha especificando que tipo de shader eles são. Ela usa o seguinte formato:

shader_type canvas_item;

Como estamos escrevendo um shader de CanvasItem, especificamos canvas_item na primeira linha. Todo o nosso código ficará abaixo dessa declaração.

Esta linha diz ao motor quais variáveis integradas (built-in) e funcionalidades devem ser fornecidas a você.

No Godot, você pode substituir (override) três funções para controlar como o shader opera: vertex, fragment e light. Este tutorial guiará você na escrita de um shader com as funções vertex e fragment. As funções de luz (light) são significativamente mais complexas do que as funções de vértice e fragmento e, por isso, não serão abordadas aqui.

Sua primeira função de fragmento

A função fragment executa para cada pixel em um Sprite2D e determina qual cor esse pixel deve ter.

Eles estão restritos aos pixels cobertos pelo Sprite2D, o que significa que você não pode usar um para, por exemplo, criar um contorno (outline) ao redor de um Sprite2D.

A função fragment mais básica não faz nada além de atribuir uma única cor a cada pixel.

Fazemos isso escrevendo um vec4 na variável integrada COLOR. vec4 é um atalho para construir um vetor com 4 números. Para mais informações sobre vetores, veja o Tutorial de matemática vetorial. COLOR é tanto uma variável de entrada para a função fragment quanto a saída final dela.

void fragment(){
  COLOR = vec4(0.4, 0.6, 0.9, 1.0);
}
../../../_images/blue-box.png

Parabéns! Você terminou. Você escreveu com sucesso seu primeiro shader no Godot.

Agora vamos deixar as coisas mais complexas.

Existem muitas entradas para a função fragment que você pode usar para calcular a COLOR. A UV é uma delas. As coordenadas UV são especificadas no seu Sprite2D (sem você saber!) e dizem ao shader de onde ler as texturas para cada parte da malha.

Na função fragment, você só pode ler da UV, mas pode usá-la em outras funções ou para atribuir valores diretamente a COLOR.

A UV varia entre 0-1 da esquerda para a direita e de cima para baixo.

../../../_images/iconuv.png
void fragment() {
  COLOR = vec4(UV, 0.5, 1.0);
}
../../../_images/UV.png

Usando a função integrada TEXTURE

A função fragment padrão lê a textura definida no Sprite2D e a exibe.

Quando você quiser ajustar uma cor em um Sprite2D, você pode ajustar a cor da textura manualmente, como no código abaixo.

void fragment(){
  // This shader will result in a blue-tinted icon
  COLOR.b = 1.0;
}

Certos nós, como Sprite2Ds, possuem uma variável de textura dedicada que pode ser acessada no shader usando TEXTURE. Se você quiser usar a textura do Sprite2D para combinar com outras cores, você pode usar a UV com a função texture para acessar essa variável. Use-as para redesenhar o Sprite2D com a textura.

void fragment(){
  COLOR = texture(TEXTURE, UV); // Read from texture again.
  COLOR.b = 1.0; //set blue channel to 1.0
}
../../../_images/blue-tex.png

Entrada uniforme

A entrada Uniform é usada para passar dados para um shader que serão os mesmos em todo o shader.

Você pode usar uniforms definindo-os no topo do seu shader da seguinte forma:

uniform float size;

Para mais informações sobre o uso, veja Linguagem de Sombreamento.

Adicione um uniform para alterar a quantidade de azul no nosso Sprite2D.

uniform float blue = 1.0; // you can assign a default value to uniforms

void fragment(){
  COLOR = texture(TEXTURE, UV); // Read from texture
  COLOR.b = blue;
}

Agora você pode alterar a quantidade de azul no Sprite2D a partir do editor. Olhe de volta no Inspector, abaixo de onde você criou seu shader. Você deverá ver uma seção chamada "Shader Param" (Parâmetros do Shader). Expanda essa seção e verá o uniform que acabou de declarar. Se você alterar o valor no editor, ele substituirá o valor padrão que você forneceu no shader.

Interagindo com shaders por código

Você pode alterar uniforms a partir do código usando a função set_shader_parameter(), que é chamada no recurso de material do nó. Com um nó Sprite2D, o seguinte código pode ser usado para definir o uniform blue.

var blue_value = 1.0
material.set_shader_parameter("blue", blue_value)

Observe que o nome do uniform é uma string. A string deve corresponder exatamente a como está escrita no shader, incluindo a ortografia e letras maiúsculas/minúsculas.

Sua primeira função vertex

Agora que temos uma função fragment, vamos escrever uma função vertex.

Use a função vertex para calcular em que lugar da tela cada vértice deve terminar.

A variável mais importante na função vertex é VERTEX. Inicialmente, ela especifica as coordenadas dos vértices no seu modelo, mas você também grava nela para determinar onde desenhar de fato esses vértices. VERTEX é um vec2 que é inicialmente apresentado no espaço local (ou seja, não relativo à câmera, viewport ou nós pais).

Você pode deslocar os vértices adicionando diretamente a VERTEX.

void vertex() {
  VERTEX += vec2(10.0, 0.0);
}

Combinado com a variável integrada TIME, isso pode ser usado para animações básicas.

void vertex() {
  // Animate Sprite2D moving in big circle around its location
  VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0);
}

Conclusão

Em sua essência, os shaders fazem o que você viu até agora: eles computam VERTEX e COLOR. Cabe a você idealizar estratégias matemáticas mais complexas para atribuir valores a essas variáveis.

Para se inspirar, dê uma olhada em alguns dos tutoriais de shader mais avançados e olhe outros sites como o Shadertoy e o The Book of Shaders.