This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this tutorial in English
Shadertoy.com é um site que reúne shaders GL contribuídos por usuários. É um ótimo recurso para encontrar código de shader e inspiração. Neste tutorial, vamos pegar um shader do Shadertoy e fazê-lo rodar no Defold. Presume-se algum entendimento básico de shaders. Se você precisar estudar o assunto, o manual de Shader é um bom ponto de partida.
O shader que usaremos é Star Nest, de Pablo Andrioli (usuário “Kali” no Shadertoy). É um fragment shader procedural puramente matemático, quase uma magia negra, que renderiza um efeito de campo estelar muito interessante.

O shader tem apenas 65 linhas de código GLSL bastante complicado, mas não se preocupe. Vamos tratá-lo como uma caixa-preta que faz seu trabalho com base em algumas entradas simples. Nosso trabalho aqui é modificar o shader para que ele se conecte ao Defold em vez do Shadertoy.
O shader Star Nest é um fragment shader puro, então precisamos apenas de algo para o shader texturizar. Há várias opções: um sprite, um tilemap, uma GUI ou um modelo. Neste tutorial, usaremos um modelo 3D simples. O motivo é que podemos transformar facilmente a renderização do modelo em um efeito de tela cheia—algo que precisamos fazer se quisermos aplicar pós-processamento visual, por exemplo.
Começamos criando uma malha de plano quadrado no Blender (ou em qualquer outro programa de modelagem 3D). Por conveniência, as 4 coordenadas dos vértices estão em -1 e 1 no eixo X, e -1 e 1 no eixo Y. O Blender usa o eixo Z apontando para cima por padrão, então você precisa girar a malha 90° ao redor do eixo X. Você também deve garantir que gerou coordenadas UV corretas para a malha. No Blender, entre no Edit Mode com a malha selecionada e então selecione Mesh ▸ UV unwrap... ▸ Unwrap.
Blender é um software 3D gratuito e open-source que pode ser baixado em blender.org.

quad.gltf encontrado em builtins/assets/meshes.O modelo deve aparecer no editor de cena, mas ele é renderizado todo preto. Isso acontece porque ele ainda não tem material definido:

Crie um novo arquivo de material star-nest.material, um programa de vertex shader star-nest.vp e um programa de fragment shader star-nest.fp:
star-nest.vp.star-nest.fp.view_proj” (de “view projection”).CONSTANT_TYPE_VIEWPROJ.Adicione uma tag “tile” em Tags. Isso faz com que o quad seja incluído no render pass quando sprites e tiles forem desenhados.

Abra o arquivo do programa de vertex shader star-nest.vp. Ele deve conter o código a seguir. Deixe o código como está.
// star-nest.vp
uniform mediump mat4 view_proj;
// positions are in world space
attribute mediump vec4 position;
attribute mediump vec2 texcoord0;
varying mediump vec2 var_texcoord0;
void main()
{
gl_Position = view_proj * vec4(position.xyz, 1.0);
var_texcoord0 = texcoord0;
}
Abra o arquivo do programa de fragment shader star-nest.fp e modifique o código para que a cor do fragmento seja definida com base nas coordenadas X e Y das coordenadas UV (var_texcoord0). Fazemos isso para garantir que o modelo esteja configurado corretamente:
// star-nest.fp
varying mediump vec2 var_texcoord0;
void main()
{
gl_FragColor = vec4(var_texcoord0.xy, 0.0, 1.0);
}
Agora o editor deve renderizar o modelo com o novo shader, e podemos ver claramente se as coordenadas UV estão corretas: o canto inferior esquerdo deve ter cor preta (0, 0, 0), o canto superior esquerdo deve ter cor verde (0, 1, 0), o canto superior direito deve ter cor amarela (1, 1, 0) e o canto inferior direito deve ter cor vermelha (1, 0, 0):

Agora tudo está pronto para começar a trabalhar no código real do shader. Primeiro, vamos dar uma olhada no código original. Ele consiste em algumas seções:

