SHADERS, POR ONDE COMEÇAR

1

Stats

186 visits, 211 views

Tools

Translations

This tutorial hasn't been translated.

License

This tutorial is licensed under CC BY 4.0. Please refer to the license text if you wish to reuse, share or remix the content contained within this tutorial.

Published on 10 Mar, 2024.

Encontrar conteúdo específico sobre shaders no Construct 3 pode ser um desafio, especialmente porque a comunidade de desenvolvedores é menor em comparação com outras plataformas. A falta de material que ensine como criar seus próprios shaders é o principal obstáculo para quem deseja aprender. No entanto, após algumas pesquisas, encontrei um pequeno guia escrito pelo Skymen que relata ter aprendido a criar e adicionar shaders no Construct 3.

De acordo com as informações fornecidas por ele, o primeiro passo é entender o funcionamento da linguagem GLSL (OpenGL Shading Language). O Skymen disponibilizou um material interessante que ensina os conceitos básicos sobre shaders na plataforma Shadertoy. Após conferir o material original em inglês, tive a ideia de traduzi-lo para o português, com a esperança de que isso facilitasse o aprendizado para outros desenvolvedores.

A tradução inclui todos os comentários dentro do prompt no Shadertoy, o que permitirá uma compreensão mais clara dos tutoriais. Para acessar a tradução, basta copiar todo o código e colá-lo dentro do prompt na plataforma Shadertoy. Em seguida, compile o código e siga as instruções fornecidas nos tutoriais.

