# Gradientes com shaders WebGL, explicados para designers

[Canonical HTML page](https://gradiently.design/pt-pt/guide/shader-gradients)

Alguns gradientes na web derivam, brilham e cintilam de uma forma que nenhuma imagem ou regra CSS consegue. São shaders. Eis como funcionam, em palavras que um designer pode usar.

## The short version

- Um gradiente WebGL é desenhado por um shader: um pequeno programa que corre no chip gráfico e decide a cor de cada píxel, muitas vezes várias vezes por segundo.
- Como um shader calcula a cor com matemática em vez de ler uma imagem guardada, pode juntar movimento, ruído, grão e luz em qualquer tamanho sem o ficheiro crescer.
- Os gradientes com shaders custam mais a correr do que os gradientes CSS, por isso um bom shader pausa quando está fora do ecrã, respeita a redução de movimento e tem uma alternativa em CSS simples.
- Usa CSS para transições paradas e movimento simples, e recorre a um shader quando a textura, a profundidade ou o movimento são o objetivo do design.

Um **gradiente WebGL** é um gradiente que o teu browser pinta com um shader: um programa curto que corre no chip gráfico (a GPU) e responde a uma pergunta para cada píxel do ecrã: “que cor tens agora?” Um gradiente CSS é uma receita fixa de cores e posições. Um shader é uma fórmula, por isso pode dobrar, granular, iluminar e mover a cor, e voltar a fazê-lo sessenta vezes por segundo, sem descarregar nenhuma imagem.

Não precisas de escrever shaders para desenhar com eles, mas saber o que conseguem e não conseguem fazer ajuda-te a preparar um briefing para um programador, a avaliar uma ferramenta e a decidir quando um simples [gradiente CSS](https://gradiently.design/pt-pt/guide/css-linear-gradient) é a melhor escolha.

## O que é um shader, em palavras simples

O WebGL é a forma de o browser falar com a GPU, e cada desenho em WebGL usa dois tipos de shader. O **vertex shader** posiciona as formas; num gradiente de ecrã inteiro, é só um retângulo a cobrir o canvas. O **fragment shader** faz o trabalho interessante: corre uma vez por píxel, recebe a posição desse píxel e o tempo atual, e devolve uma cor.

Como a GPU corre milhares destes pequenos programas em paralelo, uma fórmula que seria lenta em código comum fica barata o suficiente para ser redesenhada a cada fotograma. Eis um fragment shader completo para um gradiente de três cores cuja fronteira ondula suavemente ao longo do tempo, com uma linha de grão por cima.

```text
#version 300 es
precision highp float;

uniform vec2 u_resolution;   // canvas size in pixels
uniform float u_time;        // seconds since start
out vec4 outColor;

void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution;   // 0 to 1 across the canvas

  vec3 night  = vec3(0.07, 0.04, 0.18);  // #120a2e
  vec3 violet = vec3(0.49, 0.23, 0.93);  // #7c3aed
  vec3 coral  = vec3(0.98, 0.45, 0.45);  // #fa7373

  // A slow wave bends the blend so the colours drift.
  float wave = sin(u_time * 0.3 + uv.x * 3.0) * 0.08;
  float t = clamp(uv.y + wave, 0.0, 1.0);

  vec3 color = mix(night, violet, smoothstep(0.0, 0.6, t));
  color = mix(color, coral, smoothstep(0.55, 1.0, t));

  // Fine grain: a different random value per pixel, per frame.
  float n = fract(sin(dot(gl_FragCoord.xy + u_time, vec2(12.9898, 78.233))) * 43758.5453);
  color += (n - 0.5) * 0.04;

  outColor = vec4(color, 1.0);
}
```

Um fragment shader WebGL2 em GLSL. O `smoothstep` suaviza cada transição, a onda sinusoidal move a fronteira e as últimas linhas juntam grão, que também disfarça as bandas.

- Fundo noturno: #120a2e
- Violeta: #7c3aed
- Coral: #fa7373

As três cores do shader. O GLSL escreve a cor de 0 a 1 por canal, por isso #7c3aed passa a 124, 58 e 237 divididos por 255.

## Em que difere um gradiente com shader de um gradiente CSS

Um gradiente CSS é declarativo: listas cores e posições e o browser pinta-as. É a ferramenta certa na maior parte das vezes. Um shader é procedimental: descreves como a cor se deve comportar e a matemática produ-la. Essa diferença nota-se no que cada um consegue fazer e no que cada um custa.

| Aspeto | Gradiente CSS | Gradiente com shader |
| --- | --- | --- |
| Definido por | Cores, paragens e uma direção | Uma fórmula corrida por píxel |
| Movimento | Mover posição, tamanho ou propriedades personalizadas animadas | Tudo o que consigas exprimir em matemática, a cada fotograma |
| Textura e grão | Precisa de uma camada extra, como ruído SVG | Incluídos, e podem mover-se |
| Custo de execução | Muito baixo depois de pintado | Mantém a GPU ocupada enquanto anima |
| Funciona sem JavaScript | Sim | Não: é um script que prepara o canvas |
| Funciona em email | Em parte, consoante o cliente | Não |

Nenhum é melhor em geral. Cada um ganha num trabalho diferente.

A alternativa em CSS: `linear-gradient(180deg, #120a2e 0%, #4c1d95 40%, #7c3aed 62%, #fa7373 100%)`

A mesma paleta como gradiente CSS parado. Todos os fundos com shader devem incluir uma alternativa assim, mostrada antes de o shader arrancar e sempre que não possa correr.

## Porque é que os gradientes com shaders parecem vivos

Três técnicas fazem quase todo o trabalho, e vais ouvir os programadores referi-las. O **ruído** é aleatoriedade suave: em vez de uma transição reta, a cor segue uma paisagem de colinas suaves, e é por isso que os gradientes com shaders parecem orgânicos e não geométricos. O **domain warping** volta a alimentar o ruído com ele próprio, para a cor se dobrar e rodopiar como tinta na água. O **tempo** desliza a paisagem inteira, o que transforma um campo parado numa deriva.

- **O grão** pode ser gerado por píxel e por fotograma, por isso cintila como película em vez de ficar parado como uma textura posta por cima. Vê os [gradientes com grão](https://gradiently.design/pt-pt/guide/grainy-gradients).
- **A luz** pode ser simulada com poucas linhas: um reflexo que segue o ruído lê-se como brilho em seda ou vidro.
- **A resolução** é gratuita. A fórmula corre no tamanho que o ecrã tiver, por isso um gradiente com shader mantém-se nítido num telemóvel e num monitor 4K com o mesmo código.
- **As bandas** são mais fáceis de vencer, porque o shader pode juntar um pouco de dithering antes de a cor ser arredondada a 8 bits. As [bandas em gradientes](https://gradiently.design/pt-pt/guide/gradient-banding) explicam porque é que isso conta.

### Usa CSS quando

- O gradiente está parado, ou move-se de uma forma simples
- Fica por trás de muito texto numa página longa
- Tem de funcionar sem scripts, ou em email
- Precisas dele numa linha de código

### Usa um shader quando

- O movimento ou a textura são o objetivo do design
- É uma zona de destaque, não todas as secções
- Queres grão, brilho ou fluxo que o CSS não consegue imitar
- Consegues dar uma alternativa em CSS e uma pausa

## O custo de um fundo WebGL em movimento

Um shader animado mantém a GPU a trabalhar enquanto estiver no ecrã. Num computador de secretária, quase não se nota. Num telemóvel mais antigo, pode significar mãos quentes, bateria a descer e uma página que desliza com menos fluidez. O custo cresce com o número de píxeis e com a complexidade da fórmula, por isso um shader de ecrã inteiro num ecrã de alta densidade é o caso mais pesado. O guia [os gradientes abrandam um site?](https://gradiently.design/pt-pt/guide/css-gradient-performance) explica como medir.

1. **Mostra primeiro a alternativa em CSS** Pinta o gradiente parado de imediato e depois faz aparecer o canvas quando o shader tiver desenhado o primeiro fotograma. Ninguém deve ver uma caixa vazia.
2. **Verifica se o WebGL está disponível** Alguns browsers, dispositivos e definições de privacidade desativam-no. Se o contexto não puder ser criado, mantém a alternativa.
3. **Respeita a redução de movimento** Se o visitante pedir menos movimento, desenha um fotograma e para. A [redução de movimento](https://gradiently.design/pt-pt/guide/reduced-motion) explica a definição.
4. **Pausa quando não se vê** Para o ciclo de animação quando o canvas sai do ecrã ou o separador fica oculto.
5. **Renderiza numa resolução sensata** Os gradientes suaves raramente precisam de todos os píxeis do dispositivo. Desenhar a meia resolução e deixar o browser ampliar pode reduzir muito o trabalho, com uma desfocagem que não se distingue.

```js
const canvas = document.querySelector('#hero-canvas')
const gl = canvas.getContext('webgl2')
const still = matchMedia('(prefers-reduced-motion: reduce)').matches
let visible = true

if (!gl) {
  canvas.remove() // the CSS gradient behind it stays
} else {
  new IntersectionObserver(([entry]) => {
    visible = entry.isIntersecting
  }).observe(canvas)

  const frame = (ms) => {
    if (visible && !document.hidden) draw(gl, ms / 1000) // your draw call
    if (!still) requestAnimationFrame(frame)
  }
  requestAnimationFrame(frame)
}
```

As proteções em poucas linhas: uma alternativa quando falta o WebGL, um único fotograma para a redução de movimento e nada desenhado fora do ecrã ou num separador oculto.

Como referência, a [API WebGL no MDN](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) documenta todas as chamadas usadas aqui, e o [caniuse](https://caniuse.com/webgl2) acompanha o suporte do WebGL2 nos vários browsers.

## Desenhar um gradiente com shader: o que pôr no briefing

Podes dirigir um shader sem ler GLSL. Faz o briefing como farias o de uma matéria, não o de uma amostra de cor. Indica a cor de fundo e as três a cinco cores que vivem sobre ela, em hex. Descreve a estrutura: poças suaves, faixas longas, seda dobrada. Indica a velocidade em segundos por ciclo em vez de “lento”, porque lento significa coisas diferentes para pessoas diferentes. Diz quanto grão queres e se se move. Por fim, diz onde vai ficar o texto, para a parte mais agitada do campo se manter longe dele.

> **Avalia-o primeiro parado** Pausa o shader num fotograma ao acaso e olha para ele como imagem estática. Se um fotograma parado não ficar bem, o movimento não o vai salvar. O movimento deve acrescentar a um design que já funciona.

## Como a Gradiently desenha os Marks com shaders

Os Marks da Gradiently são gradientes com shaders. Cada Mark é uma receita de cores, até três matérias sobrepostas, luz, grão e movimento, e o Studio desenha-o ao vivo com WebGL2 por trás de tudo o que desenhas. Como é calculado e não guardado, o mesmo Mark é renderizado com nitidez em qualquer tamanho de plataforma e move-se quando exportas um vídeo em loop. O renderizador também sabe onde estão as tuas palavras, por isso consegue deslocar a parte mais calma do campo para trás delas. O guia [o que é um Mark](https://gradiently.design/pt-pt/guide/what-is-a-mark) explica a receita por inteiro.

Um Mark é uma receita de shader com um nome, um código e um dono. A sua página mostra-o a ser renderizado ao vivo.

## FAQ

### O que é um gradiente WebGL?

É um gradiente desenhado por um shader, um pequeno programa que corre no chip gráfico e calcula a cor de cada píxel. Como é uma fórmula, pode mover-se, juntar grão e ser renderizado em qualquer tamanho.

### Um gradiente com shader prejudica o desempenho?

Um shader parado ou em pausa custa pouco, mas um animado mantém a GPU ocupada enquanto está no ecrã. Pausa-o quando está fora de vista, desenha numa resolução sensata e mantém uma alternativa em CSS.

### Posso fazer um gradiente com shader sem programar?

Sim. Várias ferramentas de design geram fundos com shaders a partir de controlos, e a Forge da Gradiently constrói Marks, que são gradientes com shaders, a partir de uma frase ou controlo a controlo.

### Os gradientes com shaders respeitam as definições de redução de movimento?

Só se forem construídos para isso. Verifica a media query prefers-reduced-motion em JavaScript e desenha um único fotograma parado quando estiver ativa.

### O que é o GLSL?

O GLSL é a linguagem de shading que o WebGL usa. Os fragment shaders escritos nela decidem a cor de cada píxel, e o browser compila-os para o chip gráfico.
