RENDER EM TEMPO REAL — ESTE FUNDO É UM SHADER
Apostila completa · do zero ao muito avançado

3D em Tempo Real & Motion Graphics para Web

WebGL, Three.js, React Three Fiber, GLSL, WebGPU, GSAP, ScrollTrigger, Lottie e Rive — organizados como uma trilha única, com exercícios práticos e um plano direto para transformar isso em trabalho remunerado como creative developer.

20 módulos 5 partes PT-BR Pré-requisito: JS intermediário
ANTES DE COMEÇARComo usar esta apostila

Leia isto primeiro

Esta apostila foi desenhada para o mercado de trabalho, não para prova. Isso muda três coisas na forma de estudar:

  1. Cada módulo termina em código que roda. Não avance sem reproduzir os exemplos. O mercado paga por quem entrega, não por quem "entende o conceito".
  2. Os exercícios viram portfólio. Os projetos dos boxes Exercício foram escolhidos para se transformarem em peças publicáveis (CodePen, site pessoal, Awwwards).
  3. Os boxes roxos falam de dinheiro. Os boxes Mercado conectam a técnica ao que aparece em vaga, briefing e orçamento de freela.

Ferramentas mínimas

  • Editor: VS Code (extensões: Shader languages support, glsl-canvas, ESLint, Prettier).
  • Runtime: Node LTS + npm create vite@latest como base de todo projeto.
  • Playground: CodePen / CodeSandbox para experimentos; Shadertoy para shaders.
  • 3D DCC: Blender (gratuito) — obrigatório a partir do Módulo 09.
  • Navegador: Chrome com DevTools (aba Performance) e a extensão Spector.js para inspecionar draw calls.
Dica de estudo

O fundo do topo desta página é um fragment shader rodando ao vivo em WebGL puro, sem biblioteca. Abra o código-fonte desta apostila (Ctrl+U) e leia o script no final do arquivo depois de terminar o Módulo 05 — você vai entender cada linha.

MÓDULO 01Parte I — Fundações

Como a GPU desenha um frame

Objetivo: entender o pipeline gráfico de ponta a ponta. Tudo o que vem depois — Three.js, shaders, otimização — são camadas em cima deste modelo mental.

1.1 O contrato dos 16 milissegundos

Um site a 60fps precisa produzir um frame novo a cada 16,6 ms. Nesse orçamento cabem: seu JavaScript, o layout do navegador, a pintura da página e a renderização 3D. "Tempo real" significa exatamente isso: a imagem é calculada do zero, todo frame, dentro desse prazo — diferente de um vídeo, que é pré-renderizado.

Quem estoura o orçamento entrega jank (engasgo visível). Praticamente toda decisão técnica desta apostila deriva dessa restrição.

1.2 O pipeline gráfico em 6 estágios

Quando você "desenha um cubo", isto acontece na GPU:

  1. Vertex data: a malha vira arrays de números (posições, normais, UVs) enviados à GPU em buffers.
  2. Vertex shader: um programa seu roda uma vez por vértice e responde uma pergunta: "onde este vértice cai na tela?" (transformação por matrizes model → view → projection).
  3. Rasterização: a GPU liga os vértices em triângulos e descobre quais pixels cada triângulo cobre. Etapa fixa, você não programa.
  4. Fragment shader: outro programa seu roda uma vez por pixel coberto e responde: "que cor este pixel tem?" (luz, textura, efeitos).
  5. Testes: depth test (quem está na frente), blending (transparência).
  6. Framebuffer: o resultado vai para a tela — ou para uma textura (base do pós-processamento, Módulo 07).
Modelo mental

Vertex shader = ONDE. Fragment shader = QUAL COR. Grave esta frase. Ela resolve 80% da confusão inicial com shaders.

1.3 Por que a GPU é rápida (e burra)

A CPU tem poucos núcleos inteligentes; a GPU tem milhares de núcleos simples que executam o mesmo programa em paralelo sobre dados diferentes (SIMD). Uma tela full HD tem ~2 milhões de pixels: o fragment shader roda 2 milhões de vezes por frame, em paralelo. Consequências práticas:

  • Shaders não têm memória entre pixels: cada execução é isolada.
  • Enviar dados CPU→GPU é caro; manter dados na GPU é barato. (Por isso instancing vence loops de meshes — Módulo 08.)
  • if/else divergente em shader custa caro: os núcleos executam os dois ramos.

1.4 Sistemas de coordenadas e matrizes

A cadeia de transformações que todo vertex shader executa:

// espaço local → mundo → câmera → clip → tela
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
  • modelMatrix — posição/rotação/escala do objeto no mundo.
  • viewMatrix — o mundo visto da câmera (é a inversa da matriz da câmera).
  • projectionMatrix — perspectiva (ou ortográfica): converte o frustum da câmera no cubo de clip space (−1 a 1).

Você raramente escreve essas matrizes na mão — Three.js as fornece — mas entender a ordem é o que permite efeitos avançados como billboarding, projeção de texturas e distorção de vértices no espaço certo.

Exercício 01

Sem código: desenhe no papel o caminho de um vértice do cubo desde o array de posições até virar pixel. Depois explique em voz alta a diferença entre vertex e fragment shader como se ensinasse a alguém. Se travar, releia 1.2 — este módulo precisa estar sólido.

Mercado

Entrevistas para vagas de creative developer quase sempre incluem "explique o pipeline de renderização". Quem responde com o modelo ONDE/QUAL COR + as três matrizes passa da triagem técnica. É pergunta de eliminação, não de desempate.

MÓDULO 02Parte I — Fundações

WebGL puro: o mínimo que importa

Objetivo: montar UMA cena em WebGL cru para nunca mais ter medo do que o Three.js esconde. Você não vai trabalhar assim no dia a dia — mas quem já viu o metal por baixo depura qualquer coisa.

2.1 O que é WebGL, de fato

WebGL é uma API JavaScript que expõe a GPU no navegador, baseada em OpenGL ES. Ela é uma máquina de estados: você configura estado global (qual buffer está ativo, qual programa, qual textura) e dispara drawArrays/drawElements. WebGL2 (suportado em ~97% dos navegadores) adiciona instancing nativo, mais formatos de textura e GLSL ES 3.0.

2.2 Hello Triangle — o ritual de iniciação

main.js — triângulo em WebGL puroconst gl = canvas.getContext('webgl2');

// 1. shaders como strings
const vsSource = `#version 300 es
in vec2 aPos;
void main(){ gl_Position = vec4(aPos, 0.0, 1.0); }`;

const fsSource = `#version 300 es
precision highp float;
out vec4 outColor;
void main(){ outColor = vec4(1.0, 0.7, 0.3, 1.0); }`;

// 2. compilar e linkar
function compile(type, src){
  const s = gl.createShader(type);
  gl.shaderSource(s, src); gl.compileShader(s);
  if(!gl.getShaderParameter(s, gl.COMPILE_STATUS))
    throw new Error(gl.getShaderInfoLog(s));
  return s;
}
const prog = gl.createProgram();
gl.attachShader(prog, compile(gl.VERTEX_SHADER, vsSource));
gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, fsSource));
gl.linkProgram(prog);

// 3. dados de vértice → buffer na GPU
const verts = new Float32Array([ 0,0.8,  -0.8,-0.8,  0.8,-0.8 ]);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(prog, 'aPos');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);

// 4. desenhar
gl.useProgram(prog);
gl.drawArrays(gl.TRIANGLES, 0, 3);

São ~40 linhas para um triângulo. É exatamente por isso que Three.js existe — e é exatamente isso que ele faz por baixo, milhares de vezes por frame.

2.3 Os conceitos que você leva para sempre

TermoO que éEm Three.js vira…
AttributeDado por vértice (posição, UV, normal)BufferAttribute
UniformValor global do frame (tempo, mouse, matrizes)material.uniforms
VaryingValor interpolado do vertex → fragmentvarying/out-in no GLSL
VAO/BufferMemória de vértices na GPUBufferGeometry
ProgramPar vertex+fragment compiladoMaterial (cada material = 1 programa)
Draw callUma ordem de desenho à GPU1 por mesh visível — a métrica nº 1 de performance
Exercício 02
  1. Reproduza o Hello Triangle e mude a cor via uniform (gl.uniform3f) animada com requestAnimationFrame.
  2. Adicione um segundo attribute aColor por vértice, passe como varying e observe a interpolação (o famoso triângulo RGB).
MÓDULO 03Parte I — Fundações

Three.js essencial

Objetivo: dominar o vocabulário que 90% das vagas de web 3D pedem: cena, câmera, luz, material, geometria, loop, resize, interação e carregamento de modelos.

3.1 A cena mínima profissional

scene.js — boilerplate que você vai digitar mil vezesimport * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(35, innerWidth/innerHeight, 0.1, 100);
camera.position.set(3, 2, 5);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2)); // NUNCA sem o clamp
document.body.appendChild(renderer.domElement);