Cole o código abaixo no prompt na plataforma: shadertoy.com/new

  • Código traduzido

    /*

    por Uğur Güney. 8 de março de 2014.

    tradução em português feito por Gold Skull. 09 de março de 2024

    Olá! Comecei a aprender GLSL há um mês.

    A aceleração obtida ao usar a GPU para renderizar gráficos

    em tempo real me surpreendeu. Se você deseja aprender como

    escrever shaders, este tutorial escrito por um iniciante pode

    ser um bom ponto de partida.

    Por favor, corrija meus erros de codificação e gramática. :-)

    */

    // Escolha o tutorial alterando o número e compilando o shader novamente.

    #define TUTORIAL 0

    /* LISTA DE TUTORIAIS

    1 VOID. TELA EM BRANCO.

    2 COR SÓLIDA

    3 VETORES GLSL

    4 MODELO DE COR RGB E COMPONENTES DE VETORES

    5 O SISTEMA DE COORDENADAS

    6 RESOLUÇÃO, TAMANHO DO QUADRO

    7 TRANSFORMAÇÃO DE COORDENADAS

    8 LINHAS HORIZONTAIS E VERTICAIS

    9 VISUALIZANDO O SISTEMA DE COORDENADAS

    10 MOVENDO O CENTRO DE COORDENADAS PARA O CENTRO DO QUADRO

    11 FAZENDO A RAZÃO DE ASPECTO DO SISTEMA DE COORDENADAS 1.0

    12 DISCO

    13 FUNÇÕES

    14 FUNÇÕES EMBUTIDAS: STEP

    15 FUNÇÕES EMBUTIDAS: CLAMP

    16 FUNÇÕES EMBUTIDAS: SMOOTHSTEP

    17 FUNÇÕES EMBUTIDAS: MIX

    18 ANTI-ALIASING COM SMOOTHSTEP

    19 TRAÇADO DE FUNÇÕES

    20 ADIÇÃO E SUBTRAÇÃO DE CORES

    21 TRANSFORMAÇÕES DE COORDENADAS: ROTAÇÃO

    22 TRANSFORMAÇÕES DE COORDENADAS: ESCALA

    23 TRANSFORMAÇÕES DE COORDENADAS SUCESSIVAS

    24 TEMPO, MOVIMENTO E ANIMAÇÃO

    25 EFEITO PLASMA

    26 TEXTURAS

    27 ENTRADA DO MOUSE

    28 ALEATORIEDADE

    */

    #define PI 3.14159265359

    #define TWOPI 6.28318530718

    #if TUTORIAL == 1

    // VOID. TELA EM BRANCO.

    //

    // A função “main” é chamada várias vezes por segundo para produzir

    // o efeito do shader.

    // O sistema tem como objetivo gerar uma velocidade de 60 quadros por segundo (FPS).

    // No entanto, se o script GLSL for computacionalmente pesado, a taxa de quadros

    // cai. (Você pode verificar a taxa de quadros na barra de informações abaixo

    // da tela.)

    //

    // Como não estamos fazendo nada na função

    // este shader resultará em uma tela preta.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    }

    #elif TUTORIAL == 2

    // COR SÓLIDA

    //

    // “fragColor” é a variável de saída do shader.

    // Seu valor determina a imagem na tela.

    // Este shader define seu valor para ser a cor amarela.

    //

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    fragColor = vec4(1.0, 1.0, 0.0 ,1.0);

    }

    #elif TUTORIAL == 3

    // VETORES GLSL

    //

    // A variável “fragColor” deve ser atribuída a um objeto vec4, que é composto

    // por quatro números entre 0 e 1.

    // Os três primeiros números determinam a cor, e o quarto número

    // determina a opacidade.

    // (Por enquanto, alterar o valor de transparência não terá efeito.)

    // Um objeto de dados “vec4” pode ser construído fornecendo 4 argumentos do tipo float,

    // ou um vec3 e um argumento do tipo float.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    // Aqui estamos separando as partes de cor e transparência

    // do vec4 que representa os pixels.

    vec3 color = vec3(0.0, 1.0, 1.0);

    float alpha = 1.0;

    vec4 pixel = vec4(color, alpha);

    fragColor = pixel;

    }

    #elif TUTORIAL == 4

    // MODELO DE COR RGB E COMPONENTES DE VETORES

    //

    // Após serem inicializados, os componentes dos vetores podem ser acessados usando

    // a notação de ponto (“.”).

    //

    // RGB: en.wikipedia.org/wiki/RGB_color_model

    // Uma cor é representada por três números (aqui no intervalo de [0.0, 1.0])

    // O modelo pressupõe a adição de luzes vermelhas, verdes e azuis puras

    // com intensidades específicas

    //

    // Se você, assim como eu, não tem habilidades de design e está tendo dificuldade

    // em escolher um conjunto de cores agradável e coerente

    // você pode usar um desses sites para escolher paletas de cores.

    // Neles, você pode explorar diferentes conjuntos de cores.

    // kuler.adobe.com/create/color-wheel

    // colourlovers.com/palettes

    // colourlovers.com/colors

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    // Brinque com esses números:

    float redAmount = 0.6; // quantidade de vermelho

    float greenAmount = 0.2; // quantidade de verde

    float blueAmount = 0.9; // quantidade de azul

    vec3 color = vec3(0.0);

    // Aqui, inserimos apenas um argumento. É uma terceira maneira

    // de construir vetores.

    // “vec3(x)” é equivalente a vec3(x, x, x);

    // Este vetor é inicializado como

    // color.x = 0.0, color.y = 0.0; color.z = 0.0;

    color.x = redAmount;

    color.y = greenAmount;

    color.z = blueAmount;

    float alpha = 1.0;

    vec4 pixel = vec4(color, alpha);

    fragColor = pixel;

    }

    #elif TUTORIAL == 5

    // O SISTEMA DE COORDENADAS

    //

    // "fragCoord", "fragment coordinate" é uma variável de entrada.

    //

    // Isso nos diz em qual pixel estamos na tela. O centro das coordenadas

    // é o canto inferior esquerdo, e os valores das coordenadas aumentam em direção

    // à direita e para cima.

    //

    // A função principal é executada para cada pixel na tela. Em

    // cada chamada, o ‘gl_FracCoord’ possui as coordenadas do pixel

    // correspondente.

    //

    // GPUs têm muitos núcleos, portanto, as chamadas de função para diferentes pixels

    // podem ser calculadas em paralelo ao mesmo tempo.

    // Isso permite velocidades mais altas do que o cálculo das cores dos pixels um

    // por um em série na CPU. No entanto, também impõe uma restrição importante:

    // O valor de um pixel não pode depender do valor de outro pixel.

    // (os cálculos são feitos em paralelo e é impossível saber qual

    // deles terminará antes do outro)

    // O resultado de um pixel pode depender apenas das coordenadas do pixel (e

    // de algumas outras variáveis de entrada)

    // Esta é a diferença mais importante da programação de shaders.

    // Voltaremos a este ponto repetidamente

    //

    // Vamos desenhar algo que não seja uma cor sólida.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    // escolha duas cores

    vec3 color1 = vec3(0.886, 0.576, 0.898);

    vec3 color2 = vec3(0.537, 0.741, 0.408);

    vec3 pixel;

    // se a coordenada x for maior que 100, então plote a color1

    // caso contrário, plote a color2

    float widthOfStrip = 100.0;

    if( fragCoord.x > widthOfStrip ) {

    pixel = color2;

    } else {

    pixel = color1;

    }

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 6

    // RESOLUÇÃO, TAMANHO DO QUADRO

    //

    // Se você redimensionar o seu navegador ou entrar no modo de tela cheia e voltar

    // você verá que a proporção da largura da primeira cor para a

    // segunda cor muda com o tamanho da tela.

    // Isso ocorre porque definimos a largura da faixa em número absoluto de

    // pixels, em vez de como uma proporção da largura e altura da tela.

    //

    // Digamos que queiramos pintar as metades esquerda e direita com cores diferentes.

    // Sem saber o número de pixels na horizontal, não podemos criar

    // um shader que funcione em todos os tamanhos de quadro.

    //

    // Como podemos obter o tamanho da tela (largura e altura) em termos de

    // número de pixels? Essa informação é fornecida na variável "iResolution".

    // "iResolution.x" representa a largura do quadro, e

    // "iResolution.y" representa a altura do quadro

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec3 color1 = vec3(0.741, 0.635, 0.471);

    vec3 color2 = vec3(0.192, 0.329, 0.439);

    vec3 pixel;

    // uma forma simplificada para expressar a condicional “if” diz o seguinte:

    // “se a coordenada x de um pixel for maior que a metade da

    // largura da tela, então use a color1, caso contrário, use

    // color2."

    pixel = ( fragCoord.x > iResolution.x / 2.0 ) ? color1 : color2;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 7

    // TRANSFORMAÇÃO DE COORDENADAS

    //

    // Em vez de trabalhar com coordenadas de tela, usar nosso próprio

    // sistema de coordenadas é mais conveniente na maioria das vezes.

    //

    // Aqui, criaremos e utilizaremos um novo sistema de coordenadas chamado “r”, em vez

    // das coordenadas absolutas da tela, chamadas de “fragCoord”. No sistema “r”

    // as coordenadas x e y variam de 0 a 1. Para o eixo x, 0 representa o lado

    // esquerdo e 1 o lado direito. Para o eixo y, 0 representa a parte inferior e 1 a

    // parte superior.

    //

    // Usando o sistema “r”, vamos dividir a tela em 3 partes.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = vec2(fragCoord.x / iResolution.x,

    fragCoord.y / iResolution.y);

    // r é um vec2. Seu primeiro componente é a coordenada x do pixel dividida pela

    // largura do quadro. E o segundo componente é a coordenada y do pixel

    // dividida pela altura do quadro.

    //

    // Por exemplo, no meu laptop, o tamanho completo do quadro da tela é

    // 1440 x 900. Portanto, iResolution é (1440.0, 900.0).

    // A função principal deve ser executada 1.296.000 vezes para

    // gerar um quadro.

    // fragCoord.x terá valores entre 0 e 1439, e

    // fragCoord.y terá valores entre 0 e 899, enquanto

    // r.x e r.y terão valores entre [0,1].

    vec3 color1 = vec3(0.841, 0.582, 0.594);

    vec3 color2 = vec3(0.884, 0.850, 0.648);

    vec3 color3 = vec3(0.348, 0.555, 0.641);

    vec3 pixel;

    // uma forma simplificada para expressar a condicional “if” diz o seguinte:

    // "Se a coordenada x de um pixel for maior que a metade da

    // largura da tela, use color1; caso contrário, use

    // color2."

    if( r.x < 1.0/3.0) {

    pixel = color1;

    } else if( r.x < 2.0/3.0 ) {

    pixel = color2;

    } else {

    pixel = color3;

    }

    // pixel = ( r.x < 1.0/3.0 ) ? color1 : (r.x<2.0/3.0) ? color2: color3;

    // mesmo código, linha única.

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 8

    // LINHAS HORIZONTAIS E VERTICAIS

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = vec2( fragCoord.xy / iResolution.xy );

    // Versão mais curta da mesma transformação de coordenadas.

    // Por exemplo, "aVector.xy" é um novo vec2 formado pelos dois primeiros

    // componentes de "aVector".

    // E, quando o operador de divisão é aplicado entre vetores,

    // cada componente do primeiro vetor é dividido pelo componente correspondente

    // do segundo vetor.

    // Portanto, a primeira linha deste tutorial é igual à primeira linha

    // do tutorial anterior.

    vec3 backgroundColor = vec3(1.0);

    vec3 color1 = vec3(0.216, 0.471, 0.698);

    vec3 color2 = vec3(1.00, 0.329, 0.298);

    vec3 color3 = vec3(0.867, 0.910, 0.247);

    // Comece definindo a cor de fundo. Se o valor do pixel

    // não for substituído posteriormente, essa cor será exibida.

    vec3 pixel = backgroundColor;

    // se a coordenada x do pixel atual estiver entre esses valores,

    // então coloque a cor 1.

    // A diferença entre 0,55 e 0,54 determina

    // a largura da linha.

    float leftCoord = 0.54;

    float rightCoord = 0.55;

    if( r.x < rightCoord && r.x > leftCoord ) pixel = color1;

    // uma maneira diferente de expressar uma linha vertical

    // em termos de sua coordenada x e sua espessura:

    float lineCoordinate = 0.4;

    float lineThickness = 0.003;

    if(abs(r.x - lineCoordinate) < lineThickness) pixel = color2;

    // uma linha horizontal

    if(abs(r.y - 0.6)<0.01) pixel = color3;

    // observe como a terceira linha passa por cima das duas primeiras linhas.

    // porque é a última que define o valor do "pixel".

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 9

    // VISUALIZANDO O SISTEMA DE COORDENADAS

    //

    // Vamos usar um loop "for" e linhas horizontais e verticais para desenhar

    // uma grade no centro das coordenadas.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = vec2( fragCoord.xy / iResolution.xy );

    vec3 backgroundColor = vec3(1.0);

    vec3 axesColor = vec3(0.0, 0.0, 1.0);

    vec3 gridColor = vec3(0.5);

    // comece definindo a cor de fundo. Se o valor do pixel

    // não for sobrescrito posteriormente, essa cor será exibida.

    vec3 pixel = backgroundColor;

    // Desenha as linhas da grade

    // Usamos "const" porque as variáveis de loop só podem ser manipuladas

    // por expressões constantes.

    const float tickWidth = 0.1;

    for(float i=0.0; i<1.0; i+=tickWidth) {

    // "i" é a coordenada da linha.

    if(abs(r.x - i)<0.002) pixel = gridColor;

    if(abs(r.y - i)<0.002) pixel = gridColor;

    }

    // Desenha os eixos

    if( abs(r.x)<0.005 ) pixel = axesColor;

    if( abs(r.y)<0.006 ) pixel = axesColor;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 10

    // MOVENDO O CENTRO DE COORDENADAS PARA O CENTRO DO QUADRO

    //

    // Em vez de mapear a região [0, iResolution.x]x[0, iResolution.y] para

    // [0,1]x[0,1], vamos mapeá-la para [-1,1]x[-1,1]. Dessa forma, a coordenada

    // (0,0) não estará no canto inferior esquerdo da tela, mas sim no

    // centro da tela.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = vec2( fragCoord.xy - 0.5*iResolution.xy );

    // [0, iResolution.x] -> [-0.5*iResolution.x, 0.5*iResolution.x]

    // [0, iResolution.y] -> [-0.5*iResolution.y, 0.5*iResolution.y]

    r = 2.0 * r.xy / iResolution.xy;

    // [-0.5*iResolution.x, 0.5*iResolution.x] -> [-1.0, 1.0]

    vec3 backgroundColor = vec3(1.0);

    vec3 axesColor = vec3(0.0, 0.0, 1.0);

    vec3 gridColor = vec3(0.5);

    // comece definindo a cor de fundo. Se o valor do pixel

    // não for sobrescrito posteriormente, essa cor será exibida.

    vec3 pixel = backgroundColor;

    // Desenha as linhas da grade

    // Desta vez, em vez de percorrer um loop para cada pixel,

    // usaremos a operação de módulo para obter o mesmo resultado

    // com um único cálculo (graças a mikatalk)

    const float tickWidth = 0.1;

    if( mod(r.x, tickWidth) < 0.008 ) pixel = gridColor;

    if( mod(r.y, tickWidth) < 0.008 ) pixel = gridColor;

    // Draw the axes

    if( abs(r.x)<0.006 ) pixel = axesColor;

    if( abs(r.y)<0.007 ) pixel = axesColor;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 11

    // FAZENDO A RAZÃO DE ASPECTO DO SISTEMA DE COORDENADAS 1.0

    //

    // Como vimos nos exemplos anteriores, obtemos retângulos

    // em vez de quadrados ao plotar os sistemas de coordenadas.

    // Isso ocorre porque atribuímos o mesmo intervalo numérico, [0,1],

    // a diferentes distâncias físicas. Na verdade, a largura do quadro

    // é maior do que sua altura.

    // Portanto, para manter a proporção, não devemos mapear as distâncias

    // reais [0, iResolution.x] e [0, iResolution.y] para o mesmo intervalo.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = vec2( fragCoord.xy - 0.5*iResolution.xy );

    r = 2.0 * r.xy / iResolution.y;

    // Em vez de dividir r.x por iResolution.x e r.y por iResolution.y,

    // divida ambos por iResolution.y.

    // Dessa forma, r.y estará no intervalo [-1.0, 1.0]

    // e r.x dependerá do tamanho do quadro. Eu suponho que no modo não tela cheia

    // rx estará no intervalo [-1.78, 1.78], e no modo tela cheia

    // para o meu laptop, será no intervalo [-1.6, 1.6] (1440./900.=1.6)

    vec3 backgroundColor = vec3(1.0);

    vec3 axesColor = vec3(0.0, 0.0, 1.0);

    vec3 gridColor = vec3(0.5);

    vec3 pixel = backgroundColor;

    // Desenhe linhas de grade

    const float tickWidth = 0.1;

    for(float i=-2.0; i<2.0; i+=tickWidth) {

    // "i" é a coordenada da linha.

    if(abs(r.x - i)<0.004) pixel = gridColor;

    if(abs(r.y - i)<0.004) pixel = gridColor;

    }

    // Desenhe os eixos

    if( abs(r.x)<0.006 ) pixel = axesColor;

    if( abs(r.y)<0.007 ) pixel = axesColor;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 12

    // DISCO

    //

    // Vamos desenhar discos

    //

    // Portanto, em GLSL, não damos um comando do tipo "desenhe este disco aqui com aquela

    // cor". Em vez disso, usamos um comando indireto como "se a coordenada do pixel

    // estiver dentro deste disco, coloque aquela cor para o pixel"

    // Os comandos indiretos são um pouco contra-intuitivos até que você

    // se acostume com essa forma de pensar.

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    vec3 bgCol = vec3(0.3);

    vec3 colBlue = vec3(0.216, 0.471, 0.698);

    vec3 colRed = vec3(1.00, 0.329, 0.298);

    vec3 colYellow = vec3(0.867, 0.910, 0.247);

    vec3 pixel = bgCol;

    // Para desenhar uma forma, devemos conhecer a expressão geométrica analítica

    // dessa forma.

    // Um círculo é o conjunto de pontos que têm a mesma distância do

    // seu centro. Essa distância é chamada de raio.

    // A distância do centro de coordenadas é sqrt(x*x + y*y)

    // Fixar a distância como o raio fornecerá a fórmula para

    // um círculo no centro de coordenadas

    // sqrt(x*x + y*y) = radius

    // Os pontos dentro do círculo, o disco, são dados por

    // sqrt(x*x + y*y) < radius

    // Elevando ambos os lados ao quadrado, obtemos

    // x*x + y*y < radius*radius

    float radius = 0.8;

    if( r.x*r.x + r.y*r.y < radius*radius ) {

    pixel = colBlue;

    }

    // Existe uma expressão abreviada para sqrt(v.x*v.x + v.y*v.y)

    // de um vetor dado "v", que é "length(v)"

    if( length(r) < 0.3) {

    pixel = colYellow;

    }

    // Desenhe um disco cujo centro não está em (0,0).

    // Digamos que o centro esteja em c: (c.x, c.y).

    // A distância de qualquer ponto r: (r.x, r.y) até c é

    // sqrt((r.x-c.x)^2+(r.y-c.y)^2)

    // Defina um vetor de distância d: (r.x - c.x, r.y - c.y)

    // Em GLSL, d pode ser calculado como "d = r - c".

    // Assim como na divisão, a subtração de dois vetores é feita

    // componente por componente.

    // Então, length(d) significa sqrt(d.x^2+d.y^2)

    // que é a fórmula da distância que estamos procurando.

    vec2 center = vec2(0.9, -0.4);

    vec2 d = r - center;

    if( length(d) < 0.6) {

    pixel = colRed;

    }

    // Essa mudança do centro da forma funciona para qualquer

    // tipo de forma. Se você tiver uma fórmula em termos de r

    // f(r) = 0, então f(r-c)=0 expressa a mesma forma geométrica,

    // mas suas coordenadas estão deslocadas por c.

    fragColor = vec4(pixel, 1.0);

    }

    // Observe como o disco mais recente é exibido e os anteriores

    // são deixados para trás. Isso ocorre porque a última condição if

    // altera o valor do pixel no final.

    // Se as coordenadas do pixel se encaixarem em várias condições if,

    // a última manipulação permanecerá e fragColor será definido para essa.

    #elif TUTORIAL == 13

    // FUNÇÕES

    //

    // Funções são ótimas para reutilização de código. Vamos colocar o código para desenhar discos

    // em uma função e usar a função para desenhar.

    // Existem muitas maneiras diferentes de escrever uma função para desenhar uma forma.

    //

    // Aqui temos uma função void que não retorna nada. Em vez disso,

    // "pixel" é tratado como uma expressão "inout". "inout" é uma palavra-chave única do GLSL.

    // Por padrão, todos os argumentos são argumentos "in". Isso

    // significa que o valor da variável é fornecido ao escopo da função

    // a partir do escopo em que a função é chamada.

    // Uma variável "out" fornece o valor da variável da função

    // para o escopo em que a função é chamada.

    // Um argumento "inout" faz ambos. Primeiro, o valor da variável é

    // enviado para a função como seu argumento. Em seguida, essa variável é

    // processada dentro da função. Quando a função termina, o valor

    // da variável é atualizado onde a função é chamada.

    //

    //

    // Aqui, a variável "pixel" é inicializada com a cor de fundo na função "main".

    // Em seguida, "pixel" é passado para a função "disk".

    // Quando a condição if é satisfeita, o valor do "pixel"

    // é alterado com o argumento "color". Se não for satisfeita, o

    // "pixel" permanece inalterado e mantém seu valor anterior (que era o

    // "bgColor").

    void disk(vec2 r, vec2 center, float radius, vec3 color, inout vec3 pixel) {

    if( length(r-center) < radius) {

    pixel = color;

    }

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    vec3 bgCol = vec3(0.3);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 pixel = bgCol;

    disk(r, vec2(0.1, 0.3), 0.5, col3, pixel);

    disk(r, vec2(-0.8, -0.6), 1.5, col1, pixel);

    disk(r, vec2(0.8, 0.0), .15, col2, pixel);

    fragColor = vec4(pixel, 1.0);

    }

    // Como você pode ver, as bordas dos discos têm curvas "irregulares", onde

    // pixels individuais podem ser vistos. Isso é chamado de "aliasing". Ele ocorre

    // porque os pixels têm tamanho finito e queremos desenhar uma forma contínua

    // em uma grade descontínua.

    // Existe um método para reduzir o aliasing. Isso é feito misturando as

    // cores internas e externas na borda. Para alcançar isso,

    // precisamos aprender algumas funções embutidas.

    // E, novamente, observe a ordem das chamadas de função de disco e como elas são

    // desenhadas uma sobre a outra. Cada função de disco manipula

    // a variável de pixel. Se um pixel for manipulado por várias funções de disco,

    // o valor da última é enviado para fragColor.

    // Neste caso, os valores anteriores são completamente sobrescritos.

    // O valor final depende apenas da última função que manipulou o pixel.

    // Não há misturas entre camadas.

    #elif TUTORIAL == 14

    // FUNÇÕES EMBUTIDAS: STEP

    //

    // A função "step" é a função degrau de Heaviside :-)

    // en.wikipedia.org/wiki/Heaviside_step_function

    //

    // f(x0, x) = {1 x>x0,

    // {0 x<x0

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 bgCol = vec3(0.0); // preto

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 pixel = bgCol;

    float edge, variable, ret;

    // Divida a tela em cinco partes horizontalmente

    // para diferentes exemplos

    if(r.x < -0.6*xMax) { // Parte I

    variable = r.y;

    edge = 0.2;

    if( variable > edge ) { // Se a "variable" for maior que "edge"

    ret = 1.0; // retorne 1.0

    } else { // Se a "variable" for menor que "edge"

    ret = 0.0; // retorne 0.0

    }

    }

    else if(r.x < -0.2*xMax) { // Parte II

    variable = r.y;

    edge = -0.2;

    ret = step(edge, variable); // A função de passo é equivalente ao

    // bloco "if" da Parte I

    }

    else if(r.x < 0.2*xMax) { // Parte III

    // A função "step" retorna 0.0 ou 1.0.

    // "1.0 - step" inverte o resultado.

    ret = 1.0 - step(0.5, r.y); // Espelhe a função "step" em torno da borda.

    }

    else if(r.x < 0.6*xMax) { // Parte IV

    // Se a coordenada y for menor que -0.4, o resultado é 0.3.

    // Se a coordenada y for maior que -0.4, o resultado é 0.3 + 0.5 = 0.8.

    ret = 0.3 + 0.5*step(-0.4, r.y);

    }

    else { // Parte V

    // Combine duas funções "step" para criar uma lacuna.

    ret = step(-0.3, r.y) * (1.0 - step(0.2, r.y));

    // "1.0 - ret" criará uma lacuna.

    }

    pixel = vec3(ret); // Crie uma cor a partir do valor de retorno.

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 15

    // FUNÇÕES EMBUTIDAS: CLAMP

    //

    // A função “clamp” satura a entrada abaixo e acima dos limites especificados.

    // f(x, min, max) = { max x>max

    // { x max>x>min

    // { min min>x

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    // use [0,1] sistema de coordenadas para este exemplo

    vec3 bgCol = vec3(0.0); // preto

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 pixel = bgCol;

    float edge, variable, ret;

    // divida a tela em quatro partes horizontalmente para diferentes

    // exemplos

    if(p.x < 0.25) { // Parte I

    ret = p.y; // o valor de brilho é atribuído à coordenada y

    // isso criará um gradiente

    }

    else if(p.x < 0.5) { // Parte II

    float minVal = 0.3; // implementação do clamp

    float maxVal = 0.6;

    float variable = p.y;

    if( variable<minVal ) {

    ret = minVal;

    }

    if( variable>minVal && variable<maxVal ) {

    ret = variable;

    }

    if( variable>maxVal ) {

    ret = maxVal;

    }

    }

    else if(p.x < 0.75) { // Parte III

    float minVal = 0.6;

    float maxVal = 0.8;

    float variable = p.y;

    ret = clamp(variable, minVal, maxVal);

    }

    else { // Parte IV

    float y = cos(5.*TWOPI*p.y); // oscilar entre +1 e -1

    // 5 vezes, verticalmente

    y = (y+1.0)*0.5; // mapear [-1,1] para [0,1]

    ret = clamp(y, 0.2, 0.8);

    }

    pixel = vec3(ret); // criar uma cor a partir do valor de retorno.

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 16

    // FUNÇÕES EMBUTIDAS: SMOOTHSTEP

    //

    // A função "smoothstep" é semelhante à função "step", mas, em vez de um

    // salto abrupto de 0 para 1 na borda, ela cria uma transição suave

    // dentro de um intervalo dado.

    // en.wikipedia.org/wiki/Smoothstep

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    // use o sistema de coordenadas [0,1] para este exemplo

    vec3 bgCol = vec3(0.0); // preto

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // vermelho

    vec3 col3 = vec3(0.867, 0.910, 0.247); // amarelo

    vec3 pixel = bgCol;

    float edge, variable, ret;

    // divida a tela em quatro partes horizontalmente para diferentes

    // exemplos

    if(p.x < 1./5.) { // Parte I

    float edge = 0.5;

    ret = step(edge, p.y); // Função de etapa simples

    }

    else if(p.x < 2./5.) { // Parte II

    // linearstep (não é uma função embutida)

    float edge0 = 0.45;

    float edge1 = 0.55;

    float t = (p.y - edge0)/(edge1 - edge0);

    // quando p.y == edge0 => t = 0.0

    // quando p.y == edge1 => t = 1.0

    // O lado direito da equação é uma função linear de y

    // Portanto, entre edge0 e edge1, t tem uma transição linear

    // entre 0.0 e 1.0

    float t1 = clamp(t, 0.0, 1.0);

    // t terá valores negativos quando t<edge0 e

    // t terá valores maiores que 1.0 quando t>edge1

    // mas queremos que ele esteja restrito entre 0.0 e 1.0

    // portanto, limitamos!

    ret = t1;

    }

    else if(p.x < 3./5.) { // Parte III

    // Implementação do smoothstep

    float edge0 = 0.45;

    float edge1 = 0.55;

    float t = clamp((p.y - edge0)/(edge1 - edge0), 0.0, 1.0);

    float t1 = 3.0*t*t - 2.0*t*t*t;

    // A interpolação anterior era linear. Visualmente, ela não

    // proporciona uma transição suave atraente.

    // Para obter suavidade, implemente um polinômio cúbico de Hermite.

    // 3*t^2 - 2*t^3

    ret = t1;

    }

    else if(p.x < 4./5.) { // Parte IV

    ret = smoothstep(0.45, 0.55, p.y);

    }

    else if(p.x < 5./5.) { // Parte V

    // smootherstep, uma sugestão de Ken Perlin

    float edge0 = 0.45;

    float edge1 = 0.55;

    float t = clamp((p.y - edge0)/(edge1 - edge0), 0.0, 1.0);

    // 6*t^5 - 15*t^4 + 10*t^3

    float t1 = t*t*t*(t*(t*6. - 15.) + 10.);

    ret = t1;

    // transição mais rápida e ainda mais suave

    // mas computacionalmente mais complexa.

    }

    pixel = vec3(ret); // crie uma cor a partir do valor de retorno.

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 17

    // FUNÇÕES EMBUTIDAS: MIX

    //

    // Um *shader* pode ser criado primeiro construindo partes individuais

    // e depois combinando-as.

    // Existem diferentes maneiras de combinar partes diferentes.

    // No exemplo anterior de disco, diferentes discos foram desenhados um sobre o outro.

    // Não houve mistura de camadas. Quando os discos se sobrepõem,

    // apenas o último é visível.

    //

    // Vamos aprender a misturar diferentes tipos de dados (neste caso, vec3's

    // que representam cores)

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec3 bgCol = vec3(0.3);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // vermelho

    vec3 col3 = vec3(0.867, 0.910, 0.247); // amarelo

    vec3 ret;

    // Divida a tela em quatro partes horizontalmente para diferentes

    // exemplos

    if(p.x < 1./5.) { // Parte I

    // Implementação da função de mistura (mix)

    float x0 = 0.2; // Primeiro item a ser misturado

    float x1 = 0.7; // Segundo item a ser misturado

    float m = 0.1; // Quantidade de mistura (entre 0.0 e 1.0)

    // Experimente com esse número

    // m = 0.0 significa que a saída é totalmente x0

    // m = 1.0 significa que a saída é totalmente x1

    // 0.0 < m < 1.0 é uma mistura linear de x0 e x1

    float val = x0*(1.0-m) + x1*m;

    ret = vec3(val);

    }

    else if(p.x < 2./5.) { // Parte II

    // Teste todos os valores possíveis de mistura

    float x0 = 0.2;

    float x1 = 0.7;

    float m = p.y;

    float val = x0*(1.0-m) + x1*m;

    ret = vec3(val);

    }

    else if(p.x < 3./5.) { // Parte III

    // Use a função de mistura (mix)

    float x0 = 0.2;

    float x1 = 0.7;

    float m = p.y;

    float val = mix(x0, x1, m);

    ret = vec3(val);

    }

    else if(p.x < 4./5.) { // Parte IV

    // Misture cores em vez de números

    float m = p.y;

    ret = mix(col1, col2, m);

    }

    else if(p.x < 5./5.) { // Parte V

    // Combine smoothstep e mix para transições de cor

    float m = smoothstep(0.5, 0.6, p.y);

    ret = mix(col1, col2, m);

    }

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 18

    // ANTI-ALIASING COM SMOOTHSTEP

    //

    float linearstep(float edge0, float edge1, float x) {

    float t = (x - edge0)/(edge1 - edge0);

    return clamp(t, 0.0, 1.0);

    }

    float smootherstep(float edge0, float edge1, float x) {

    float t = (x - edge0)/(edge1 - edge0);

    float t1 = t*t*t*(t*(t*6. - 15.) + 10.);

    return clamp(t1, 0.0, 1.0);

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 bgCol = vec3(0.3);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 pixel = bgCol;

    float m;

    float radius = 0.4; // Aumente isso para ver o efeito melhor

    if( r.x < -0.5*xMax ) { // Parte I

    // Sem interpolação, mas com aliasing

    m = step( radius, length(r - vec2(-0.5*xMax-0.4,0.0)) );

    // Se a distância do centro for menor que o raio,

    // então o valor de mistura (mix) é 0.0

    // caso contrário, o valor de mistura (mix) é 1.0

    pixel = mix(col1, bgCol, m);

    }

    else if( r.x < -0.0*xMax ) { // Parte II

    // linearstep (interpolação de primeira ordem, linear)

    m = linearstep( radius-0.005, radius+0.005, length(r - vec2(-0.0*xMax-0.4,0.0)) );

    // O valor de mistura é interpolado linearmente quando a distância até o centro

    // é 0,005 menor e maior que o raio.

    pixel = mix(col1, bgCol, m);

    }

    else if( r.x < 0.5*xMax ) { // Parte III

    // smoothstep (interpolação cúbica)

    m = smoothstep( radius-0.005, radius+0.005, length(r - vec2(0.5*xMax-0.4,0.0)) );

    pixel = mix(col1, bgCol, m);

    }

    else if( r.x < 1.0*xMax ) { // Parte IV

    // smootherstep (interpolação de sexta ordem)

    m = smootherstep( radius-0.005, radius+0.005, length(r - vec2(1.0*xMax-0.4,0.0)) );

    pixel = mix(col1, bgCol, m);

    }

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 19

    // TRAÇADO DE FUNÇÕES

    //

    // Sempre é útil ver os gráficos de funções no sistema de coordenadas cartesianas

    // para entender o que estão fazendo com precisão.

    //

    // Vamos plotar algumas funções unidimensionais!

    //

    // Se o valor de y é uma função f do valor de x, a expressão da relação entre eles é: y = f(x)

    // Em outras palavras, o gráfico de uma função é o conjunto de todos os pontos

    // que satisfazem a expressão: y - f(x) = 0

    // Esse conjunto tem espessura zero e não pode ser visto.

    // Em vez disso, usamos o conjunto de (x, y) que satisfaz: -d < y - f(x) < d

    // ou seja, abs(y - f(x)) < d

    // onde d é a espessura (a espessura é na direção y)

    // Devido às propriedades da função absoluta, a condição

    // abs(y - f(x)) < d é equivalente à condição:

    // abs(f(x) - y) < d

    // Usaremos esta última para traçar funções. (na anterior,

    // precisávamos negar a função que queríamos plotar)

    //

    float linearstep(float edge0, float edge1, float x) {

    float t = (x - edge0)/(edge1 - edge0);

    return clamp(t, 0.0, 1.0);

    }

    float smootherstep(float edge0, float edge1, float x) {

    float t = (x - edge0)/(edge1 - edge0);

    float t1 = t*t*t*(t*(t*6. - 15.) + 10.);

    return clamp(t1, 0.0, 1.0);

    }

    void plot(vec2 r, float y, float lineThickness, vec3 color, inout vec3 pixel) {

    if( abs(y - r.y) < lineThickness ) pixel = color;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    vec3 bgCol = vec3(1.0);

    vec3 axesCol = vec3(0.0, 0.0, 1.0);

    vec3 gridCol = vec3(0.5);

    vec3 col1 = vec3(0.841, 0.582, 0.594);

    vec3 col2 = vec3(0.884, 0.850, 0.648);

    vec3 col3 = vec3(0.348, 0.555, 0.641);

    vec3 pixel = bgCol;

    // Desenha linhas da grade

    const float tickWidth = 0.1;

    for(float i=-2.0; i<2.0; i+=tickWidth) {

    // "i" é a coordenada da linha.

    if(abs(r.x - i)<0.004) pixel = gridCol;

    if(abs(r.y - i)<0.004) pixel = gridCol;

    }

    // Desenhe os eixos

    if( abs(r.x)<0.006 ) pixel = axesCol;

    if( abs(r.y)<0.007 ) pixel = axesCol;

    // Desenhe as funções

    float x = r.x;

    float y = r.y;

    // Funções em rosa

    // y = 2*x + 5

    if( abs(2.*x + .5 - y) < 0.02 ) pixel = col1;

    // y = x^2 - .2

    if( abs(r.x*r.x-0.2 - y) < 0.01 ) pixel = col1;

    // y = sin(PI x)

    if( abs(sin(PI*r.x) - y) < 0.02 ) pixel = col1;

    // Funções em azul, as variações da função degrau

    // (as funções são escaladas e traduzidas verticalmente)

    if( abs(0.25*step(0.0, x)+0.6 - y) < 0.01 ) pixel = col3;

    if( abs(0.25*linearstep(-0.5, 0.5, x)+0.1 - y) < 0.01 ) pixel = col3;

    if( abs(0.25*smoothstep(-0.5, 0.5, x)-0.4 - y) < 0.01 ) pixel = col3;

    if( abs(0.25*smootherstep(-0.5, 0.5, x)-0.9 - y) < 0.01 ) pixel = col3;

    // Funções amarelas

    // Tenha uma função que trace funções :-)

    plot(r, 0.5*clamp(sin(TWOPI*x), 0.0, 1.0)-0.7, 0.015, col2, pixel);

    // Curva de sino em torno de -0.5

    plot(r, 0.6*exp(-10.0*(x+0.8)*(x+0.8)) - 0.1, 0.015, col2, pixel);

    fragColor = vec4(pixel, 1.0);

    }

    // No futuro, podemos usar este framework para visualizar gráficos de funções

    // e projetar e encontrar funções de acordo com nossas preferências.

    // Na verdade, usar ferramentas como Mathematica, Matlab, matplotlib, etc. para plotar funções

    // é muito mais prático. No entanto, eles exigem uma tradução das funções

    // do GLSL para a linguagem deles. Aqui, podemos plotar as implementações nativas

    // das funções do GLSL.

    #elif TUTORIAL == 20

    // ADIÇÃO E SUBTRAÇÃO DE CORES

    //

    // Como desenhar uma forma sobre outra e como as camadas

    // abaixo afetam as camadas superiores?

    //

    // Nas funções de desenho de formas anteriores, definimos o valor do pixel

    // a partir da função. Desta vez, a função de forma apenas retornará um valor

    // de ponto flutuante entre 0,0 e 1,0 para indicar a área da forma. Mais tarde,

    // esse valor pode ser multiplicado por alguma cor e usado para determinar a cor

    // final do pixel.

    // Uma função que retorna 1,0 dentro da área do disco

    // retorna 0,0 fora da área do disco

    // e tem uma transição suave no raio

    float disk(vec2 r, vec2 center, float radius) {

    float distanceFromCenter = length(r-center);

    float outsideOfDisk = smoothstep( radius-0.005, radius+0.005, distanceFromCenter);

    float insideOfDisk = 1.0 - outsideOfDisk;

    return insideOfDisk;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 black = vec3(0.0);

    vec3 white = vec3(1.0);

    vec3 gray = vec3(0.3);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // vermelho

    vec3 col3 = vec3(0.867, 0.910, 0.247); // amarelo

    vec3 ret;

    float d;

    if(p.x < 1./3.) { // Parte I

    // camadas opacas sobrepostas

    ret = gray;

    // atribua um valor cinza ao pixel primeiro

    d = disk(r, vec2(-1.1,0.3), 0.4);

    ret = mix(ret, col1, d); // misture o valor de cor anterior com

    // o novo valor de cor de acordo com

    // a função da área da forma.

    // nesta linha, a cor anterior é cinza.

    d = disk(r, vec2(-1.3,0.0), 0.4);

    ret = mix(ret, col2, d);

    d = disk(r, vec2(-1.05,-0.3), 0.4);

    ret = mix(ret, col3, d); // aqui, a cor anterior pode ser cinza,

    // azul ou rosa.

    }

    else if(p.x < 2./3.) { // Parte II

    // Adição de Cores

    // Assim é como as luzes de diferentes cores se somam

    // en.wikipedia.org/wiki/Additive_color

    ret = black; // start with black pixels

    ret += disk(r, vec2(0.1,0.3), 0.4)*col1; // Adicione a nova cor

    // à cor anterior

    ret += disk(r, vec2(-.1,0.0), 0.4)*col2;

    ret += disk(r, vec2(.15,-0.3), 0.4)*col3;

    // Quando todos os componentes de "ret" se tornam iguais ou maiores que 1.0

    // ela se torna branca.

    }

    else if(p.x < 3./3.) { // Part III

    // Subtração de Cores

    // Assim é como os corantes de diferentes cores se somam

    // en.wikipedia.org/wiki/Subtractive_color

    ret = white; // start with white

    ret -= disk(r, vec2(1.1,0.3), 0.4)*col1;

    ret -= disk(r, vec2(1.05,0.0), 0.4)* col2;

    ret -= disk(r, vec2(1.35,-0.25), 0.4)* col3;

    // Quando todos os componentes de "ret" se tornam iguais ou menores que 0.0

    // ela se torna preta.

    }

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 21

    // TRANSFORMAÇÕES DE COORDENADAS: ROTAÇÃO

    //

    // Até agora, traduzimos o centro de coordenadas para desenhar formas geométricas

    // em diferentes partes da tela.

    // Vamos aprender como rotacionar as formas.

    // uma função que desenha uma grade (anti-aliasing) do sistema de coordenadas.

    float coordinateGrid(vec2 r) {

    vec3 axesCol = vec3(0.0, 0.0, 1.0);

    vec3 gridCol = vec3(0.5);

    float ret = 0.0;

    // Desenha linhas da grade

    const float tickWidth = 0.1;

    for(float i=-2.0; i<2.0; i+=tickWidth) {

    // "i" é a coordenada da linha.

    ret += 1.-smoothstep(0.0, 0.008, abs(r.x-i));

    ret += 1.-smoothstep(0.0, 0.008, abs(r.y-i));

    }

    // Desenha os eixos

    ret += 1.-smoothstep(0.001, 0.015, abs(r.x));

    ret += 1.-smoothstep(0.001, 0.015, abs(r.y));

    return ret;

    }

    // retorna 1.0 se estiver dentro do círculo

    float disk(vec2 r, vec2 center, float radius) {

    return 1.0 - smoothstep( radius-0.005, radius+0.005, length(r-center));

    }

    // retorna 1.0 se estiver dentro do retângulo

    float rectangle(vec2 r, vec2 topLeft, vec2 bottomRight) {

    float ret;

    float d = 0.005;

    ret = smoothstep(topLeft.x-d, topLeft.x+d, r.x);

    ret *= smoothstep(topLeft.y-d, topLeft.y+d, r.y);

    ret *= 1.0 - smoothstep(bottomRight.y-d, bottomRight.y+d, r.y);

    ret *= 1.0 - smoothstep(bottomRight.x-d, bottomRight.x+d, r.x);

    return ret;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 bgCol = vec3(1.0);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 ret;

    vec2 q;

    float angle;

    angle = 0.2*PI; // ângulo em radianos (PI é 180 graus)

    // q é o sistema de coordenadas rotacionado

    q.x = cos(angle)*r.x + sin(angle)*r.y;

    q.y = - sin(angle)*r.x + cos(angle)*r.y;

    ret = bgCol;

    // desenhe os sistemas de coordenadas antigo e novo

    ret = mix(ret, col1, coordinateGrid(r)*0.4 );

    ret = mix(ret, col2, coordinateGrid(q) );

    // desenhe formas no sistema de coordenadas antigo, r, e no sistema de coordenadas novo, q

    ret = mix(ret, col1, disk(r, vec2(1.0, 0.0), 0.2));

    ret = mix(ret, col2, disk(q, vec2(1.0, 0.0), 0.2));

    ret = mix(ret, col1, rectangle(r, vec2(-0.8, 0.2), vec2(-0.5, 0.4)) );

    ret = mix(ret, col2, rectangle(q, vec2(-0.8, 0.2), vec2(-0.5, 0.4)) );

    // como você pode ver, ambos os círculos são desenhados na mesma coordenada, (1,0),

    // em seus respectivos sistemas de coordenadas. Mas eles aparecem

    // em diferentes posições na tela.

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 22

    // TRANSFORMAÇÕES DE COORDENADAS: ESCALA

    //

    // Escalando o sistema de coordenadas.

    // Uma função que desenha uma grade (anti-aliasing) do sistema de coordenadas.

    float coordinateGrid(vec2 r) {

    vec3 axesCol = vec3(0.0, 0.0, 1.0);

    vec3 gridCol = vec3(0.5);

    float ret = 0.0;

    // Desenha linhas da grade

    const float tickWidth = 0.1;

    for(float i=-2.0; i<2.0; i+=tickWidth) {

    // "i" é a coordenada da linha.

    ret += 1.-smoothstep(0.0, 0.008, abs(r.x-i));

    ret += 1.-smoothstep(0.0, 0.008, abs(r.y-i));

    }

    // Desenha os eixos

    ret += 1.-smoothstep(0.001, 0.015, abs(r.x));

    ret += 1.-smoothstep(0.001, 0.015, abs(r.y));

    return ret;

    }

    // retorna 1.0 se estiver dentro do círculo

    float disk(vec2 r, vec2 center, float radius) {

    return 1.0 - smoothstep( radius-0.005, radius+0.005, length(r-center));

    }

    // retorna 1.0 se estiver dentro do retângulo

    float rectangle(vec2 r, vec2 topLeft, vec2 bottomRight) {

    float ret;

    float d = 0.005;

    ret = smoothstep(topLeft.x-d, topLeft.x+d, r.x);

    ret *= smoothstep(topLeft.y-d, topLeft.y+d, r.y);

    ret *= 1.0 - smoothstep(bottomRight.y-d, bottomRight.y+d, r.y);

    ret *= 1.0 - smoothstep(bottomRight.x-d, bottomRight.x+d, r.x);

    return ret;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 bgCol = vec3(1.0);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 ret = bgCol;

    // original

    ret = mix(ret, col1, coordinateGrid(r)/2.0);

    // escalado

    float scaleFactor = 3.3; // aproxime-se tanto assim

    vec2 q = r / scaleFactor;

    ret = mix(ret, col2, coordinateGrid(q)/2.0);

    ret = mix(ret, col2, disk(q, vec2(0.0, 0.0), 0.1));

    ret = mix(ret, col1, disk(r, vec2(0.0, 0.0), 0.1));

    ret = mix(ret, col1, rectangle(r, vec2(-0.5, 0.0), vec2(-0.2, 0.2)) );

    ret = mix(ret, col2, rectangle(q, vec2(-0.5, 0.0), vec2(-0.2, 0.2)) );

    // observe como o retângulo que não está centrado na origem das coordenadas

    // mudou sua localização após a escala, mas os discos no centro

    // permaneceram onde estão.

    // Isso ocorre porque a escala é feita multiplicando todas as coordenadas de pixel

    // por uma constante.

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 23

    // TRANSFORMAÇÕES DE COORDENADAS SUCESSIVAS

    //

    // Desenhar uma forma na localização desejada, com tamanho desejado e

    // orientação desejada requer domínio na aplicação sucessiva de

    // transformações.

    //

    // Em geral, transformações não comutam. Isso significa que

    // se você alterar a ordem delas, obterá resultados diferentes.

    //

    // Vamos tentar aplicar transformações em diferentes ordens.

    float coordinateGrid(vec2 r) {

    vec3 axesCol = vec3(0.0, 0.0, 1.0);

    vec3 gridCol = vec3(0.5);

    float ret = 0.0;

    // Desenhe linhas de grade

    const float tickWidth = 0.1;

    for(float i=-2.0; i<2.0; i+=tickWidth) {

    // "i" é a coordenada da linha.

    ret += 1.-smoothstep(0.0, 0.008, abs(r.x-i));

    ret += 1.-smoothstep(0.0, 0.008, abs(r.y-i));

    }

    // Desenhe os eixos

    ret += 1.-smoothstep(0.001, 0.015, abs(r.x));

    ret += 1.-smoothstep(0.001, 0.015, abs(r.y));

    return ret;

    }

    // retorna 1.0 se estiver dentro do círculo

    float disk(vec2 r, vec2 center, float radius) {

    return 1.0 - smoothstep( radius-0.005, radius+0.005, length(r-center));

    }

    // retorna 1.0 se estiver dentro do disco

    float rectangle(vec2 r, vec2 topLeft, vec2 bottomRight) {

    float ret;

    float d = 0.005;

    ret = smoothstep(topLeft.x-d, topLeft.x+d, r.x);

    ret *= smoothstep(topLeft.y-d, topLeft.y+d, r.y);

    ret *= 1.0 - smoothstep(bottomRight.y-d, bottomRight.y+d, r.y);

    ret *= 1.0 - smoothstep(bottomRight.x-d, bottomRight.x+d, r.x);

    return ret;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 bgCol = vec3(1.0);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 ret = bgCol;

    float angle = 0.6;

    mat2 rotationMatrix = mat2(cos(angle), -sin(angle),

    sin(angle), cos(angle));

    if(p.x < 1./2.) { // Parte I

    // Coloque a origem no centro da Parte I

    r = r - vec2(-xMax/2.0, 0.0);

    vec2 rotated = rotationMatrix*r;

    vec2 rotatedTranslated = rotated - vec2(0.4, 0.5);

    ret = mix(ret, col1, coordinateGrid(r)*0.3);

    ret = mix(ret, col2, coordinateGrid(rotated)*0.3);

    ret = mix(ret, col3, coordinateGrid(rotatedTranslated)*0.3);

    ret = mix(ret, col1, rectangle(r, vec2(-.1, -.2), vec2(0.1, 0.2)) );

    ret = mix(ret, col2, rectangle(rotated, vec2(-.1, -.2), vec2(0.1, 0.2)) );

    ret = mix(ret, col3, rectangle(rotatedTranslated, vec2(-.1, -.2), vec2(0.1, 0.2)) );

    }

    else if(p.x < 2./2.) { // Parte II

    r = r - vec2(xMax*0.5, 0.0);

    vec2 translated = r - vec2(0.4, 0.5);

    vec2 translatedRotated = rotationMatrix*translated;

    ret = mix(ret, col1, coordinateGrid(r)*0.3);

    ret = mix(ret, col2, coordinateGrid(translated)*0.3);

    ret = mix(ret, col3, coordinateGrid(translatedRotated)*0.3);

    ret = mix(ret, col1, rectangle(r, vec2(-.1, -.2), vec2(0.1, 0.2)) );

    ret = mix(ret, col2, rectangle(translated, vec2(-.1, -.2), vec2(0.1, 0.2)) );

    ret = mix(ret, col3, rectangle(translatedRotated, vec2(-.1, -.2), vec2(0.1, 0.2)) );

    }

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 24

    // TEMPO, MOVIMENTO E ANIMAÇÃO

    //

    // Um dos inputs que um shader recebe pode ser o tempo.

    // No ShaderToy, a variável "iTime" armazena o valor do

    // tempo em segundos desde que o shader foi iniciado.

    //

    // Vamos alterar algumas variáveis em relação ao tempo!

    float disk(vec2 r, vec2 center, float radius) {

    return 1.0 - smoothstep( radius-0.005, radius+0.005, length(r-center));

    }

    float rect(vec2 r, vec2 bottomLeft, vec2 topRight) {

    float ret;

    float d = 0.005;

    ret = smoothstep(bottomLeft.x-d, bottomLeft.x+d, r.x);

    ret *= smoothstep(bottomLeft.y-d, bottomLeft.y+d, r.y);

    ret *= 1.0 - smoothstep(topRight.y-d, topRight.y+d, r.y);

    ret *= 1.0 - smoothstep(topRight.x-d, topRight.x+d, r.x);

    return ret;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 col1 = vec3(0.216, 0.471, 0.698); // blue

    vec3 col2 = vec3(1.00, 0.329, 0.298); // yellow

    vec3 col3 = vec3(0.867, 0.910, 0.247); // red

    vec3 ret;

    if(p.x < 1./5.) { // Part I

    vec2 q = r + vec2(xMax*4./5.,0.);

    ret = vec3(0.2);

    // y coordinate depends on time

    float y = iTime;

    // mod constraints y to be between 0.0 and 2.0,

    // and y jumps from 2.0 to 0.0

    // substracting -1.0 makes why jump from 1.0 to -1.0

    y = mod(y, 2.0) - 1.0;

    ret = mix(ret, col1, disk(q, vec2(0.0, y), 0.1) );

    }

    else if(p.x < 2./5.) { // Part II

    vec2 q = r + vec2(xMax*2./5.,0.);

    ret = vec3(0.3);

    // oscillation

    float amplitude = 0.8;

    // y coordinate oscillates with a period of 0.5 seconds

    float y = 0.8*sin(0.5*iTime*TWOPI);

    // radius oscillates too

    float radius = 0.15 + 0.05*sin(iTime*8.0);

    ret = mix(ret, col1, disk(q, vec2(0.0, y), radius) );

    }

    else if(p.x < 3./5.) { // Part III

    vec2 q = r + vec2(xMax*0./5.,0.);

    ret = vec3(0.4);

    // booth coordinates oscillates

    float x = 0.2*cos(iTime*5.0);

    // but they have a phase difference of PI/2

    float y = 0.3*cos(iTime*5.0 + PI/2.0);

    float radius = 0.2 + 0.1*sin(iTime*2.0);

    // make the color mixture time dependent

    vec3 color = mix(col1, col2, sin(iTime)*0.5+0.5);

    ret = mix(ret, color, rect(q, vec2(x-0.1, y-0.1), vec2(x+0.1, y+0.1)) );

    // try different phases, different amplitudes and different frequencies

    // for x and y coordinates

    }

    else if(p.x < 4./5.) { // Part IV

    vec2 q = r + vec2(-xMax*2./5.,0.);

    ret = vec3(0.3);

    for(float i=-1.0; i<1.0; i+= 0.2) {

    float x = 0.2*cos(iTime*5.0 + i*PI);

    // y coordinate is the loop value

    float y = i;

    vec2 s = q - vec2(x, y);

    // each box has a different phase

    float angle = iTime*3. + i;

    mat2 rot = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));

    s = rot*s;

    ret = mix(ret, col1, rect(s, vec2(-0.06, -0.06), vec2(0.06, 0.06)) );

    }

    }

    else if(p.x < 5./5.) { // Part V

    vec2 q = r + vec2(-xMax*4./5., 0.);

    ret = vec3(0.2);

    // let stop and move again periodically

    float speed = 2.0;

    float t = iTime*speed;

    float stopEveryAngle = PI/2.0;

    float stopRatio = 0.5;

    float t1 = (floor(t) + smoothstep(0.0, 1.0-stopRatio, fract(t)) )*stopEveryAngle;

    float x = -0.2*cos(t1);

    float y = 0.3*sin(t1);

    float dx = 0.1 + 0.03*sin(t*10.0);

    float dy = 0.1 + 0.03*sin(t*10.0+PI);

    ret = mix(ret, col1, rect(q, vec2(x-dx, y-dy), vec2(x+dx, y+dy)) );

    }

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 25

    // EFEITO PLASMA

    //

    // Dissemos que a cor de um pixel depende apenas de suas coordenadas

    // e de outras entradas (como o tempo)

    //

    // Existe um efeito chamado Plasma, que é baseado em uma mistura de

    // funções complexas na forma de f(x,y).

    //

    // Vamos escrever um plasma!

    //

    // en.wikipedia.org/wiki/Plasma_effect

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float t = iTime;

    r = r * 8.0;

    float v1 = sin(r.x +t);

    float v2 = sin(r.y +t);

    float v3 = sin(r.x+r.y +t);

    float v4 = sin(length(r) +1.7*t);

    float v = v1+v2+v3+v4;

    vec3 ret;

    if(p.x < 1./10.) { // Parte I

    // ondas verticais

    ret = vec3(v1);

    }

    else if(p.x < 2./10.) { // Parte II

    // ondas horizontais

    ret = vec3(v2);

    }

    else if(p.x < 3./10.) { // Parte III

    // ondas diagonais

    ret = vec3(v3);

    }

    else if(p.x < 4./10.) { // Parte IV

    // ondas circulares

    ret = vec3(v4);

    }

    else if(p.x < 5./10.) { // Parte V

    // a soma de todas as ondas

    ret = vec3(v);

    }

    else if(p.x < 6./10.) { // Parte VI

    // Adicionar periodicidade aos gradientes

    ret = vec3(sin(2.*v));

    }

    else if(p.x < 10./10.) { // Parte VII

    // misturar cores

    v *= 1.0;

    ret = vec3(sin(v), sin(v+0.5*PI), sin(v+1.0*PI));

    }

    ret = 0.5 + 0.5*ret;

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.);

    }

    #elif TUTORIAL == 26

    // TEXTURAS

    //

    // ShaderToy pode usar até quatro texturas.

    float rect(vec2 r, vec2 bottomLeft, vec2 topRight) {

    float ret;

    float d = 0.005;

    ret = smoothstep(bottomLeft.x-d, bottomLeft.x+d, r.x);

    ret *= smoothstep(bottomLeft.y-d, bottomLeft.y+d, r.y);

    ret *= 1.0 - smoothstep(topRight.y-d, topRight.y+d, r.y);

    ret *= 1.0 - smoothstep(topRight.x-d, topRight.x+d, r.x);

    return ret;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 bgCol = vec3(0.3);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 ret;

    if(p.x < 1./3.) { // Parte I

    ret = texture(iChannel1, p).xyz;

    }

    else if(p.x < 2./3.) { // Parte II

    ret = texture(iChannel1, 4.*p+vec2(0.,iTime)).xyz;

    }

    else if(p.x < 3./3.) { // Parte III

    r = r - vec2(xMax*2./3., 0.);

    float angle = iTime;

    mat2 rotMat = mat2(cos(angle), -sin(angle),

    sin(angle), cos(angle));

    vec2 q = rotMat*r;

    vec3 texA = texture(iChannel1, q).xyz;

    vec3 texB = texture(iChannel2, q).xyz;

    angle = -iTime;

    rotMat = mat2(cos(angle), -sin(angle),

    sin(angle), cos(angle));

    q = rotMat*r;

    ret = mix(texA, texB, rect(q, vec2(-0.3, -0.3), vec2(.3, .3)) );

    }

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 27

    // ENTRADA DO MOUSE

    //

    // O ShaderToy fornece as coordenadas do cursor do mouse e cliques de botão

    // como entrada via o vec4 iMouse.

    //

    // Vamos escrever um shader com funcionalidade básica de mouse.

    // Quando clicado no quadro, o pequeno disco seguirá o

    // cursor. A coordenada x do cursor altera a cor de fundo.

    // E se o cursor estiver dentro do disco maior, sua cor mudará.

    float disk(vec2 r, vec2 center, float radius) {

    return 1.0 - smoothstep( radius-0.5, radius+0.5, length(r-center));

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    // A cor de fundo depende da coordenada x do cursor

    vec3 bgCol = vec3(iMouse.x / iResolution.x);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 ret = bgCol;

    vec2 center;

    // Desenha o grande disco amarelo

    center = vec2(100., iResolution.y/2.);

    float radius = 60.;

    // Se as coordenadas do cursor estiverem dentro do disco

    if( length(iMouse.xy-center)>radius ) {

    // use color3

    ret = mix(ret, col3, disk(fragCoord.xy, center, radius));

    }

    else {

    // caso contrário use color2

    ret = mix(ret, col2, disk(fragCoord.xy, center, radius));

    }

    // Desenha o pequeno disco azul no cursor

    center = iMouse.xy;

    ret = mix(ret, col1, disk(fragCoord.xy, center, 20.));

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    #elif TUTORIAL == 28

    // ALEATORIEDADE

    //

    // Não sei por que, mas o GLSL não possui geradores de números aleatórios.

    // Isso não é um problema se você estiver escrevendo seu código em

    // uma linguagem de programação que possui funções de geração de números aleatórios.

    // Dessa forma, você pode gerar os valores aleatórios usando a linguagem e enviá-los

    // para o shader por meio de uniformes.

    //

    // No entanto, se você estiver usando um sistema que permite apenas escrever

    // o código do shader, como o ShaderToy, então você precisa escrever seus próprios

    // geradores pseudo-aleatórios.

    //

    // Aqui está um padrão que vi repetidamente em muitos shaders diferentes

    // no ShaderToy

    // Vamos desenhar N discos diferentes em locais aleatórios usando esse padrão.

    float hash(float seed)

    {

    // Retorne um número "random" com base na "seed"

    return fract(sin(seed) * 43758.5453);

    }

    vec2 hashPosition(float x)

    {

    // Retorne uma posição "random" com base na "seed"

    return vec2(hash(x), hash(x * 1.1));

    }

    float disk(vec2 r, vec2 center, float radius) {

    return 1.0 - smoothstep( radius-0.005, radius+0.005, length(r-center));

    }

    float coordinateGrid(vec2 r) {

    vec3 axesCol = vec3(0.0, 0.0, 1.0);

    vec3 gridCol = vec3(0.5);

    float ret = 0.0;

    // Desenhe linhas de grade

    const float tickWidth = 0.1;

    for(float i=-2.0; i<2.0; i+=tickWidth) {

    // "i" é a coordenada da linha.

    ret += 1.-smoothstep(0.0, 0.005, abs(r.x-i));

    ret += 1.-smoothstep(0.0, 0.01, abs(r.y-i));

    }

    // Desenhe os eixos

    ret += 1.-smoothstep(0.001, 0.005, abs(r.x));

    ret += 1.-smoothstep(0.001, 0.005, abs(r.y));

    return ret;

    }

    float plot(vec2 r, float y, float thickness) {

    return ( abs(y - r.y) < thickness ) ? 1.0 : 0.0;

    }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    vec2 p = vec2(fragCoord.xy / iResolution.xy);

    vec2 r = 2.0*vec2(fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;

    float xMax = iResolution.x/iResolution.y;

    vec3 bgCol = vec3(0.3);

    vec3 col1 = vec3(0.216, 0.471, 0.698); // azul

    vec3 col2 = vec3(1.00, 0.329, 0.298); // amarelo

    vec3 col3 = vec3(0.867, 0.910, 0.247); // vermelho

    vec3 ret = bgCol;

    vec3 white = vec3(1.);

    vec3 gray = vec3(.3);

    if(r.y > 0.7) {

    // Sistema de coordenadas traduzido e rotacionado

    vec2 q = (r-vec2(0.,0.9))*vec2(1.,20.);

    ret = mix(white, gray, coordinateGrid(q));

    // Apenas a função seno regular

    float y = sin(5.*q.x) * 2.0 - 1.0;

    ret = mix(ret, col1, plot(q, y, 0.1));

    }

    else if(r.y > 0.4) {

    vec2 q = (r-vec2(0.,0.6))*vec2(1.,20.);

    ret = mix(white, col1, coordinateGrid(q));

    // Obtenha a parte decimal da função seno

    float y = fract(sin(5.*q.x)) * 2.0 - 1.0;

    ret = mix(ret, col2, plot(q, y, 0.1));

    }

    else if(r.y > 0.1) {

    vec3 white = vec3(1.);

    vec2 q = (r-vec2(0.,0.25))*vec2(1.,20.);

    ret = mix(white, gray, coordinateGrid(q));

    // amplie o resultado da função seno

    // aumente a escala e observe a transição de

    // um padrão periódico para um padrão caótico

    float scale = 10.0;

    float y = fract(sin(5.*q.x) * scale) * 2.0 - 1.0;

    ret = mix(ret, col1, plot(q, y, 0.2));

    }

    else if(r.y > -0.2) {

    vec3 white = vec3(1.);

    vec2 q = (r-vec2(0., -0.0))*vec2(1.,10.);

    ret = mix(white, col1, coordinateGrid(q));

    float seed = q.x;

    // Amplie com um número real grande

    float y = fract(sin(seed) * 43758.5453) * 2.0 - 1.0;

    // Isso pode ser usado como um valor pseudoaleatório

    // Esse tipo de função, em que dois valores de entrada

    // que estão próximos um do outro (como posições q.x próximas)

    // retornam valores de saída muito diferentes, são chamadas de

    // "funções hash".

    ret = mix(ret, col2, plot(q, y, 0.1));

    }

    else {

    vec2 q = (r-vec2(0., -0.6));

    // use o índice do loop como a semente

    // e varie diferentes quantidades de discos, como

    // localização e raio

    for(float i=0.0; i<6.0; i++) {

    // altere a semente e obtenha diferentes distribuições

    float seed = i + 0.0;

    vec2 pos = (vec2(hash(seed), hash(seed + 0.5))-0.5)*3.;;

    float radius = hash(seed + 3.5);

    pos *= vec2(1.0,0.3);

    ret = mix(ret, col1, disk(q, pos, 0.2*radius));

    }

    }

    vec3 pixel = ret;

    fragColor = vec4(pixel, 1.0);

    }

    /* Fim dos Tutoriais */

    #elif TUTORIAL == 0

    // TELA DE BOAS VINDAS

    float square(vec2 r, vec2 bottomLeft, float side) {

    vec2 p = r - bottomLeft;

    return ( p.x > 0.0 && p.x < side && p.y>0.0 && p.y < side ) ? 1.0 : 0.0;

    }

    float character(vec2 r, vec2 bottomLeft, float charCode, float squareSide) {

    vec2 p = r - bottomLeft;

    float ret = 0.0;

    float num, quotient, remainder, divider;

    float x, y;

    num = charCode;

    for(int i=0; i<20; i++) {

    float boxNo = float(19-i);

    divider = pow(2., boxNo);

    quotient = floor(num / divider);

    remainder = num - quotient*divider;

    num = remainder;

    y = floor(boxNo/4.0);

    x = boxNo - y*4.0;

    if(quotient == 1.) {

    ret += square( p, squareSide*vec2(x, y), squareSide );

    }

    }

    return ret;

    }

    mat2 rot(float th) { return mat2(cos(th), -sin(th), sin(th), cos(th)); }

    void mainImage( out vec4 fragColor, in vec2 fragCoord )

    {

    float G = 990623.; // Caracteres compactados :-)

    float L = 69919.;

    float S = 991119.;

    float t = iTime;

    vec2 r = (fragCoord.xy - 0.5*iResolution.xy) / iResolution.y;

    //vec2 rL = rot(t)*r+0.0001*t;

    //vec2 rL = r+vec2(cos(t*0.02),sin(t*0.02))*t*0.05;

    float c = 0.05;//+0.03*sin(2.5*t);

    vec2 pL = (mod(r+vec2(cos(0.3*t),sin(0.3*t)), 2.0*c)-c)/c;

    float circ = 1.0-smoothstep(0.75, 0.8, length(pL));

    vec2 rG = rot(2.*3.1415*smoothstep(0.,1.,mod(1.5*t,4.0)))*r;

    vec2 rStripes = rot(0.2)*r;

    float xMax = 0.5*iResolution.x/iResolution.y;

    float letterWidth = 2.0*xMax*0.9/4.0;

    float side = letterWidth/4.;

    float space = 2.0*xMax*0.1/5.0;

    r += 0.001; // Para eliminar a linha horizontal azul em y=0.

    float maskGS = character(r, vec2(-xMax+space, -2.5*side)+vec2(letterWidth+space, 0.0)*0.0, G, side);

    float maskG = character(rG, vec2(-xMax+space, -2.5*side)+vec2(letterWidth+space, 0.0)*0.0, G, side);

    float maskL1 = character(r, vec2(-xMax+space, -2.5*side)+vec2(letterWidth+space, 0.0)*1.0, L, side);

    float maskSS = character(r, vec2(-xMax+space, -2.5*side)+vec2(letterWidth+space, 0.0)*2.0, S, side);

    float maskS = character(r, vec2(-xMax+space, -2.5*side)+vec2(letterWidth+space, 0.0)*2.0 + vec2(0.01*sin(2.1*t),0.012*cos(t)), S, side);

    float maskL2 = character(r, vec2(-xMax+space, -2.5*side)+vec2(letterWidth+space, 0.0)*3.0, L, side);

    float maskStripes = step(0.25, mod(rStripes.x - 0.5*t, 0.5));

    float i255 = 0.00392156862;

    vec3 blue = vec3(43., 172., 181.)*i255;

    vec3 pink = vec3(232., 77., 91.)*i255;

    vec3 dark = vec3(59., 59., 59.)*i255;

    vec3 light = vec3(245., 236., 217.)*i255;

    vec3 green = vec3(180., 204., 18.)*i255;

    vec3 pixel = blue;

    pixel = mix(pixel, light, maskGS);

    pixel = mix(pixel, light, maskSS);

    pixel -= 0.1*maskStripes;

    pixel = mix(pixel, green, maskG);

    pixel = mix(pixel, pink, maskL1*circ);

    pixel = mix(pixel, green, maskS);

    pixel = mix(pixel, pink, maskL2*(1.-circ));

    float dirt = pow(texture(iChannel0, 4.0*r).x, 4.0);

    pixel -= (0.2*dirt - 0.1)*(maskG+maskS); // dirt

    pixel -= smoothstep(0.45, 2.5, length(r));

    fragColor = vec4(pixel, 1.0);

    }

    #endif

  • 0 Comments

Want to leave a comment? Login or Register an account!