Introdução aos shaders

Esta página explica o que são shaders e lhe dará uma visão geral de como eles funcionam no Godot. Para uma referência detalhada da linguagem de shading do motor, veja Linguagem de shading.

Shaders são um tipo especial de programa executado em unidades de processamento gráfico (GPUs). Eles foram inicialmente usados para sombrear cenas 3D, mas hoje em dia podem fazer muito mais. Você pode usá-los para controlar como o motor desenha a geometria e os pixels na tela, permitindo obter todos os tipos de efeitos.

Motores de renderização modernos como o Godot desenham tudo com shaders: placas gráficas podem executar milhares de instruções em paralelo, levando a uma incrível velocidade de renderização.

Por causa de sua natureza paralela, porém, os shaders não processam informações da mesma forma que um programa típico faz. O código do shader é executado em cada vértice ou pixel isoladamente. Você também não pode armazenar dados entre quadros. Como resultado, ao trabalhar com shaders, você precisa programar e pensar de forma diferente de outras linguagens de programação.

Suponha que você queira atualizar todos os pixels em uma textura para uma determinada cor. Em GDScript, seu código utilizaria loops for:

for x in range(width):
  for y in range(height):
    set_color(x, y, some_color)

Seu código já é parte de um loop em um shader, então o código correspondente ficaria assim.

void fragment() {
  COLOR = some_color;
}

Nota

A placa gráfica chama a função fragment() uma ou mais vezes para cada pixel a ser desenhado. Mais sobre isso abaixo.

Shaders em Godot

Godot provides a shading language based on the popular OpenGL Shading Language (GLSL) but simplified. The engine handles some of the lower-level initialization work for you, making it easier to write complex shaders.

In Godot, shaders are made up of three main functions: vertex(), fragment(), and light().

  1. The vertex() function runs over all the vertices in the mesh and sets their positions and some other per-vertex variables.

  2. The fragment() function runs for every pixel covered by the mesh. It uses values output by the vertex() function, interpolated between the vertices.

  3. The light() function runs for every pixel and for every light. It takes variables from the fragment() function and from its previous runs.

Aviso

The light() function won't run if the vertex_lighting render mode is enabled, or if Rendering > Quality > Shading > Force Vertex Shading is enabled in the Project Settings. It's enabled by default on mobile platforms.

Tipos de shader

Instead of supplying a general-purpose configuration for all uses (2D, 3D, particles), you must specify the type of shader you're writing. Different types support different render modes, built-in variables, and processing functions.

Em Godot, todos os shaders precisam especificar seu tipo na primeira linha, assim:

shader_type spatial;

Aqui estão os tipos disponíveis:

Modos de renderização

Shaders have optional render modes you can specify on the second line, after the shader type, like so:

shader_type spatial;
render_mode unshaded, cull_disabled;

Render modes alter the way Godot applies the shader. For example, the unshaded mode makes the engine skip the built-in light processor function.

Cada tipo de shader tem diferentes modos de renderização. Veja a referência para cada tipo de shader para uma lista completa de modos de renderização.

Funções de processador

Dependendo do tipo de shader, você pode anular diferentes funções do processador. Para spatial e canvas_item, você tem acesso a vertex(), fragment(), e light(). Para particles, você só tem acesso a vertex().

Processador de vértice

A função de processamento vertex() é chamada uma vez para cada vértice em spatial e canvas_item shaders. Para particles shaders, ela é chamada uma vez para cada partícula.

Each vertex in your world's geometry has properties like a position and color. The function modifies those values and passes them to the fragment function. You can also use it to send extra data to the fragment function using varyings.

Por padrão, Godot transforma suas informações de vértices para você, o que é necessário para projetar a geometria na tela. Você pode utilizar modos de renderização para transformar os dados você mesmo; veja o Spatial shader doc para um exemplo.

Processador de fragmentos

A função de processamento fragment() é utilizada para configurar os parâmetros do material Godot por pixel. Este código roda em cada pixel visível do objeto ou desenhos primitivos. Está disponível apenas em shaders spatial e canvas_item.

O uso padrão da função de fragmento é configurar as propriedades do material usadas para calcular a iluminação. Por exemplo, você definiria valores para ROUGHNESS, RIM ou TRANSMISSION, que informariam à função de luz como as luzes respondem a esse fragmento. Isso torna possível controlar um pipeline de sombreamento complexo sem que o usuário precise escrever muito código. Se você não precisa dessa funcionalidade integrada, pode ignorá-la e escrever sua própria função de processamento de luz, e Godot a otimizará. Por exemplo, se você não escrever um valor para RIM, o Godot não calculará a iluminação do aro. Durante a compilação, Godot verifica se RIM é usado; caso contrário, ele corta todo o código correspondente. Portanto, você não desperdiçará cálculos nos efeitos que não usa.

Processador de luz

O processador light() funciona por pixel também, e funciona uma vez para cada luz que afeta o objeto. Ele não funciona se nenhuma luz afetar o objeto. Existe como uma função chamada dentro do processador fragment() e normalmente opera na configuração das propriedades do material dentro da função fragment().

O processador light() funciona de forma diferente em 2D do que em 3D; para uma descrição de como funciona em cada um deles, veja sua documentação, CanvasItem shaders e Shaders espaciais, respectivamente.