const mesh = new THREE.Mesh(
  new THREE.TorusKnotGeometry(0.8, 0.28, 128, 32),
  new THREE.MeshStandardMaterial({ color: '#8b7cff', roughness: 0.25 })
);
scene.add(mesh);

scene.add(new THREE.AmbientLight('#ffffff', 0.4));
const key = new THREE.DirectionalLight('#ffb454', 2.5);
key.position.set(4, 5, 3);
scene.add(key);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
  const t = clock.getElapsedTime();
  mesh.rotation.y = t * 0.4;      // tempo absoluto, não += (frame-rate independent)
  controls.update();
  renderer.render(scene, camera);
});

addEventListener('resize', () => {
  camera.aspect = innerWidth/innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});
Erros clássicos de iniciante
  • Tela preta? Checklist: há luz na cena? A câmera olha para o objeto? O objeto está dentro de near/far? Chamou render()?
  • Animar com += 0.01 deixa a velocidade dependente do FPS. Use tempo do Clock ou multiplique por delta.
  • setPixelRatio sem clamp renderiza 9x mais pixels num celular 3x — a causa nº 1 de site 3D lento em mobile.

3.2 Materiais: qual usar quando

MaterialCustoUso
MeshBasicMaterialBaixíssimoSem luz: UI 3D, wireframes, fundos, objetos emissivos simples
MeshLambert/PhongBaixoEstética estilizada/retro, mobile agressivo
MeshStandardMaterialMédioPBR padrão do mercado: metalness/roughness + env map
MeshPhysicalMaterialAltoVidro (transmission), verniz (clearcoat), iridescência, sheen
ShaderMaterialVocê decideTudo que é premiado — Módulos 05–07

Para PBR convincente, environment map é mais importante que luzes: carregue um HDRI com RGBELoader e defina scene.environment. É o segredo do "look de produto" (e-commerce, configuradores).

3.3 Carregando modelos GLTF

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

const draco = new DRACOLoader().setDecoderPath('/draco/');
const loader = new GLTFLoader().setDRACOLoader(draco);

loader.load('/models/sneaker.glb', (gltf) => {
  scene.add(gltf.scene);
  // animações embutidas:
  const mixer = new THREE.AnimationMixer(gltf.scene);
  gltf.animations.forEach(clip => mixer.clipAction(clip).play());
});

GLTF/GLB é o formato do mercado web. Esqueça OBJ e FBX para entrega final. Compressão e otimização de assets são o Módulo 09.

3.4 Interação: raycasting

const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

addEventListener('pointermove', e => {
  pointer.set((e.clientX/innerWidth)*2-1, -(e.clientY/innerHeight)*2+1);
});

// dentro do loop:
raycaster.setFromCamera(pointer, camera);
const hits = raycaster.intersectObjects(scene.children, true);
document.body.style.cursor = hits.length ? 'pointer' : 'default';
Exercício 03 — primeiro projeto de portfólio

Visualizador de produto: um objeto (pode ser um GLB gratuito do Sketchfab/Poly Haven) com HDRI, órbita suave, 3 botões HTML que trocam a cor do material com transição, e hover que destaca a peça via raycasting. Publique no CodePen/Vercel. Este exato padrão é vendido como "configurador 3D" por R$ 4–15 mil no mercado brasileiro de freela.

Mercado

Palavras que aparecem em vagas e que este módulo cobre: Three.js, GLTF, PBR, raycasting, render loop, responsive canvas. Coloque-as literalmente no seu LinkedIn/CV junto do link do exercício — recrutador filtra por keyword antes de olhar talento.

MÓDULO 04Parte II — 3D Avançado

React Three Fiber & ecossistema

Objetivo: dominar o stack que domina as vagas: R3F + drei + zustand. A maioria dos estúdios digitais (e quase todos os sites premiados recentes) monta 3D declarativo dentro de React/Next.js.

4.1 A ideia central: cena como componente

React Three Fiber (R3F) é um renderer de React para Three.js. Cada elemento JSX em minúsculas vira um objeto Three.js; props viram propriedades; args viram argumentos do construtor. Não há custo extra de performance no loop — o React só roda quando o estado muda; a renderização 3D continua no rAF.

App.jsximport { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Environment, ContactShadows } from '@react-three/drei';
import { useRef, useState } from 'react';

function Knot(){
  const ref = useRef();
  const [hover, setHover] = useState(false);
  useFrame((state, delta) => {
    ref.current.rotation.y += delta * 0.4;
    // interpolação exponencial estável em qualquer FPS:
    ref.current.scale.setScalar(THREE.MathUtils.damp(
      ref.current.scale.x, hover ? 1.15 : 1, 8, delta));
  });
  return (
    <mesh ref={ref}
      onPointerOver={() => setHover(true)}
      onPointerOut={() => setHover(false)}>
      <torusKnotGeometry args={[0.8, 0.28, 128, 32]} />
      <meshStandardMaterial color="#8b7cff" roughness={0.25} />
    </mesh>
  );
}

export default function App(){
  return (
    <Canvas camera={{ position: [3,2,5], fov: 35 }} dpr={[1,2]}>
      <Knot />
      <Environment preset="city" />
      <ContactShadows opacity={0.5} blur={2} />
      <OrbitControls enableDamping />
    </Canvas>
  );
}

Note o que sumiu: renderer, resize, loop, dispose. R3F gerencia tudo — inclusive descarte automático de memória quando componentes desmontam, algo doloroso no Three.js puro.

4.2 As três regras de ouro do R3F

  1. Nunca use setState dentro de useFrame. Mutação direta de refs para o que muda a cada frame; estado React só para eventos discretos (clique, troca de cena).
  2. Nunca crie objetos no corpo do componente sem memo. new Vector3() por render = lixo para o GC. Use useMemo, constantes fora do componente, ou os helpers de string do R3F.
  3. Estado global fora do React: zustand com getState()/subscribe transiente dentro do useFrame evita re-renders. É o padrão dos exemplos oficiais da Poimandres.

4.3 drei: o cinto de utilidades

@react-three/drei condensa anos de código de produção. Os que mais caem em projeto real:

  • <Environment />, <ContactShadows />, <AccumulativeShadows /> — o "look caro" instantâneo.
  • <ScrollControls /> + useScroll — scrollytelling nativo (Módulo 15).
  • <Text /> (SDF, nítido em qualquer zoom), <Html /> (DOM ancorado no 3D).
  • useGLTF (com draco embutido) + gltfjsx, a CLI que converte um GLB em componente JSX editável — ferramenta de produtividade absurda.
  • <Instances />, <Merged />, <Detailed /> (LOD), <PerformanceMonitor /> — performance declarativa.
  • <MeshTransmissionMaterial /> — o vidro grosso dispersivo que você vê em todo site premiado.

4.4 Física e o resto do ecossistema

  • @react-three/rapier — física (WASM) declarativa: <RigidBody> em volta do mesh e pronto.
  • @react-three/postprocessing — efeitos com merge automático de passes (Módulo 07).
  • leva — painel de controles para tunar valores ao vivo. Use em TODO projeto; metade do trabalho de creative dev é ajuste fino.
  • maath — helpers matemáticos (damp, random em superfícies).
Exercício 04

Refaça o visualizador de produto do Módulo 03 em R3F + drei: useGLTF, Environment com HDRI, ContactShadows, troca de material via zustand, hover com damp, painel leva para roughness/metalness. Compare a contagem de linhas com a versão vanilla e escreva um post curto "Three.js vs R3F" no LinkedIn com os dois links — conteúdo técnico comparativo performa muito bem para atrair recrutador.

MÓDULO 05Parte II — 3D Avançado

Shaders GLSL do zero ao ruído

Objetivo: escrever vertex e fragment shaders próprios. Esta é a linha que separa "usa Three.js" de "creative developer". Salários e orçamentos mudam de faixa aqui.

5.1 GLSL em 10 minutos

GLSL é C simplificado, fortemente tipado, sem ponteiros. O que você precisa:

float a = 1.0;            // SEMPRE com ponto — 1 é int e dá erro
vec2 uv = vec2(0.5);      // vec2(0.5, 0.5)
vec3 cor = vec3(uv, 1.0); // construção por composição
float r = cor.r;           // swizzle: .xyzw .rgba .stpq
vec2 inv = cor.yx;         // reordenar componentes é grátis

As funções que constroem 95% dos efeitos — decore-as:

FunçãoO que fazUso típico
mix(a,b,t)Interpolação linearGradientes, blending de cores
step(edge,x)0 ou 1 (corte seco)Listras, máscaras duras
smoothstep(a,b,x)0→1 suaveBordas antialiased, glow, máscaras
fract(x)Parte fracionáriaRepetição/tiling de padrões
length(v) / distance(a,b)DistânciaCírculos, campos radiais, SDFs
dot(a,b)Produto escalarIluminação (N·L), fresnel
clamp, abs, sin, pow, floorModelagem de curvas e ritmo

