O Shader de Vento
O shader em questão é uma adaptação de um shader de vento original disponível em Godot Simple Wind Shader 2D . Ele foi modificado para ajustar o comportamento dos assets, permitindo movimentos variados e mais naturais. O objetivo principal é distorcer a geometria dos objetos para simular a influência do vento, criando um ambiente mais dinâmico e realista.
shader_type canvas_item;
render_mode blend_mix;
uniform float speed = 1.0;
uniform float minStrength : hint_range(0.0, 1.0) = 0.05;
uniform float maxStrength : hint_range(0.0, 1.0) = 0.01;
uniform float strengthScale = 100.0;
uniform float interval = 3.5;
uniform float detail = 1.0;
uniform float distortion : hint_range(0.0, 1.0);
uniform float heightOffset : hint_range(0.0, 1.0);
uniform float offset = 0;
float getWind(vec2 vertex, vec2 uv, float time){
float diff = pow(maxStrength - minStrength, 2.0);
float strength = clamp(minStrength + diff + sin(time / interval) * diff, minStrength, maxStrength) * strengthScale;
float wind = (sin(time) + cos(time * detail)) * strength * max(0.0, (1.0 - uv.y) - heightOffset);
return wind;
}
void vertex() {
vec4 pos = WORLD_MATRIX * vec4(0.0, 0.0, 0.0, 1.0);
float time = TIME * speed + offset;
VERTEX.x += getWind(VERTEX.xy, UV, time)
}
Análise Detalhada do Código
First-things-first!
shader_type canvas_item;
render_mode blend_mix;
Aqui, estamos definindo o tipo de shader como canvas_item
, o que significa que ele será aplicado a elementos 2D, como sprites ou UI. O render_mode blend_mix
especifica como as cores serão misturadas. Imagine que estamos escolhendo o tipo de pincel e tinta para pintar nossa tela.
Configurações do Vento (Uniformes)
A variável wave
é inicialmente definida como a posição do horizonte (horizon), representando a linha de base da água. Adicionamos uma componente senoide que varia ao longo do eixo X e do tempo para simular ondas que se movem horizontalmente:
uniform float speed = 1.0;
uniform float minStrength : hint_range(0.0, 1.0) = 0.05;
uniform float maxStrength : hint_range(0.0, 1.0) = 0.01;
uniform float strengthScale = 100.0;
uniform float interval = 3.5;
uniform float detail = 1.0;
uniform float distortion : hint_range(0.0, 1.0);
uniform float heightOffset : hint_range(0.0, 1.0);
A variável speed
controla a velocidade do vento. Imagine que você pode acelerar ou desacelerar o tempo, fazendo o vento soprar mais rápido ou mais devagar. As variáveis minStrength e maxStrength representam a força mínima e máxima do vento, permitindo que ele varie entre uma brisa suave e uma rajada mais forte, como acontece na natureza.
O strengthScale
é um fator que amplifica a força calculada do vento, ajustando a amplitude do movimento dos objetos. Pense nisso como ajustar o volume de um som: você pode aumentar ou diminuir a intensidade do som.
O interval
define o tempo entre as mudanças de força do vento. É como definir a duração de cada nota em uma música, controlando o ritmo das variações do vento. A variável detail influencia a complexidade do movimento, adicionando mais "ondas" ou variações ao efeito, semelhante a adicionar mais notas a uma melodia para torná-la mais rica.
A distortion
ajusta o quanto o objeto será distorcido pelo vento. Imagine uma folha de papel versus uma placa de metal; a folha se dobra facilmente, enquanto a placa permanece rígida. O heightOffset
define a altura a partir da qual o vento começa a afetar o objeto, permitindo que a base permaneça estática enquanto o topo balança, como o tronco de uma árvore que é firme, mas os galhos se movem com o vento.
Offset Personalizado para Movimentos Únicos
uniform float offset = 0;
O offset
é um toque especial que permite que cada objeto tenha um movimento único. Se você imaginar uma fila de árvores, não queremos que todas se movam exatamente da mesma forma. Ao adicionar um valor diferente de offset a cada uma, introduzimos variações no movimento, tornando a cena mais natural e viva.
A Função getWind
Esta função é o coração do shader, onde calculamos a influência do vento em cada ponto do objeto. Vamos destrinchá-la:
float getWind(vec2 vertex, vec2 uv, float time){
float diff = pow(maxStrength - minStrength, 2.0);
float strength = clamp(minStrength + diff + sin(time / interval) * diff, minStrength, maxStrength) * strengthScale;
float wind = (sin(time) + cos(time * detail)) * strength * max(0.0, (1.0 - uv.y) - heightOffset);
return wind;
}
Primeiro, calculamos a diferença quadrática entre a força máxima e mínima do vento com float diff = pow(maxStrength - minStrength, 2.0);
. Isso nos dá uma base para variar a força do vento de forma suave.
Em seguida, determinamos a força atual do vento:
float strength = clamp(minStrength + diff + sin(time / interval) * diff, minStrength, maxStrength) * strengthScale;
Aqui, usamos uma função seno para criar uma oscilação suave ao longo do tempo. A divisão time / interval
controla a frequência dessa oscilação, como ajustar o ritmo de uma música. O resultado é então limitado entre minStrength
e maxStrength
com clamp
, garantindo que a força do vento permaneça dentro dos limites desejados. Multiplicamos tudo por strengthScale
para ajustar a intensidade geral.
Agora, calculamos o efeito do vento no vértice:
float wind = (sin(time) + cos(time * detail)) * strength * max(0.0, (1.0 - uv.y) - heightOffset);
A combinação de sin(time)
e cos(time * detail)
cria um padrão complexo de movimento. O resultado é multiplicado pela força calculada e por uma máscara vertical max(0.0, (1.0 - uv.y) - heightOffset)
, que faz com que o vento afete mais as partes superiores do objeto. Imagine uma grama alta, onde a base está firme no chão e as pontas balançam livremente.
A Função vertex
Esta função aplica o deslocamento calculado aos vértices do objeto, dando vida ao movimento do vento:
void vertex() {
vec4 pos = WORLD_MATRIX * vec4(0.0, 0.0, 0.0, 1.0);
float time = TIME * speed + offset;
VERTEX.x += getWind(VERTEX.xy, UV, time);
}
Calculamos o tempo ajustado com float time = TIME * speed + offset;
, onde TIME
é o tempo global do jogo. Multiplicamos por speed para controlar a velocidade e adicionamos offset
para introduzir variações entre diferentes objetos.
Finalmente, aplicamos o deslocamento ao vértice com VERTEX.x += getWind(VERTEX.xy, UV, time);
. Isso move cada vértice ao longo do eixo X, criando um movimento lateral que simula o vento soprando. É como se cada ponto do objeto estivesse sendo empurrado pelo vento, mas de forma ligeiramente diferente, resultando em um efeito ondulante e natural.
Considerações Finais
A capacidade de usar exemplos do dia a dia, como o movimento das árvores ou bandeiras ao vento, nos ajuda a visualizar o que o código está fazendo. Isso não apenas facilita a compreensão, mas também nos permite ser mais criativos ao adaptar o shader para diferentes necessidades.