As linhas 5–18 definem várias constantes. Podemos deixá-las como estão.
As linhas 21 e 63 contêm as coordenadas X e Y de textura em screen space do fragmento de entrada (in vec2 fragCoord) e a cor do fragmento de saída (out vec4 fragColor).
No Defold, as coordenadas de textura de entrada são passadas do vertex shader como coordenadas UV (no intervalo 0–1) por meio de uma variável varying var_texcoord0. A cor do fragmento de saída é definida na variável integrada gl_FragColor.
As linhas 23–27 configuram as dimensões da textura, bem como a direção do movimento e o tempo escalado. A resolução do viewport/textura é passada ao shader como uniform vec3 iResolution. O shader calcula coordenadas no estilo UV com a proporção correta a partir das coordenadas do fragmento e da resolução. Também é feito algum deslocamento da resolução para obter um enquadramento melhor.
A versão para Defold precisa alterar esses cálculos para usar as coordenadas UV de var_texcoord0.
O tempo também é configurado aqui. Ele é passado ao shader como uniform float iGlobalTime. Atualmente, o Defold não suporta uniforms float, então precisamos fornecer o tempo por meio de um vec4.
As linhas 29–39 configuram a rotação da renderização volumétrica, com a posição do mouse afetando a rotação. As coordenadas do mouse são passadas ao shader como uniform vec4 iMouse.
Neste tutorial, vamos ignorar a entrada do mouse.
As linhas 41–62 são o núcleo do shader. Podemos deixar esse código como está.
Percorrer as seções acima e fazer as alterações necessárias resulta no código de shader a seguir. Ele foi limpo um pouco para melhorar a legibilidade. As diferenças entre as versões do Defold e do Shadertoy são anotadas:
// Star Nest by Pablo Román Andrioli
// This content is under the MIT License.
#define iterations 17
#define formuparam 0.53
#define volsteps 20
#define stepsize 0.1
#define zoom 0.800
#define tile 0.850
#define speed 0.010
#define brightness 0.0015
#define darkmatter 0.300
#define distfading 0.730
#define saturation 0.850
varying mediump vec2 var_texcoord0; // <1>
void main() // <2>
{
// get coords and direction
vec2 res = vec2(1.0, 1.0); // <3>
vec2 uv = var_texcoord0.xy * res.xy - 0.5;
vec3 dir = vec3(uv * zoom, 1.0);
float time = 0.0; // <4>
float a1=0.5; // <5>
float a2=0.8;
mat2 rot1=mat2(cos(a1),sin(a1),-sin(a1),cos(a1));
mat2 rot2=mat2(cos(a2),sin(a2),-sin(a2),cos(a2));
dir.xz*=rot1;
dir.xy*=rot2;
vec3 from = vec3(1.0, 0.5, 0.5);
from += vec3(time * 2.0, time, -2.0);
from.xz *= rot1;
from.xy *= rot2;
//volumetric rendering
float s = 0.1, fade = 1.0;
vec3 v = vec3(0.0);
for(int r = 0; r < volsteps; r++) {
vec3 p = from + s * dir * 0.5;
// tiling fold
p = abs(vec3(tile) - mod(p, vec3(tile * 2.0)));
float pa, a = pa = 0.0;
for (int i=0; i < iterations; i++) {
// the magic formula
p = abs(p) / dot(p, p) - formuparam;
// absolute sum of average change
a += abs(length(p) - pa);
pa = length(p);
}
//dark matter
float dm = max(0.0, darkmatter - a * a * 0.001);
a *= a * a;
// dark matter, don't render near
if(r > 6) fade *= 1.0 - dm;
v += fade;
// coloring based on distance
v += vec3(s, s * s, s * s * s * s) * a * brightness * fade;
fade *= distfading;
s += stepsize;
}
// color adjust
v = mix(vec3(length(v)), v, saturation);
gl_FragColor = vec4(v * 0.01, 1.0); // <6>
}
var_texcoord0 com as coordenadas UV. Precisamos declará-la.void mainImage(out vec4 fragColor, in vec2 fragCoord). No Defold, não recebemos parâmetros em main(). Então, em vez disso, lemos a varying var_texcoord0 e escrevemos em gl_FragColor.vec2 = vec2(1.0, 1.0);. Com um modelo retangular de tamanho 1280⨉720, em vez disso definimos vec2 res = vec2(1.78, 1.0); e multiplicamos as coordenadas uv por isso para obter a proporção correta.time é definido como zero. Adicionaremos tempo na próxima etapa.iMouse. Observe que ainda usamos os cálculos de rotação para reduzir a simetria visual na renderização volumétrica.Salve o programa de fragment shader. O modelo agora deve estar bem texturizado com um campo estelar no editor Scene:

A peça final do quebra-cabeça é introduzir tempo para fazer as estrelas se moverem. Para passar um valor de tempo ao shader, precisamos usar uma constante de shader, uma uniform. Para configurar uma nova constante:
CONSTANT_TYPE_USER. Deixe os componentes x, y, z e w em 0.
Agora precisamos modificar o código do shader para declarar e usar a nova constante:
...
varying mediump vec2 var_texcoord0;
uniform lowp vec4 time; // <1>
void main()
{
//get coords and direction
vec2 res = vec2(2.0, 1.0);
vec2 uv = var_texcoord0.xy * res.xy - 0.5;
vec3 dir = vec3(uv * zoom, 1.0);
float time = time.x * speed + 0.25; // <2>
...
vec4 com o nome “time”. Deve bastar mantê-la em lowp (Low precision).x da uniform de tempo e use-o para calcular um valor de tempo.O passo final é alimentar o shader com um valor de tempo:
star-nest.script.function init(self)
self.t = 0 -- <1>
end
function update(self, dt)
self.t = self.t + dt -- <2>
go.set("#model", "time", vmath.vector4(self.t, 0, 0, 0)) -- <3>
end
t no componente de script (self) e inicialize-o com 0.self.t pelo número de segundos que se passou desde o último frame. Esse valor está disponível pelo parâmetro dt (delta time) e é 1/60 (update() é chamado 60 vezes por segundo).vector4, então usamos o componente x para o valor de tempo.Por fim, adicione star-nest.script como componente de script ao objeto de jogo “star-nest”:

E é isso! Terminamos!
Um exercício divertido de continuação é adicionar ao shader a entrada original de movimento do mouse. Deve ser bem direto se você entender como lidar com entrada.
Boas criações com Defold!