5.2 ShaderMaterial no Three.js

material.jsconst material = new THREE.ShaderMaterial({
  uniforms: {
    uTime:  { value: 0 },
    uMouse: { value: new THREE.Vector2() },
    uColorA:{ value: new THREE.Color('#8b7cff') },
    uColorB:{ value: new THREE.Color('#ffb454') },
  },
  vertexShader: /* glsl */`
    uniform float uTime;
    varying vec2 vUv;
    varying float vWave;
    void main(){
      vUv = uv;
      vec3 p = position;
      float wave = sin(p.x * 3.0 + uTime * 2.0) * 0.15;
      p.z += wave;                 // deslocamento de vértice
      vWave = wave;                // manda pro fragment
      gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
    }`,
  fragmentShader: /* glsl */`
    uniform vec3 uColorA, uColorB;
    varying vec2 vUv;
    varying float vWave;
    void main(){
      vec3 col = mix(uColorA, uColorB, vUv.y + vWave);
      gl_FragColor = vec4(col, 1.0);
    }`
});
// no loop: material.uniforms.uTime.value = clock.getElapsedTime();

position, uv, normal e as matrizes já chegam prontos no ShaderMaterial — Three.js injeta. Com RawShaderMaterial nada é injetado.

5.3 Desenhando com matemática: o kit SDF 2D

// círculo com borda suave e SEM serrilhado:
float circle(vec2 uv, vec2 c, float r){
  float d = distance(uv, c);
  return smoothstep(r, r - fwidth(d)*1.5, d);
}
// grid de repetição:
vec2 gv = fract(uv * 10.0) - 0.5;
// listras animadas:
float stripes = step(0.5, fract(uv.x * 8.0 + uTime));

5.4 Ruído: a alma do orgânico

Quase todo efeito "vivo" (água, fumaça, aurora, terreno, distorção) é ruído — funções pseudoaleatórias suaves. A hierarquia:

  1. random(hash): fract(sin(dot(uv, vec2(12.9898,78.233))) * 43758.5453) — ruído branco, base de tudo.
  2. Value/Perlin/Simplex noise: interpolações suaves do hash. Na prática: copie as implementações clássicas de Ashima/Ian McEwan (padrão da indústria, licença MIT) ou use o pacote glsl-noise/módulos do lygia.
  3. FBM (Fractal Brownian Motion): soma de oitavas de ruído — nuvens, mármore, terrenos:
float fbm(vec2 p){
  float v = 0.0, a = 0.5;
  for(int i = 0; i < 5; i++){
    v += a * snoise(p);
    p *= 2.0; a *= 0.5;
  }
  return v;
}
// domain warping — o truque dos shaders premiados:
float n = fbm(uv + fbm(uv + uTime * 0.1));
Método de estudo

Estude em The Book of Shaders (tem tradução PT) na ordem: shaping functions → cores → formas → ruído. Depois passe uma semana no Shadertoy só lendo shaders alheios e comentando linha a linha. Ler shader é uma habilidade separada de escrever.

Exercício 05
  1. Plano subdividido (256×256) com ondas por vertex shader + cor por altura — um "oceano estilizado".
  2. Fragment shader fullscreen com FBM + domain warping + paleta com mix em 3 cores — um fundo "aurora". (O hero desta apostila é exatamente isso; compare com o código no fim do arquivo.)
  3. Publique os dois no Shadertoy/CodePen com descrição do processo.
Mercado

"GLSL" no CV com portfólio de shaders reais coloca você em outra prateleira: vagas de creative developer em estúdios (nacionais e internacionais/remotas) listam shaders como diferencial decisivo, e freelas de "fundo interativo tipo Stripe/Linear" são vendidos a partir de R$ 3–8 mil por peça.

MÓDULO 06Parte II — 3D Avançado

Técnicas avançadas de shader

Objetivo: os padrões que aparecem em site premiado: fresnel, dissolve, partículas em GPU (GPGPU/FBO), raymarching e patch de materiais nativos.

6.1 Fresnel — o efeito mais rentável por linha de código

Bordas de objetos brilhando conforme o ângulo de visão. Base de hologramas, force fields, vidro estilizado, rim light:

// vertex: passe normal e posição no espaço do mundo
vNormal = normalize(normalMatrix * normal);
vViewDir = normalize(cameraPosition - (modelMatrix * vec4(position,1.0)).xyz);

// fragment:
float fresnel = pow(1.0 - clamp(dot(vNormal, vViewDir), 0.0, 1.0), 3.0);
gl_FragColor = vec4(uColor * fresnel, fresnel);

6.2 Dissolve com ruído

O clássico de transição de objetos (aparecer/desaparecer "queimando"):

float n = snoise(vUv * 6.0);
if(n < uProgress) discard;                    // recorta o pixel
float edge = smoothstep(uProgress, uProgress + 0.08, n);
vec3 col = mix(uEdgeColor * 3.0, uBaseColor, edge); // borda emissiva p/ bloom

Anime uProgress de 0→1 com GSAP (Módulo 13) e você tem uma transição de cena vendável.

6.3 Partículas em GPU: de Points ao GPGPU

Nível 1 — THREE.Points + shader

Milhares de pontos numa BufferGeometry, animados no vertex shader por atributo aleatório + uTime. Custo quase zero de CPU; suficiente para poeira, estrelas, campos de fundo.

gl_PointSize = uSize * (1.0 / -viewPosition.z); // atenuação por distância
// no fragment, desenhe um disco suave:
float d = length(gl_PointCoord - 0.5);
float alpha = smoothstep(0.5, 0.1, d);

Nível 2 — GPGPU / FBO particles (o "muito avançado")

Para partículas com física e memória (fluidos, enxames, morphing entre formas), o estado (posição/velocidade) vive em texturas: cada pixel de uma textura RGBA float = uma partícula. Um shader de simulação lê a textura do frame anterior e escreve a próxima (ping-pong entre dois render targets); o shader de render lê a posição da textura para posicionar cada ponto.

  • No Three.js: GPUComputationRenderer (addon oficial) monta o ping-pong para você.
  • Padrões clássicos: curl noise para fluxo orgânico, atração ao mouse, morphing entre nuvens de pontos amostradas de modelos (MeshSurfaceSampler).
  • 500 mil+ partículas a 60fps é factível em desktop médio.

6.4 Raymarching: cenas sem geometria

Técnica de renderizar num único quad fullscreen: para cada pixel, "marche" um raio pela cena definida por SDFs 3D (funções de distância). É como se faz metaballs/blobs gosmentos, fractais e nuvens volumétricas:

float sdSphere(vec3 p, float r){ return length(p) - r; }
float smin(float a, float b, float k){ // blend "gosmento" entre formas
  float h = clamp(0.5 + 0.5*(b-a)/k, 0.0, 1.0);
  return mix(b, a, h) - k*h*(1.0-h);
}
// loop de marcha:
for(int i=0; i<64; i++){
  float d = map(ro + rd * t);   // map() = sua cena em SDFs
  if(d < 0.001 || t > 20.0) break;
  t += d;
}

Referência canônica: os artigos de Inigo Quilez (iquilezles.org) — a biblioteca de SDFs dele é o dicionário da área.

6.5 Estender materiais nativos: onBeforeCompile e CSM

