Construindo um mundo aberto no navegador, parte 14: O mundo ganha vida
Por Oleg Sidorkin, CTO e cofundador da Cinevva
Chegou agora? Consulte o guia da série. Ele explica o que é um spike e reúne links para todas as partes.
Depois da parte 13, já tínhamos a escultura. Elevar o terreno, escavar cavernas, suavizar penhascos, tudo em tempo real e com as emendas intactas. Mas o mundo parecia uma demonstração técnica. Sombreamento plano com cores de depuração, sobreposições de wireframe, geometria cinza com cores de LOD para diferenciar os chunks. Dava para editá-lo. Não dava para senti-lo.
Três spikes mudaram isso. Não a arquitetura. Exatamente o mesmo pipeline, os mesmos buffers e a mesma costura de emendas da Parte 13. Apenas adicionamos as camadas que fazem o terreno parecer um lugar: superfícies que respondem à forma, vida crescendo sobre essas superfícies e um corpo que caminha por elas.
A diferença entre "tecnicamente funciona" e "quero ficar aqui" revelou-se surpreendentemente pequena.
Esculpa um penhasco e veja-o virar rocha
O Spike 28 fez uma pergunta específica: um material de quatro camadas com mapeamento triplanar consegue rodar em terreno gerado por compute sem estourar o orçamento de frame? A resposta foi sim, mas o interessante foi o que aconteceu depois.
Abrir o Spike 28 em uma nova aba ↗ · Ver código-fonte
Quatro texturas procedurais (grama, rocha, areia e neve) geradas a partir de ruído FBM na inicialização. Sem arquivos externos, sem pipeline de assets, apenas matemática e uma DataTexture. Os pesos dos materiais vêm da própria superfície: inclinação e altitude. Áreas planas abaixo da linha das árvores recebem grama. Faces íngremes recebem rocha. Terrenos baixos recebem areia. Picos altos recebem neve. Cada peso bruto vem de um smoothstep aplicado à inclinação e à altitude; depois, eles são normalizados por fragmento para que
O momento que nos convenceu: eleve o terreno com o pincel para criar um penhasco íngreme, e a textura de rocha aparece na nova face no mesmo frame. Achate-o novamente, e a grama recupera a superfície. O material não sabe nada sobre o pincel. Ele apenas lê a posição no mundo e a normal da superfície, os mesmos dados usados para construir a geometria. O ciclo de feedback entre escultura e visual é instantâneo e não depende de scripts específicos.
O Spike 8 testou o custo dos materiais de terreno há um mês e meio, usando uma malha estática no WebGL. O Spike 28 comprova que isso funciona em terreno dinâmico gerado por compute, com todo o pipeline de escultura rodando por baixo. Já havíamos reservado orçamento para isso, mas ainda assim foi um alívio ver tudo se manter a 60 fps.
80.000 tufos de grama e o teto de uma caverna
O Spike 29 foi quando os instintos de desenvolvimento de jogos assumiram o controle.
Abrir o Spike 29 em uma nova aba ↗ · Ver código-fonte
Queríamos a grama de Breath of the Wild. Não a contagem de polígonos, mas a sensação. Grama que cobre os lugares certos, move-se com o vento e faz você querer correr por ela.
Cada tufo de grama é composto por três quads que se cruzam em ângulos de 60 graus, com quatro segmentos verticais para permitir a curvatura. É um formato de cruz que parece volumétrico de qualquer ângulo, sem truques de billboard. Um InstancedMesh por chunk, 80.000 tufos espalhados pelo mundo, aproximadamente 2,4 milhões de vértices de grama no total.
O posicionamento é a parte que importa. A CPU percorre uma grade com jitter em cada chunk e avalia a mesma lógica de inclinação e altitude usada pelo shader de materiais na GPU. Onde o sistema de materiais diz "grama", a grama cresce. Onde rocha ou areia predominam, a densidade cai para zero. A transição é suave porque os pesos de smoothstep subjacentes produzem gradientes contínuos nas bordas dos biomas. Você não percebe uma fronteira porque ela não existe.
Então fizemos algo que eu não tinha certeza de que funcionaria. Grama em superfícies SDF. A função de dispersão percorre cada coluna do volume SDF e encontra cruzamentos por zero entre voxels adjacentes. Onde o SDF passa de negativo para positivo, há uma superfície. A normal da superfície é simplesmente o gradiente normalizado do campo,
Esculpa uma caverna com o pincel SDF no Spike 27. Volte ao Spike 29 e haverá grama crescendo sobre o teto da caverna. O código de dispersão não sabe o que é uma caverna. Ele apenas enxerga uma superfície com a inclinação certa, na altitude certa. É esse tipo de comportamento emergente que torna tão gratificante criar sistemas de mundo aberto.
O vento é uma onda senoidal no vertex shader TSL, modulada pela coordenada V da lâmina para que as pontas balancem enquanto as raízes permanecem fixas. Pressione V para alternar entre suave, forte e desligado. O vento atua simultaneamente sobre todos os 80 mil tufos, sem nenhum custo de CPU, porque ocorre inteiramente no vertex shader.
O Spike 7 testou 50 mil instâncias de grama, e estávamos preocupados em atingir o limite. O Spike 29 roda 80 mil sobre terreno gerado por compute, costura de emendas, texturização com múltiplos materiais e pincéis. Agrupar tudo em menos chamadas de desenho de InstancedMesh ainda importa mais do que reduzir a quantidade de vértices por lâmina. A lição do Spike 7 se confirmou.
Uma coisa que deixamos para depois: a grama não é atualizada quando você esculpe o terreno sob ela. As matrizes das instâncias são definidas no momento da dispersão. Esculpa uma colina até transformá-la em vale, e a grama ficará flutuando no ar até você pressionar G para dispersá-la novamente. Bom o suficiente para um spike. Em produção, precisaremos refazer a dispersão nos chunks marcados como alterados.
O primeiro passo
O Spike 30 é aquele ao qual continuo voltando.
Abrir o Spike 30 em uma nova aba ↗ · Ver código-fonte
Nenhuma biblioteca de física. Uma cápsula personalizada com timestep fixo de 120 Hz. A base de código de produção usa Rapier em um web worker (o Spike 2 comprovou que a latência é aceitável), mas este spike precisava validar as próprias consultas de colisão. Um personagem consegue caminhar por terreno de heightmap, passar para terreno SDF esculpido e não cair através dele?
O núcleo é uma função chamada terrainQuery(x, y, z). Ela verifica se a posição está dentro de um chunk travado em MC. Se estiver, interpola trilinearmente o espelho do SDF na CPU e retorna o gradiente como normal da superfície. Caso contrário, faz uma consulta no heightmap com uma normal calculada por diferença central. O personagem não sabe sobre qual sistema de terreno está pisando. Ele apenas pergunta onde está o chão e recebe uma resposta.
A colisão com SDF era a parte que eu esperava ser difícil. Sete pontos de sondagem ao redor da cápsula (base, centro, topo e quatro deslocamentos cardinais). Uma sonda na posição
o que deixa apenas o componente que desliza ao longo da superfície. Não é uma engine de física. São consultas geométricas e uma resposta simples. Ainda assim, isso lida com cavernas, saliências e túneis esculpidos sem nenhum código de caso especial para cada formato. Você entra em uma caverna que escavou dez segundos antes, e a cápsula acompanha exatamente o contorno do teto.
O modelo de movimentação começou prático e acabou ficando divertido. Caminhar, correr, dar pique, pular e deslizar em inclinações acima de 45 graus. Então adicionei um parapente ao estilo BotW, e o spike virou algo que eu não queria fechar.
Pressione Espaço enquanto estiver no ar. A gravidade cai de -30 para -4. A velocidade de queda fica limitada a -3. Uma malha em forma de asa delta se abre a partir da cápsula com interpolação de escala. Incline para a esquerda, e a asa se inclina. A orientação da cápsula se alinha automaticamente ao vetor de velocidade, para que você sempre olhe na direção do movimento. Solte Espaço para cair. Toque o chão e você volta a correr.
A câmera recua para uma visão em terceira pessoa (pressione P para alternar). Ela segue o jogador por trás, com suavização da guinada. Um ray marching do jogador até a câmera testa o terreno em 20 passos. Voe para dentro de uma caverna, e o braço da câmera encurta suavemente em vez de atravessar a rocha. Saia pelo outro lado, e ele volta a se estender.
Este foi o momento que fez o spike valer a pena: esculpa um penhasco alto com o pincel de heightmap. Mude para a câmera em terceira pessoa. Corra até a borda. Pule. Abra o parapente. Incline para a esquerda sobre o terreno que você acabou de esculpir, com a grama cultivada no Spike 29 balançando lá embaixo, a textura de rocha do Spike 28 na face do penhasco e as emendas Transvoxel mantendo-se intactas em todas as bordas de chunks. Pouse do outro lado. Tudo funcionando junto em uma única aba do navegador.
Esculpindo sob os próprios pés
Uma coisa sobre a qual eu não tinha certeza: o que acontece quando você esculpe o terreno sobre o qual o personagem está parado? As alterações do heightmap propagam-se instantaneamente por terrainQuery(), pois ela lê diretamente o buffer da CPU. As alterações de SDF propagam-se pelo espelho do SDF na CPU. A cápsula resolve a penetração no próximo tick de física, o que, a 120 Hz, ocorre em até
Simplesmente funciona. Eleve o chão sob o jogador, e ele sobe junto. Escave o chão, e ele cai. Nenhum tratamento especial. A física roda rápido o suficiente para que alterações de terreno em um único frame nunca produzam grandes penetrações. Esse foi um feliz acaso da escolha de 120 Hz para o timestep. Escolhemos essa frequência para ter movimentos suaves, e ela tornou a edição de terreno segura sem nenhum custo adicional.
30 spikes depois
Começamos esta série com um heightmap plano e 500 cubos. Agora temos terreno esculpido com cavernas volumétricas, costura de emendas com Transvoxel, texturização com múltiplos materiais que responde ao formato da superfície, 80.000 tufos de grama balançando ao vento e um personagem que caminha, corre, dá piques, pula e plana sobre tudo isso.
Nada disso está em produção ainda. A base de código em world/client/ ainda roda WebGL com chunks simples de heightmap. Tudo que veio desses 30 spikes está em páginas HTML independentes. O trabalho de integração vem a seguir: migrar para WebGPURenderer, conectar a política híbrida de HM/MC ao gerenciador de chunks e integrar os sistemas de pincéis e materiais ao multiplayer.
Mas o sistema de renderização não é mais o risco. As questões em aberto agora são sobre fluxo de dados: persistência das edições, sincronização em rede das pinceladas e escultura colaborativa. O que acontece entre jogadores, não entre triângulos.
Se você acompanha esta série desde a Parte 1, obrigado por continuar conosco durante as partes confusas. Se acabou de encontrá-la, volte à Parte 1. É nos caminhos errados que estão as lições.
Tecnologias mencionadas neste capítulo
TSL (Three Shading Language). O sistema de shaders baseado em nós do Three.js para o renderizador WebGPU. Os materiais são compostos a partir de nós (positionWorld, normalWorld, smoothstep, triplanarTexture) usando composição de funções em JavaScript. O grafo de shaders é compilado para WGSL em tempo de execução. A TSL substitui o ShaderMaterial em GLSL puro para alvos WebGPU e fornece interoperabilidade entre os recursos padrão de materiais do Three.js (luzes, sombras e neblina) e lógica personalizada por fragmento.
Mapeamento triplanar. Uma técnica de projeção de texturas que amostra uma textura três vezes (nos planos XY, XZ e YZ) e faz a mistura com base na direção da normal da superfície. Isso elimina o estiramento de UV em geometrias de malha arbitrárias, o que é essencial para a saída de marching cubes, na qual os triângulos não têm coordenadas UV significativas. A TSL fornece triplanarTexture() como um nó integrado.
Vegetação instanciada com lâminas de quads cruzados. Cada tufo de grama é composto por três quads que se cruzam em ângulos de 60 graus, criando uma aparência volumétrica de qualquer direção de visualização. Quatro segmentos verticais por quad permitem uma curvatura suave na animação do vento. Todo o campo é renderizado como um único InstancedMesh por chunk. A capacidade é superalocada em 25% para que novas instâncias possam ocupar os espaços reservados quando o terreno for editado, sem realocar o buffer da GPU. Consulte nosso guia de geração de paisagens sobre vegetação. Colisão de cápsula vs. SDF. Colisão do personagem contra terreno volumétrico sem um motor de física. A cápsula é testada em vários pontos contra o SDF. Quando o valor do campo é menor que o raio da cápsula, o gradiente fornece a normal voltada para fora, e a diferença fornece a profundidade da penetração. Isso lida com cavernas, saliências e túneis sem nenhum código específico para cada forma. Consulte colisões em terrenos SDF.
Controlador de personagem com passo de tempo fixo. A física avança a 120 Hz independentemente da taxa de quadros, acumulando o tempo real e consumindo-o em passos de tamanho fixo. Um número máximo de subpassos evita uma espiral da morte em quadros lentos. O ajuste ao solo mantém a cápsula em contato com o terreno ao percorrer inclinações. O passo de tempo fixo garante um comportamento determinístico para futuras repetições multijogador.
Parte 14 de 29. Anterior: Parte 13 — Esculpindo o terreno e a morte da função matemática Próxima: Parte 15 — Substitua a linha de base e depois sincronize-a Guia da série: /pt-BR/blog/2026-02-25-open-world-browser-series-guide