CSS e web

Shaders de gradiente em WebGL, explicados para designers

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

GradientlyConta verificada do Gradiently··7 min de leitura
Capa: Thistle Mosaic · GR·2BAQ·CR

Resumindo

  • Um gradiente WebGL é desenhado por um shader: um pequeno programa que roda no chip gráfico e decide a cor de cada pixel, muitas vezes várias vezes por segundo.
  • Como o shader calcula a cor com matemática em vez de ler uma imagem armazenada, ele pode acrescentar movimento, ruído, grão e luz em qualquer tamanho sem que o arquivo cresça.
  • Gradientes shader custam mais para rodar do que gradientes CSS, então um bom shader pausa quando está fora da tela, respeita o movimento reduzido e tem uma alternativa simples em CSS.
  • Use CSS para misturas paradas e movimentos simples, e recorra a um shader quando a textura, a profundidade ou o movimento forem o centro do design.
Nesta página

Um gradiente WebGL é um gradiente que o seu navegador pinta com um shader: um programa curto que roda no chip gráfico (a GPU) e responde a uma pergunta para cada pixel da tela, “de que cor você é agora?”. Um gradiente CSS é uma receita fixa de cores e posições. Um shader é uma fórmula, então pode curvar, granular, iluminar e mover a cor, e fazer tudo de novo sessenta vezes por segundo, sem baixar nenhuma imagem.

Você não precisa escrever shaders para criar com eles, mas saber o que eles fazem e o que não fazem ajuda a orientar um desenvolvedor, avaliar uma ferramenta e decidir quando um simples gradiente CSS é a melhor escolha.

O que é um shader, em palavras simples

O WebGL é o jeito de o navegador conversar com a GPU, e todo desenho em WebGL usa dois tipos de shader. O vertex shader posiciona as formas; num gradiente de tela cheia, é só um retângulo cobrindo o canvas. O fragment shader faz o trabalho interessante: roda uma vez por pixel, recebe a posição desse pixel e o tempo atual e devolve uma cor.

Como a GPU roda milhares desses programinhas em paralelo, uma fórmula que seria lenta num código comum fica barata o bastante para ser redesenhada a cada quadro. Aqui está um fragment shader completo para um gradiente de três cores cuja fronteira balança suavemente com o 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. smoothstep suaviza cada transição, a onda senoidal move a fronteira e as últimas linhas acrescentam um grão que também esconde as faixas.
As três cores do shader. O GLSL escreve a cor de 0 a 1 por canal, então #7c3aed vira 124, 58 e 237 divididos por 255.

Como um gradiente shader difere de um gradiente CSS

Um gradiente CSS é declarativo: você lista cores e posições, e o navegador as pinta. É a ferramenta certa na maior parte do tempo. Um shader é procedural: você descreve como a cor deve se comportar, e a matemática a produz. Essa diferença aparece no que cada um consegue fazer e no quanto cada um custa.