O segredo sujo dos profissionais: você raramente reescreve PBR do zero. Em vez disso, injeta código no shader do MeshStandardMaterial via material.onBeforeCompile (substituindo chunks como #include <begin_vertex>) — ou usa a lib three-custom-shader-material, que torna isso ergonômico. Resultado: deformação por vértice + ruído mantendo sombras, env map e toda a iluminação PBR.

Exercício 06 — peça de destaque do portfólio

Escolha UM e leve à excelência: (a) galáxia de 200k partículas com curl noise reagindo ao mouse via GPGPU; (b) blob raymarched com smin + fresnel como hero de landing page; (c) modelo GLTF com dissolve emissivo disparado por clique. Grave um vídeo de 20s do resultado — vídeo curto de shader é o formato que mais gera contato de trabalho no X/LinkedIn/Instagram.

MÓDULO 07Parte II — 3D Avançado

Pós-processamento & render targets

Objetivo: o "acabamento de cinema": bloom, DOF, grain, aberração cromática, transições — e a base técnica de tudo isso, o render target.

7.1 Render target: renderizar para textura

Um WebGLRenderTarget é um framebuffer fora da tela. Renderize a cena nele e o resultado vira uma textura que outro shader pode ler. Disso derivam: pós-processamento, espelhos/portais, refração de "vidro" lendo a cena atrás, GPGPU (Módulo 06), sombras e transições entre cenas.

const rt = new THREE.WebGLRenderTarget(w, h);
renderer.setRenderTarget(rt);
renderer.render(sceneA, camera);   // vai para a textura
renderer.setRenderTarget(null);
quadMaterial.uniforms.tScene.value = rt.texture;
renderer.render(quadScene, quadCam); // quad fullscreen aplica o efeito

7.2 O stack moderno: pmndrs/postprocessing

Em produção, use a lib postprocessing (ou @react-three/postprocessing no R3F): ela funde vários efeitos em um único shader, muito mais rápido que o EffectComposer clássico encadeado.

import { EffectComposer, Bloom, DepthOfField, Noise, ChromaticAberration, Vignette }
  from '@react-three/postprocessing';

<EffectComposer>
  <Bloom intensity={0.9} luminanceThreshold={1.0} mipmapBlur />
  <DepthOfField focusDistance={0.01} focalLength={0.05} bokehScale={3} />
  <ChromaticAberration offset={[0.0012, 0.0008]} />
  <Noise opacity={0.04} />
  <Vignette darkness={0.6} />
</EffectComposer>
Bloom seletivo — o jeito certo

Com luminanceThreshold: 1, só brilha o que tem cor acima de 1.0. Então: materiais emissivos com emissiveIntensity alto (ou cores como [3, 1.2, 0.5]) brilham; o resto não. Nada de camadas/máscaras complicadas.

7.3 Efeitos custom e transições de cena

Um efeito próprio na lib postprocessing é uma classe Effect com um fragment que recebe inputColor e UV. Receita de transição de cena premiada: renderize cena A e cena B em dois render targets e misture no shader com uma máscara de ruído/wipe controlada por uProgress — o mesmo dissolve do Módulo 06, agora em tela cheia.

  • Color grading: LUTs via LUT3DEffect — exporte a LUT do DaVinci/Photoshop e o site ganha o mesmo grade do vídeo da campanha.
  • Tone mapping: ACESFilmic (padrão do Three) + trabalhar luz em valores HDR é o que faz a cena parecer "renderizada de verdade".
  • Custo: cada passe fullscreen roda por pixel; em mobile, limite-se a bloom + vignette + grain. DOF e SSR são os vilões.
Exercício 07

Monte uma cena noturna: objetos emissivos + bloom seletivo + grain + vignette + leve aberração cromática, com painel leva expondo os parâmetros. Depois implemente uma transição por dissolve entre duas cenas disparada por botão. Este combo é literalmente o visual de metade dos sites do Awwwards.

MÓDULO 08Parte II — 3D Avançado

Performance: 60fps de verdade

Objetivo: a habilidade que clientes pagam caro e poucos têm: fazer rodar liso em celular médio. Metodologia de diagnóstico + as otimizações por ordem de impacto.

8.1 Diagnóstico antes de otimizar

  1. Meça: stats.js/r3f-perf na tela, aba Performance do DevTools para ver se o gargalo é CPU (script) ou GPU (frames longos sem script).
  2. Inspecione: Spector.js mostra cada draw call do frame. renderer.info mostra calls, triângulos, texturas e programas.
  3. Teste no pior dispositivo-alvo, não no seu desktop. Chrome DevTools → CPU throttling 4x + um Android de verdade.
SintomaGargalo provávelAtaque
CPU alta, GPU folgadaDraw calls / JS no loopInstancing, merge, cortar trabalho por frame
GPU alta em tela grandeFragment-bound (fill rate)Baixar DPR, simplificar shaders/pós, menos transparência
GPU alta mesmo em tela pequenaVertex-boundMenos polígonos, LOD, frustum culling
Travadas periódicasGC / upload de texturaZerar alocações no loop, pré-aquecer com compile()

8.2 Draw calls: a métrica rainha

Cada mesh visível = 1 draw call, e cada call tem custo fixo de CPU. Alvo prático: < 100 calls em mobile, < 300 em desktop.

  • InstancedMesh: N cópias da mesma geometria+material em 1 call. 10.000 cubos, uma chamada. Atualize matrizes via setMatrixAt + instanceMatrix.needsUpdate; varie cor por instância com instanceColor; varie movimento por instância com atributos custom no shader.
  • Merge: BufferGeometryUtils.mergeGeometries para estática que compartilha material.
  • Atlas de texturas para permitir merge/instancing entre objetos "diferentes".

8.3 A lista de impacto (em ordem)

  1. renderer.setPixelRatio(Math.min(devicePixelRatio, 2)) — e considere 1.5 em cenas pesadas. Ganho imediato de até 4x em fill rate.
  2. Instancing/merge até draw calls caberem no alvo.
  3. Texturas: KTX2/Basis (Módulo 09), potências de 2, mipmaps, nunca maiores que o necessário na tela.
  4. Sombras: desligue o que puder; shadow.mapSize 1024 no máximo em mobile; sombras estáticas → bake ou ContactShadows.
  5. Transparência e overdraw: camadas de alpha empilhadas destroem mobile. Use alphaTest/discard quando der.
  6. Zero alocação no loop: reuse Vector3/Matrix4 pré-criados; nada de new, map, spread dentro do rAF.
  7. Dispose: geometry.dispose(), material.dispose(), texture.dispose() ao remover objetos — vazamento de VRAM derruba a aba em SPAs.
  8. Qualidade adaptativa: meça FPS por alguns segundos e degrade (DPR, pós, sombras) — <PerformanceMonitor> do drei faz isso declarativamente.
  9. Pré-compilação: renderer.compile(scene, camera) (ou compileAsync) antes de revelar a cena, para evitar o engasgo do primeiro frame.
  10. Demanda: cena parada não precisa renderizar — frameloop="demand" no R3F ou invalidação manual.
Antipadrão de portfólio

Site lindo que derrete o celular do recrutador = reprovação silenciosa. Todo projeto público seu deve abrir em um Android intermediário sem aquecer. Coloque no rodapé do case: "60fps em Moto G / draw calls: 47" — isso comunica senioridade instantaneamente.

Exercício 08

Pegue seu projeto do Módulo 06 e faça uma auditoria escrita: draw calls antes/depois, DPR, tamanho de texturas, FPS em throttling 4x. Otimize até dobrar o FPS ou cortar as calls pela metade e publique o antes/depois como estudo de caso. Auditorias assim são um serviço vendável por si só (retainers de performance).

MÓDULO 09Parte II — 3D Avançado

Assets: Blender → Web

Objetivo: o pipeline de conteúdo — modelar/otimizar no Blender, bake de luz, compressão, e entregar um GLB que carrega em 2s no 4G.

9.1 O que você precisa do Blender (e só isso)

Você não precisa virar 3D artist. Precisa de: navegação, modelagem básica/edição de malha, modificador Decimate, UV unwrap, materiais Principled BSDF, e exportação GLTF. Isso é ~2 semanas de prática direcionada (o clássico "donut" do Blender Guru + um curso curto de low poly).

9.2 Regras de asset para tempo real

  • Orçamento de polígonos: herói de cena 20–80k tris; objetos de apoio 1–5k. Use Decimate/retopologia.
  • Aplicar transformações (Ctrl+A) e escala 1,1,1 antes de exportar — evita 90% dos bugs de "modelo gigante/deitado".
  • Materiais: só Principled BSDF vira PBR no GLTF. Procedurais do Blender NÃO exportam — precisam de bake para textura.
  • Bake de iluminação: para cenas estáticas, asse luz+AO em lightmap e use MeshBasicMaterial com a textura — visual de render offline com custo de material sem luz. É assim que ambientes ricos rodam em mobile.

9.3 Compressão: gltf-transform é seu melhor amigo

terminal — pipeline de otimização padrãonpm i -g @gltf-transform/cli

# tudo de uma vez: dedup, prune, resize, Draco + KTX2
gltf-transform optimize in.glb out.glb --compress draco --texture-compress ktx2

# ou por etapas, com controle:
gltf-transform resize in.glb tmp.glb --width 1024 --height 1024
gltf-transform etc1s tmp.glb tmp2.glb        # KTX2 universal (UI/albedo)
gltf-transform draco tmp2.glb out.glb        # geometria ~10x menor
  • Draco: comprime geometria (10–20x). Custo: decodificação em WASM no load.
  • Meshopt: alternativa que decodifica quase instantaneamente — melhor para muitos modelos pequenos.
  • KTX2/Basis: textura comprimida que permanece comprimida na VRAM (JPG/PNG são descomprimidos na GPU!). Corta uso de memória em 4–8x — o upgrade mais subestimado para mobile. Carregue com KTX2Loader.
  • Alvo de entrega: herói de landing page ≤ 2–4 MB total de assets 3D.

9.4 Onde conseguir assets (com licença limpa)

  • Poly Haven — HDRIs, texturas e modelos CC0 (uso comercial livre). Base de todo freela.
  • Sketchfab — filtre por licença CC; cheque atribuição antes de usar em cliente.
  • Quaternius / Kenney — packs low poly CC0 excelentes para protótipos e jogos.
Exercício 09

Modele (ou baixe e edite) um objeto de produto no Blender, asse AO, exporte GLB, rode o pipeline gltf-transform e documente: tamanho antes/depois, tempo de load em 4G simulado. Meta: sair de >20 MB para <3 MB sem perda visível.

MÓDULO 10Parte II — 3D Avançado

WebGPU & TSL — o futuro (que já começou)

Objetivo: entender o que muda com WebGPU, escrever materiais em TSL (a nova linguagem de nós do Three.js) e usar compute shaders — o assunto que separa candidatos em 2026.

10.1 Por que WebGPU existe

WebGL espelha OpenGL de 2007: máquina de estados global, sem acesso a recursos modernos da GPU. WebGPU é uma API nova (inspirada em Vulkan/Metal/DX12) com:

  • Compute shaders: GPU para cálculo genérico de verdade — partículas, física, IA — sem a gambiarra de texturas do GPGPU clássico.
  • Menos overhead de CPU: pipelines pré-validados, bind groups — milhares de objetos ficam baratos.
  • WGSL: nova linguagem de shader (substitui GLSL nesse contexto).
  • Suporte: Chrome/Edge estáveis há tempo, Safari e Firefox chegando/chegado nas versões recentes — mas em produção você sempre mantém fallback WebGL2 por enquanto.

10.2 WebGPURenderer no Three.js

import * as THREE from 'three/webgpu';

const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();   // assíncrono!
// se WebGPU não existir, este renderer cai sozinho para WebGL2 —
// e é por isso que os materiais agora são escritos em TSL, não GLSL.

10.3 TSL: shaders em JavaScript

TSL (Three.js Shading Language) descreve o shader como grafo de nós em JS; o Three compila para WGSL ou GLSL conforme o backend. É o novo caminho oficial — ShaderMaterial GLSL continua funcionando no WebGL, mas o ecossistema novo é TSL:

import { uniform, uv, sin, time, mix, color, positionLocal, vec4 }
  from 'three/tsl';

const uColorA = uniform(color('#8b7cff'));
const uColorB = uniform(color('#ffb454'));

const mat = new THREE.MeshStandardNodeMaterial();
// deslocamento de vértice + cor — mantendo TODO o PBR:
const wave = sin(positionLocal.x.mul(3).add(time.mul(2))).mul(0.15);
mat.positionNode = positionLocal.add(vec4(0, 0, wave, 0).xyz);
mat.colorNode = mix(uColorA, uColorB, uv().y.add(wave));

Repare: é o exercício do Módulo 05, agora com iluminação PBR de graça e portátil entre backends. TSL também elimina classes inteiras de bug de string GLSL e permite compor materiais como funções JS.

10.4 Compute: partículas sem ping-pong

Com Fn().compute(count) em TSL, você escreve a simulação de N partículas lendo/escrevendo storage buffers diretamente — o GPGPU do Módulo 06 sem texturas-gambiarra, com milhões de partículas. A estrutura: um instancedArray de posições/velocidades, um kernel de update rodado por frame com renderer.compute(), e um material de pontos/instâncias lendo o buffer.

Mercado

Em entrevista, saber articular "WebGPU: compute, menos overhead, WGSL; TSL como camada portátil; fallback WebGL2 em produção" já coloca você à frente — pouquíssimos candidatos estudaram isso. Um demo de compute particles no portfólio é sinal forte de atualização técnica.

Exercício 10

Porte seu shader do Módulo 05 para TSL com MeshStandardNodeMaterial e rode com WebGPURenderer. Depois siga o exemplo oficial webgpu_compute_particles do repositório do Three.js e modifique-o (força do mouse, cores por velocidade).

MÓDULO 11Parte III — Motion

Princípios de animação aplicados a interface

Objetivo: o olho. Ferramenta sem princípio produz movimento amador. Aqui traduzimos os 12 princípios da Disney + prática de motion design para o vocabulário de web.

11.1 Os princípios que importam na web

PrincípioTradução para UI/web
Easing (slow in/out)NADA se move linearmente. Entradas desaceleram (ease-out), saídas aceleram (ease-in). Linear só para rotação contínua e marquees.
TimingMicro-interações 150–250ms; transições de layout 300–500ms; momentos hero 600–1200ms. Mais que isso vira lentidão percebida.
AntecipaçãoUm recuo de 2–4% antes do movimento principal (botão que "arma" antes de pular).
Follow-through / overshootElementos passam 2–8% do alvo e voltam (back/elastic ease) — vida sem exagero.
Staggering (ação secundária)Itens de lista entram com 40–90ms de atraso entre si, nunca todos juntos.
ArcosMovimento em curva (motion path) parece natural; linha reta parece mecânica.
Squash & stretchEm UI: deformação sutil de scale no impacto (0.95/1.05). Em 3D/canvas: livre.
Encenação (staging)Uma coisa importante por vez. Coreografia guia o olho; caos compete por atenção.

11.2 Easing como identidade

Curvas de easing são a "voz" do produto. Construa uma paleta de 3 e use consistentemente:

/* paleta típica de produto */
--ease-out:  cubic-bezier(0.16, 1, 0.3, 1);   /* entradas — "expo out" */
--ease-in:   cubic-bezier(0.7, 0, 0.84, 0);   /* saídas */
--ease-move: cubic-bezier(0.65, 0, 0.35, 1);  /* deslocamentos em tela */

Alternativa moderna: animação por spring/física (stiffness/damping em vez de duração) — é o modelo do Framer Motion e do linear() do CSS; movimentos interrompíveis ficam naturais porque a velocidade é preservada.

11.3 Coreografia de página

Padrão profissional de entrada de uma seção hero: (1) máscara/loader sai; (2) título revela por linhas com stagger; (3) subtítulo e CTA entram 100–200ms depois; (4) mídia/3D entra por último com o maior movimento. Um único "beat" claro. Estude decompondo sites do Awwwards em câmera lenta (DevTools → Animations → 10%).

Exercício 11

Escolha um site premiado, grave a entrada da home e faça um "motion breakdown" por escrito: ordem, durações estimadas, easings, staggers. Recrie apenas com CSS. Este tipo de análise publicada demonstra olho — e olho é o que diretor de arte procura num dev.

MÓDULO 12Parte III — Motion

CSS, WAAPI & scroll-driven animations

Objetivo: extrair o máximo da plataforma antes de importar biblioteca — inclusive as novidades que rodam fora da main thread.

12.1 A regra do compositor

Anime só transform, opacity e filter: o navegador executa na thread do compositor, imune a jank de JavaScript. Animar width/top/margin força layout+paint a cada frame. will-change: transform promove a camada — use pontualmente, remova depois.

/* reveal padrão de produção */
.reveal{ opacity:0; transform:translateY(24px);
  transition: opacity .6s var(--ease-out), transform .6s var(--ease-out); }
.reveal.in{ opacity:1; transform:none; }
/* dispare .in com IntersectionObserver */

12.2 Keyframes compostos e stagger em CSS puro

@keyframes rise{ from{ opacity:0; transform:translateY(30px) } }
.item{ animation: rise .6s var(--ease-out) backwards;
       animation-delay: calc(var(--i) * 70ms); }
<!-- no HTML: style="--i:0" , "--i:1" ... -->

12.3 Web Animations API (WAAPI)

Keyframes do CSS com controle de JS — sem tocar em classes, com play/pause/reverse e finished como Promise:

const anim = el.animate(
  [ { opacity: 0, transform: 'translateY(24px)' },
    { opacity: 1, transform: 'none' } ],
  { duration: 600, easing: 'cubic-bezier(.16,1,.3,1)', fill: 'both' }
);
await anim.finished;
anim.reverse();

WAAPI é a base sobre a qual libs como Motion (Framer Motion vanilla) aceleram por hardware. Para orquestração complexa, ainda assim, GSAP ganha (Módulo 13).

12.4 Scroll-driven animations nativas — a grande novidade

CSS agora anima dirigido pelo scroll, na thread do compositor, sem uma linha de JS:

/* barra de progresso de leitura */
.progress{ transform-origin:left;
  animation: grow linear;
  animation-timeline: scroll(root); }      /* timeline = scroll da página */
@keyframes grow{ from{ transform:scaleX(0) } to{ transform:scaleX(1) } }

/* reveal quando o elemento cruza o viewport */
.card{ animation: rise linear both;
  animation-timeline: view();              /* timeline = posição no viewport */
  animation-range: entry 0% entry 60%; }

Suporte: Chrome/Edge sim; Safari/Firefox em adoção — em produção use como progressive enhancement com @supports (animation-timeline: view()) e fallback via IntersectionObserver/GSAP. Cobre parallax simples, reveals e progress bars com custo zero de JS.

12.5 View Transitions API

document.startViewTransition() anima entre estados do DOM (e entre páginas em MPA) com morphing automático de elementos compartilhados via view-transition-name. É o caminho nativo para o efeito "a imagem do card vira a imagem do detalhe" — antes exclusividade de FLIP manual (Módulo 13).

Exercício 12

Construa uma landing page SEM bibliotecas: reveals por IntersectionObserver, stagger por variável CSS, barra de progresso e um parallax com animation-timeline, e uma transição de lista→detalhe com View Transitions. Documente o suporte de navegadores no README. "Sabe fazer sem lib" é argumento de contratação em times enxutos.

MÓDULO 13Parte III — Motion

GSAP profissional

Objetivo: dominar a ferramenta padrão da indústria de sites premiados — timelines, ScrollTrigger, SplitText, Flip e arquitetura de animação em projetos reais. (Desde 2024 o GSAP é 100% gratuito, plugins incluídos.)

13.1 Tween, timeline e a gramática básica

gsap.to('.box',   { x: 200, duration: 1, ease: 'power3.out' });
gsap.from('.title',{ y: 40, opacity: 0 });
gsap.set('.el',    { transformOrigin: 'center bottom' });

// timeline: a razão de o GSAP dominar — sequência com controle total
const tl = gsap.timeline({ defaults: { ease: 'power3.out', duration: 0.8 } });
tl.from('.hero-title .line', { yPercent: 110, stagger: 0.08 })
  .from('.hero-sub',  { opacity: 0, y: 20 }, '-=0.4')  // overlap
  .from('.hero-cta',  { opacity: 0, y: 20 }, '<0.1')   // 0.1s após o início do anterior
  .add('media')                                     // label
  .from('.hero-media', { scale: 1.15, opacity: 0, duration: 1.2 }, 'media');

tl.pause(); tl.play(); tl.reverse(); tl.timeScale(2); tl.seek('media');

Position parameters ('-=0.4', '<', labels) são o que transforma sequência em coreografia. Staggers aceitam objeto: stagger: { each: 0.06, from: 'center', grid: 'auto' }.

13.2 ScrollTrigger: o motor do scrollytelling

gsap.registerPlugin(ScrollTrigger);

// 1) disparo simples
gsap.from('.card', {
  y: 60, opacity: 0, stagger: 0.1,
  scrollTrigger: { trigger: '.cards', start: 'top 75%' }
});

// 2) scrub: a animação É o scroll
gsap.to('.horizontal-track', {
  xPercent: -100,
  scrollTrigger: {
    trigger: '.horizontal',
    start: 'top top',
    end: '+=3000',       // 3000px de scroll viram a duração
    scrub: 1,            // 1s de suavização (catch-up)
    pin: true,           // segura a seção na tela
    anticipatePin: 1
  }
});

// 3) debug SEMPRE com markers durante o dev
scrollTrigger: { ..., markers: true }
  • pin + scrub é a dupla por trás de 90% dos "sites que contam história ao rolar".
  • ScrollTrigger.batch() para revelar dezenas de cards com eficiência.
  • Layout que muda depois do load (imagens, fonts) → ScrollTrigger.refresh().
  • Responsivo profissional: gsap.matchMedia() cria/destroi animações por breakpoint (e por prefers-reduced-motion!) com cleanup automático.

13.3 SplitText e tipografia cinética

const split = SplitText.create('.hero-title', { type: 'lines,words', mask: 'lines' });
gsap.from(split.words, { yPercent: 120, stagger: 0.02, ease: 'expo.out', duration: 1 });

O reveal "texto subindo de dentro de uma máscara" é a assinatura tipográfica dos sites premiados. Espere as fontes (document.fonts.ready) antes de splitar.

13.4 Flip: animar mudanças de layout

Técnica FLIP (First, Last, Invert, Play): capture o estado, mude o DOM (reordenar grid, mover para outro container, fullscreen), e o plugin anima a diferença como transform barato:

const state = Flip.getState('.card');
container.appendChild(card);            // mudança brusca de layout
Flip.from(state, { duration: 0.7, ease: 'power3.inOut', absolute: true });

13.5 Arquitetura de animação em projeto real

  • gsap.context() / useGSAP() (React): escopo de seletores + revert() automático no unmount — obrigatório em SPA para não vazar triggers.
  • Módulos por seção: cada seção exporta init()/destroy(); um orquestrador central decide o que vive em cada página/breakpoint.
  • Smooth scroll: o padrão de mercado é Lenis + integração com o ticker do GSAP; ScrollSmoother é alternativa oficial do GSAP.
  • quickTo/quickSetter para o que segue o mouse (cursores custom, magnetic buttons) sem criar tweens novos por evento.
Exercício 13 — a peça central do portfólio de motion

Uma landing de "estúdio fictício": preloader com contador, hero com SplitText mascarado, seção horizontal com pin+scrub, grid com Flip ao filtrar, magnetic button com quickTo, e Lenis. Tudo dentro de matchMedia com versão reduzida para mobile e reduced-motion. Esta única página cobre 80% do que agências pedem em teste técnico.

MÓDULO 14Parte III — Motion

Lottie, Rive & vetor animado

Objetivo: o canal designer→dev: rodar animações do After Effects e do Rive na web, controlá-las por código e saber quando usar cada formato.

14.1 Lottie: After Effects exportado como JSON

O motion designer anima no AE, exporta com o plugin Bodymovin, e você toca o JSON com lottie-web (ou @lottiefiles/dotlottie-web, formato .lottie comprimido):

import lottie from 'lottie-web';

const anim = lottie.loadAnimation({
  container: document.querySelector('#hero-anim'),
  path: '/anim/hero.json',
  renderer: 'svg',        // svg = nítido/estilizável; canvas = mais rápido p/ cenas densas
  loop: false, autoplay: false
});

// controle por código — o que diferencia dev de embed:
anim.playSegments([0, 45], true);       // intro
btn.onmouseenter = () => anim.playSegments([45, 90], true); // hover loop

// dirigido por scroll (com GSAP):
gsap.to({ f: 0 }, {
  f: anim.totalFrames - 1,
  onUpdate(){ anim.goToAndStop(this.targets()[0].f, true); },
  scrollTrigger: { trigger: '#hero-anim', scrub: true, pin: true, end: '+=1500' }
});
  • Regras para o designer: shape layers (nada de raster escondido), sem efeitos não suportados, composições enxutas. Combine cedo — retrabalho de Lottie é o atrito clássico designer/dev.
  • Performance: JSONs > 300 KB ou dezenas de camadas com máscaras derrubam mobile; prefira .lottie, lazy load e destroy() fora da tela.

14.2 Rive: animação com lógica embutida

Rive é editor + runtime próprios com state machines: o designer define estados (idle, hover, pressed, sucesso) e inputs; o dev só alimenta os inputs. Interatividade rica (personagens que seguem o cursor, botões com física, jogos leves) com arquivo minúsculo (.riv binário) e render em canvas/WebGL rápido.

import { Rive } from '@rive-app/canvas';

const r = new Rive({
  src: '/anim/robot.riv', canvas, autoplay: true,
  stateMachines: 'Main',
  onLoad(){
    const inputs = r.stateMachineInputs('Main');
    const look = inputs.find(i => i.name === 'lookX');
    addEventListener('pointermove', e => look.value = e.clientX / innerWidth * 100);
  }
});

14.3 Qual formato quando

CenárioEscolha
Designer trabalha em AE; ilustração/ícones animados; onboardingLottie
Mascote/UI interativa com estados e lógica; jogos levesRive
Coreografia de DOM/texto/layout da própria páginaGSAP/CSS
Cena com câmera, luz, profundidade, materialThree.js/R3F
Vídeo cinematográfico sem interação<video> otimizado (sério — às vezes é a resposta certa)
Exercício 14

Baixe uma animação gratuita do LottieFiles e implemente: intro + loop de hover por segmentos + versão scrubada por scroll. Depois recrie um botão com estados (idle/hover/press/success) no editor do Rive e conecte os inputs. Você acabou de cobrir os dois pedidos mais comuns de "animação do designer" em agência.

MÓDULO 15Parte IV — Integração

Scrollytelling: 3D dirigido por scroll

Objetivo: juntar as duas metades da apostila no formato mais pedido do mercado premium: a página que conta uma história 3D conforme o usuário rola (estilo Apple/Vision Pro).

15.1 A arquitetura

O padrão é sempre o mesmo, independente da stack:

  1. O scroll vira um progresso normalizado 0→1 (por seção ou global), suavizado (Lenis/scrub).
  2. Esse progresso alimenta uma timeline que move câmera, objetos, uniforms e DOM.
  3. O canvas fica fixed atrás do conteúdo; o HTML rola por cima e fornece a altura de scroll.

15.2 Implementação A — GSAP + Three.js (vanilla)

const tl = gsap.timeline({
  scrollTrigger: { trigger: '#story', start: 'top top', end: 'bottom bottom', scrub: 1 }
});
// capítulos como posições da timeline:
tl.to(camera.position, { x: 2, y: 1, z: 4, ease: 'none' })
  .to(mesh.rotation,   { y: Math.PI, ease: 'none' }, '<')
  .to(material.uniforms.uProgress, { value: 1, ease: 'none' })
  .to('#chapter-2', { opacity: 1, y: 0 }, '<');
// regra: com scrub, ease 'none' nos tweens — o easing vem do dedo do usuário.

Para a câmera seguir um trajeto artístico, anime um progresso 0→1 e leia posições de uma CatmullRomCurve3 (curve.getPointAt(p)) + lookAt em alvos por capítulo.

15.3 Implementação B — R3F + drei ScrollControls

<Canvas>
  <ScrollControls pages={4} damping={0.15}>
    <Scene />                 {/* lê useScroll() no useFrame */}
    <Scroll html>             {/* DOM que rola junto */}
      <section>Capítulo 1</section>
      ...
    </Scroll>
  </ScrollControls>
</Canvas>

// dentro de Scene:
const scroll = useScroll();
useFrame(() => {
  const p = scroll.offset;                     // 0..1 global
  const cap2 = scroll.range(0.25, 0.25);      // 0..1 dentro do capítulo 2
  camera.position.lerp(alvo[capAtual], 0.1);
  material.uProgress = cap2;
});

Alternativa comum em produção: Lenis controlando o scroll da página + ScrollTrigger emitindo o progresso para dentro do estado (zustand) que a cena R3F consome — desacopla DOM e canvas.

15.4 Truques de produção

  • Sequência de imagens: o efeito Apple clássico às vezes nem é 3D — é um vídeo exportado como frames desenhados num canvas 2D conforme o scroll. Saiba oferecer: custa menos e roda em tudo.
  • Capítulos com folga: deixe "zonas mortas" (10–15% de scroll sem mudança) entre capítulos para o texto respirar.
  • Mobile: reduza páginas de scroll, congele DOF/pós, e teste o pin com barra de endereço que recolhe (unidades svh/dvh).
  • Acessibilidade: todo o conteúdo textual precisa existir e fazer sentido com a cena estática (Módulo 17).
Exercício 15 — o projeto-vitrine

Página de produto fictício em 4 capítulos: câmera em curva, material trocando por uniform, texto revelando por capítulo, dissolve no final. Versão desktop (scrub) e mobile (simplificada). Este é O projeto que abre porta de agência — capriche por 2–3 semanas e trate como case, com página de "making of".

MÓDULO 16Parte IV — Integração

Transições de página & "SPA feel"

Objetivo: a cola que faz o site parecer um produto contínuo: preloaders, transições entre rotas, curtains, e WebGL persistente entre páginas.

16.1 O ciclo de uma transição

Toda transição de página é o mesmo autômato: leave (anima saída, bloqueia interação) → swap (troca conteúdo/rota, reseta scroll, refresh de triggers) → enter (anima entrada, libera). Ferramentas por contexto:

  • MPA moderno: View Transitions API cross-document (progressive enhancement).
  • Sites de agência (vanilla/CMS): barba.js ou Taxi.js interceptando navegação + hooks de GSAP.
  • Next/React: Framer Motion (AnimatePresence) ou orquestração própria com GSAP no App Router.

16.2 Padrões visuais canônicos

  • Curtain: um painel (ou 3, com stagger) sobe cobrindo a tela no leave e revela a próxima página no enter.
  • Elemento compartilhado: a thumb do card vira o hero do detalhe (FLIP/View Transitions).
  • Transição em WebGL: um plano fullscreen com shader (dissolve/ripple do Módulo 07) por cima de tudo — o canvas persiste entre rotas e a transição roda nele.
  • Preloader honesto: progresso real de assets (THREE.LoadingManageronProgress) alimentando um contador — e a saída do preloader é a primeira frase da coreografia do hero.

16.3 WebGL persistente entre páginas

Padrão avançado de estúdio: um único canvas fixo vive fora do container de rotas; cada página registra suas "cenas" (planos com texturas/efeitos posicionados sobre elementos DOM — técnica de DOM sync: medir getBoundingClientRect e projetar para unidades de mundo). Na troca de rota, os objetos da página antiga animam para fora, os novos para dentro — o WebGL nunca é destruído, então não há flash nem recompilação de shaders. É assim que funcionam os sites em que "as imagens têm ondulação" e sobrevivem à navegação.

Exercício 16

Adicione ao projeto do Módulo 13: preloader com progresso real, curtain de 3 painéis entre duas páginas (barba.js ou View Transitions), e uma imagem DOM-sincronizada com shader de hover (distorção por ruído seguindo o mouse). Cheque: scroll resetado, triggers atualizados, foco de teclado movido para o novo conteúdo.

MÓDULO 17Parte IV — Integração

Acessibilidade & performance de motion

Objetivo: o que separa profissional de amador aos olhos de um tech lead: movimento que respeita pessoas e métricas.

17.1 prefers-reduced-motion não é opcional

Distúrbios vestibulares são comuns; parallax e zoom em tela cheia causam náusea real. Reduced motion ≠ sem animação: mantenha opacidade/cor, corte deslocamento grande, parallax, autoplay e scroll-hijacking.

/* CSS */
@media (prefers-reduced-motion: reduce){
  *{ animation-duration:.01ms !important; transition-duration:.01ms !important }
}
// GSAP — por design, não por gambiarra:
const mm = gsap.matchMedia();
mm.add('(prefers-reduced-motion: no-preference)', () => { /* coreografia completa */ });
mm.add('(prefers-reduced-motion: reduce)', () => { /* fades simples */ });

17.2 Regras práticas de a11y para sites criativos

  • Conteúdo em texto real (não em canvas/textura); a página deve fazer sentido com JS/animações desligados.
  • Nada pisca mais de 3x/segundo (risco fotossensível — WCAG 2.3.1).
  • Scroll-hijacking mantém teclado funcionando (PgDn, setas, âncoras); pinos não podem prender o foco.
  • Cursor custom nunca remove o cursor real sem alternativa; magnetic buttons continuam clicáveis por teclado.
  • Canvas decorativo: aria-hidden="true"; canvas com conteúdo: descrição textual adjacente.

17.3 Core Web Vitals em site criativo

MétricaRisco típico em site criativoDefesa
LCP (<2.5s)Esperar 5 MB de GLB para mostrar algoHero em imagem/CSS primeiro; 3D hidrata depois; assets do Módulo 09
INP (<200ms)Main thread ocupada por JS de animaçãoCompositor-only, Lenis leve, dividir trabalho, web workers/OffscreenCanvas
CLS (<0.1)Canvas/imagens sem dimensão, fonts trocandoReservar espaço, font-display: swap + métricas ajustadas, animar só transform

Padrão de carregamento profissional: página funcional e bonita sem o 3D → 3D entra como realce (progressive enhancement) com requestIdleCallback/interseção → preloader só quando a experiência exige (e aí que seja parte da direção de arte).

Mercado

Times sérios reprovam candidato criativo que ignora reduced-motion e Web Vitals. Inversamente: um case que mostra Lighthouse verde COM WebGL pesado é raro e memorável. Use isso como diferencial explícito na apresentação do portfólio.

MÓDULO 18Parte V — Carreira

Portfólio que contrata

Objetivo: transformar os exercícios desta apostila num portfólio que passa em triagem de agência e gera inbound de freela.

18.1 A anatomia do portfólio de creative dev

  • 3 a 5 cases, não 15 experimentos. Qualidade brutal em poucos. O resto vai para uma página "lab/playground".
  • O próprio site é o case nº 1. Ele precisa demonstrar: coreografia de entrada, transição de página, um momento WebGL memorável, e abrir liso no celular. Recrutador julga em 30 segundos no celular.
  • Cada case conta processo: problema → direção → 2-3 desafios técnicos com trechos de código/GIFs → resultado com números (FPS, peso, draw calls, prazo). Vídeo de 30–60s no topo (nem todo mundo espera o load).
  • Créditos honestos: se o design não é seu, credite. Estúdios valorizam quem sabe trabalhar COM designer.

18.2 Distribuição: portfólio parado não contrata

  • Awwwards / FWA / CSSDA: submeta seu melhor projeto. Mesmo um "Honorable Mention" muda seu inbound — diretores de estúdio garimpam esses sites para contratar. Custa a taxa de submissão; trate como investimento de marketing.
  • Codrops: escrever um tutorial/demos para o Codrops é historicamente o atalho nº 1 para reconhecimento internacional na área.
  • X/Twitter + Instagram + LinkedIn: poste vídeos curtos de WIP semanalmente. A comunidade creative dev vive no X; clientes brasileiros vivem no Instagram/LinkedIn. Um clipe de 15s de shader roda mais que qualquer currículo.
  • CodePen/Shadertoy: picks do CodePen ainda geram convite de trabalho. Mantenha 1 pen novo por semana durante a fase de construção.

18.3 O teste dos 30 segundos

Checklist do seu site pelo olhar de quem contrata: carrega rápido no 4G? algo memorável acontece sem rolar? dá para achar contato em 1 clique? tem vídeo dos cases? roda no iPhone antigo do diretor de arte? Se alguma resposta é "não", conserte antes de divulgar.

Exercício 18

Escreva o case do projeto do Módulo 15 no formato problema→desafios→números ANTES de considerá-lo pronto. Se não há o que escrever, o projeto ainda não é um case — volte e crie os desafios (otimização, um shader autoral, uma interação única).

MÓDULO 19Parte V — Carreira

Mercado, vagas & precificação

Objetivo: mapa realista de onde está o dinheiro: cargos, empregadores, faixas, freela e como se posicionar. (Valores são ordens de grandeza para orientar negociação — pesquise o momento atual antes de fechar qualquer acordo.)

19.1 Os cargos e onde eles vivem

CargoO que fazOnde
Creative Developer / Creative TechnologistSites premiados, campanhas, instalaçõesEstúdios digitais (BR: agências de publicidade digital e estúdios boutique; internacional/remoto: estúdios de premiação, produtoras)
Frontend com foco em motion/interactionDesign systems com motion, produto com "delight"Startups de produto, fintechs, big techs
Dev 3D web / Graphics engineerConfiguradores, e-commerce 3D, mapas, gêmeos digitais, enginesIndústria, imobiliário, automotivo, empresas de visualização
Dev de jogos web / playable adsJogos HTML5, anúncios jogáveisAdTech, estúdios de games casuais
Motion designer que codificaLottie/Rive pipelines, protótiposTimes de brand/marketing

19.2 Faixas de referência (ordens de grandeza)

  • CLT/PJ Brasil: front pleno com diferencial de motion/3D costuma ficar acima da média de front puro; sênior criativo em estúdio forte ou produto: R$ 12–25k+/mês PJ nas praças aquecidas.
  • Remoto internacional: é onde a especialidade paga de verdade — creative devs sêniores freelancers cobram na faixa de US$ 60–120+/hora; contratos full-time remotos US$ 70–150k+/ano dependendo do estúdio/país. O portfólio (Módulo 18) é o passaporte; inglês funcional é pré-requisito.
  • Freela BR (projetos): landing com motion caprichado R$ 5–20k; scrollytelling/site 3D completo R$ 15–60k+; configurador de produto R$ 10–40k. Varia com cliente e escopo — o teto é definido pelo valor para o negócio, não pela sua hora.

19.3 Precificação de freela sem se machucar

  1. Escopo fechado por entregáveis, nunca "site animado": nº de seções, nº de interações únicas, dispositivos suportados, rodadas de ajuste (2, e por escrito).
  2. Precifique por valor + risco: comece do seu custo/hora × horas × 1.5 de margem de imprevisto criativo (efeito "só mais um ajuste no easing" consome semanas), depois ajuste pelo valor para o cliente.
  3. 50% para começar, 50% na entrega (ou 40/30/30 com marco de aprovação de protótipo). Protótipo de movimento (uma seção real) como marco evita refação da direção inteira.
  4. Cobre à parte: otimização de assets 3D fornecidos "crus" pelo cliente, compra de licenças (fonts, modelos), manutenção pós-entrega.
  5. Retainer de performance/motion (X horas/mês) é a renda recorrente natural depois do primeiro projeto.

19.4 Como as vagas testam você

  • Teste prático: reproduzir uma seção de site premiado a partir de um vídeo/Figma em 3–7 dias. (O Módulo 13 é literalmente o treino disso.)
  • Perguntas recorrentes: pipeline gráfico (M01), draw calls e como reduzi-los (M08), diferença vertex/fragment (M01), como estruturar animação em SPA sem vazamento (M13), reduced-motion (M17), e "explique um efeito do seu portfólio linha a linha".
  • Sinais que os estúdios procuram: gosto (referências que você cita), colaboração com designer, e cases com números.
Posicionamento — o conselho mais importante do módulo

Não se venda como "sei Three.js". Venda um resultado de negócio: "faço páginas de produto 3D que aumentam conversão", "transformo marcas em experiências que ganham prêmio e imprensa", "deixo sites pesados rodando a 60fps em qualquer celular". Especialista em resultado cobra 3–5x mais que generalista em ferramenta — com o mesmo conhecimento técnico desta apostila.

MÓDULO 20Parte V — Carreira

Roteiro de 6 meses + projetos-guia

Objetivo: o plano de execução. 10–15h/semana. Ao fim: 4 cases, presença pública e prontidão para teste técnico.

20.1 O cronograma

MêsMódulosEntregável público
101–03Visualizador de produto (Ex. 03) no ar + 4 pens de estudo
204–05Versão R3F + 2 shaders publicados (oceano, aurora) + post comparativo
306–08Peça de partículas/raymarching otimizada com auditoria antes/depois (Ex. 06+08)
409–13Landing "estúdio fictício" completa com GSAP (Ex. 13) — seu futuro site pessoal
514–17Scrollytelling 3D (Ex. 15) com Lottie/Rive de apoio, a11y e Vitals verdes
618–19Portfólio no ar com 4 cases escritos; submissão a Awwwards; 20 candidaturas/propostas enviadas

20.2 Rotina semanal que funciona

  • 60% construir o projeto do mês, 20% estudar (docs, Book of Shaders, breakdowns), 20% publicar (1 vídeo curto ou pen por semana, sem exceção).
  • Toda sexta: teste em celular real + throttling. Todo projeto nasce com reduced-motion.
  • Recrie 1 efeito de site premiado por mês do zero — é o exercício com melhor razão aprendizado/hora da área.

20.3 Depois dos 6 meses: trilhas de aprofundamento

  • Trilha estúdio internacional: WebGPU/TSL a fundo (M10), OGL (lib mínima usada por vários estúdios), escrever para Codrops, inglês de entrevista.
  • Trilha produto: Framer Motion/Motion One, design systems de animação, React Server Components + hidratação de canvas, métricas.
  • Trilha jogos/experiências: física (Rapier), áudio reativo (Web Audio + FFT em uniforms), multiplayer leve (WebSocket), PlayCanvas/Needle.
  • Trilha instalações: TouchDesigner, projeção, sensores — creative coding fora do navegador, diárias altas em eventos.
A métrica única

Se tiver que acompanhar um só número nos 6 meses: peças publicadas por semana. Consistência pública compõe: cada pen/vídeo é um bilhete de loteria permanente que trabalha por você para sempre.

ANEXOReferência rápida

Glossário & recursos

Glossário essencial

Draw callUma ordem de desenho enviada à GPU; a principal métrica de custo de CPU em cena 3D.
Uniform / Attribute / VaryingDado global do frame / dado por vértice / dado interpolado entre vertex e fragment.
UVCoordenadas 2D (0–1) que mapeiam textura sobre a superfície.
NormalVetor perpendicular à superfície; base de toda iluminação.
PBRPhysically Based Rendering — materiais por metalness/roughness reagindo a ambiente.
HDRI / env mapImagem panorâmica de alta faixa dinâmica usada como iluminação/reflexo.
SDFSigned Distance Function — descreve formas por distância; base de raymarching e texto nítido.
FBMSoma fractal de oitavas de ruído; nuvens, terrenos, mármore.
InstancingN cópias de uma geometria em 1 draw call.
Render target / FBORenderização para textura em vez da tela; base de pós-processamento e GPGPU.
Frustum cullingNão desenhar o que está fora do campo da câmera.
KTX2 / BasisTextura comprimida que permanece comprimida na VRAM.
Draco / MeshoptCompressões de geometria para GLTF.
ScrubVincular o progresso de uma animação ao scroll.
FLIPFirst-Last-Invert-Play: animar mudanças de layout via transform barato.
StaggerAtraso incremental entre elementos de um grupo animado.
EasingCurva de aceleração de uma animação.
JankEngasgo perceptível por frames perdidos.
WGSL / TSLLinguagem de shader do WebGPU / camada de nós JS do Three.js que compila para WGSL e GLSL.
State machine (Rive)Grafo de estados de animação controlado por inputs.

Recursos por módulo

  • Docs oficiais: threejs.org (manual + exemplos — leia os exemplos, são a melhor escola), docs.pmnd.rs (R3F/drei), gsap.com/docs, developer.mozilla.org (WAAPI, scroll-driven, view transitions), rive.app/docs, lottiefiles.com.
  • Shaders: The Book of Shaders (PT), iquilezles.org (Inigo Quilez), Shadertoy, lygia.xyz (biblioteca modular de GLSL/WGSL).
  • Cursos pagos que o mercado respeita: Three.js Journey (Bruno Simon) — cobre do básico a shaders/R3F e é citado em vaga; cursos de motion/creative dev da Awwwards Academy.
  • Inspiração/estudo: awwwards.com, tympanus.net/codrops (tutoriais + roundups mensais), openprocessing e o X da comunidade creative dev.
  • Ferramentas: Spector.js, gltf.report, leva, r3f-perf, Lenis, barba.js, gltf-transform, Blender + gltfjsx.
  • Assets CC0: Poly Haven, Kenney, Quaternius, ambientCG.

FIM DA APOSTILA — 20 módulos · Parte I a V · Feche o loop: publique algo esta semana.