AspectoGradiente CSSGradiente shader
Definido porCores, paradas e uma direçãoUma fórmula executada por pixel
MovimentoPosição ou tamanho em movimento, ou propriedades personalizadas animadasTudo o que você conseguir expressar em matemática, a cada quadro
Textura e grãoPrecisa de uma camada extra, como ruído em SVGEmbutidos, e podem se mover
Custo para rodarMuito baixo depois de pintadoMantém a GPU ocupada enquanto anima
Funciona sem JavaScriptSimNão: um script prepara o canvas
Funciona em e-mailEm parte, dependendo do clienteNão
Nenhum é melhor em geral. Cada um vence num trabalho diferente.
A alternativa em CSSlinear-gradient(180deg, #120a2e 0%, #4c1d95 40%, #7c3aed 62%, #fa7373 100%)
A mesma paleta como um gradiente CSS parado. Todo fundo shader deve vir com uma alternativa assim, mostrada antes de o shader começar e sempre que ele não puder rodar.

Por que os gradientes shader parecem vivos

Três técnicas fazem a maior parte do trabalho, e você vai ouvir os desenvolvedores falarem delas. O ruído é uma aleatoriedade suave: em vez de uma mistura reta, a cor segue uma paisagem de colinas suaves, e é por isso que os gradientes shader parecem orgânicos, e não geométricos. O domain warping realimenta o ruído nele mesmo, e a cor se dobra e rodopia 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 pixel e por quadro, então cintila como filme em vez de ficar parado como uma textura colocada por cima. Veja gradientes com grão.
  • A luz pode ser simulada com poucas linhas: um reflexo que acompanha o ruído parece o brilho da seda ou do vidro.
  • A resolução sai de graça. A fórmula roda no tamanho que a tela tiver, então um gradiente shader fica nítido num celular e num monitor 4K com o mesmo código.
  • As faixas são mais fáceis de vencer, porque o shader pode acrescentar um pouco de dithering antes de a cor ser arredondada para 8 bits. O guia de banding em gradientes explica por que isso importa.

Use CSS quando

  • O gradiente é parado, ou se move de um jeito simples
  • Ele fica atrás de muito texto numa página longa
  • Precisa funcionar sem scripts, ou em e-mail
  • Você precisa dele numa linha de código

Use um shader quando

  • O movimento ou a textura são o centro do design
  • É uma área principal, não todas as seções
  • Você quer grão, brilho ou fluxo que o CSS não simula
  • Você pode oferecer uma alternativa em CSS e uma pausa

O custo de um fundo WebGL em movimento

Um shader que anima mantém a GPU trabalhando enquanto está na tela. Num computador de mesa, mal se nota. Num celular mais antigo, pode significar mãos quentes, bateria acabando e uma página que rola com menos suavidade. O custo cresce com o número de pixels e a complexidade da fórmula, então um shader em tela cheia numa tela de alta densidade é o caso mais pesado. O guia gradientes deixam um site lento? mostra como medir.

  1. 1

    Mostre primeiro a alternativa em CSS

    Pinte o gradiente parado na hora e faça o canvas aparecer com um esmaecimento quando o shader tiver desenhado o primeiro quadro. Ninguém deve ver uma caixa vazia.

  2. 2

    Verifique se o WebGL está disponível

    Alguns navegadores, aparelhos e configurações de privacidade o desativam. Se o contexto não puder ser criado, mantenha a alternativa.

  3. 3

    Respeite o movimento reduzido

    Se a pessoa pede menos movimento, desenhe um quadro e pare. O guia de movimento reduzido explica a configuração.

  4. 4

    Pause quando não puder ser visto

    Pare o loop de animação quando o canvas sair da área visível ou a aba estiver oculta.

  5. 5

    Renderize numa resolução sensata

    Gradientes suaves raramente precisam de todos os pixels do aparelho. Desenhar na metade da resolução e deixar o navegador ampliar pode reduzir muito o trabalho, com um desfoque impossível de notar.

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 WebGL, um único quadro para o movimento reduzido e nada de desenhar fora da tela ou com a aba oculta.

Para referência, a API WebGL na MDN documenta cada chamada usada aqui, e o caniuse acompanha o suporte ao WebGL2 nos navegadores.

Como criar um gradiente shader: o que colocar no briefing

Você pode dirigir um shader sem ler GLSL. Faça o briefing como faria de um material, e não de uma amostra de cor. Informe a cor de fundo e as três a cinco cores que vivem sobre ela, em hex. Descreva a estrutura: poças suaves, faixas longas, seda dobrada. Diga a velocidade em segundos por ciclo em vez de “devagar”, porque devagar significa coisas diferentes para pessoas diferentes. Diga quanto grão e se ele se move. Por fim, diga onde o texto vai ficar, para que a parte mais agitada do campo fique longe dele.

Como o Gradiently desenha os Marks com shaders

Os Marks do Gradiently são gradientes shader. Cada Mark é uma receita de cores, até três materiais sobrepostos, luz, grão e movimento, e o Studio o desenha ao vivo com WebGL2 atrás de tudo o que você cria. Como é calculado, e não armazenado, o mesmo Mark é renderizado com nitidez em qualquer tamanho de plataforma e se move quando você exporta um vídeo em loop. O renderizador também sabe onde estão as suas palavras, então consegue mover a parte mais calma do campo para trás delas. O guia o que é um Mark explica a receita por completo.

Thistle Mosaic

GR·2BAQ·CR

Thistle Mosaic

Ver este Mark
Um Mark é uma receita de shader com nome, código e um único dono. A página dele o mostra sendo renderizado ao vivo.

Perguntas frequentes

O que é um gradiente WebGL?

É um gradiente desenhado por um shader, um pequeno programa que roda no chip gráfico e calcula a cor de cada pixel. Como é uma fórmula, ele pode se mover, ganhar grão e ser renderizado em qualquer tamanho.

Um gradiente shader prejudica o desempenho?

Um shader parado ou pausado custa pouco, mas um que anima mantém a GPU ocupada enquanto está na tela. Pause-o quando estiver fora da área visível, desenhe numa resolução sensata e mantenha uma alternativa em CSS.

Dá para fazer um gradiente shader sem programar?

Sim. Várias ferramentas de design geram fundos shader a partir de controles, e a Forge do Gradiently cria Marks, que são gradientes shader, a partir de uma frase ou controle por controle.

Os gradientes shader respeitam a configuração de movimento reduzido?

Só se forem feitos para isso. Verifique a media query prefers-reduced-motion em JavaScript e desenhe um único quadro parado quando ela estiver ativa.

O que é GLSL?

GLSL é a linguagem de shaders que o WebGL usa. Os fragment shaders escritos nela decidem a cor de cada pixel, e o navegador os compila para o chip gráfico.

Compartilhar este guia

Escrito pelo GradientlyConta verificada do Gradiently

A equipe por trás do Gradiently, uma ferramenta de design criada em torno dos Marks: gradientes vivos que fazem tudo o que você cria ter a sua cara.

Ver nosso perfil