Skip to content

Técnicas de Pincel 3D e Escultura de Mundos Dentro do Jogo

Queremos que os jogadores esculpam o mundo. Não que posicionem prefabs em uma grade. Não que ativem e desativem blocos. Mas que realmente remodelem o terreno: abram rios, ergam montanhas, suavizem encostas e escavem cavernas. O tipo de coisa que o ZBrush e o modo de escultura do Blender oferecem aos artistas, mas executado a 60 fps dentro de um jogo multijogador no navegador.

Esse é um problema complexo de engenharia que abrange representação de dados, extração de malha, computação na GPU, matemática de pincéis e sincronização de rede. Este guia documenta tudo o que descobrimos.

Os Dois Mundos da Representação de Terreno

Todo sistema de escultura começa com uma escolha sobre como os dados do terreno são armazenados. Essa escolha determina quais tipos de edição são possíveis, a velocidade de execução e o consumo de memória.

Mapas de altura

Um mapa de altura armazena um valor de altura por ponto da grade. Você pode imaginá-lo como uma imagem em tons de cinza na qual o brilho equivale à elevação. Nosso terreno atual em world/client funciona exatamente assim: noise.ts gera a altura por meio de ruído de valor FBM, e cada Chunk armazena um mapa de altura Float32Array, que é projetado sobre uma PlaneGeometry.

Mapas de altura são rápidos. A amostragem consiste em uma única consulta ao array com interpolação bilinear. O LOD é trivial, pois basta reduzir a resolução da grade. A mesclagem de texturas baseada em splats é mapeada diretamente na grade UV. As colisões de física se resumem a uma consulta de altura.

A limitação é a topologia. Um mapa de altura só pode representar uma altura por coordenada (x, z). Sem cavernas. Sem saliências. Sem arcos. Sem túneis. Se um jogador esculpir um penhasco que se dobra sobre si mesmo, um mapa de altura não conseguirá armazená-lo. Para terrenos compostos principalmente por colinas e montanhas, isso é suficiente. Para escultura livre, na qual os jogadores podem cavar o solo, é um beco sem saída.

Volumétricos (campos escalares 3D)

A alternativa é armazenar um valor em cada ponto do espaço 3D. Se o valor for negativo dentro do material sólido e positivo fora dele (ou vice-versa), você terá um Campo de Distância com Sinal (SDF). Se o valor for apenas uma densidade (acima de determinado limiar é sólido, abaixo é vazio), você terá um campo de densidade.

Representações volumétricas aceitam qualquer topologia. Cavernas, saliências, ilhas flutuantes e túneis através de montanhas. A contrapartida é o consumo de memória e a complexidade. Uma grade 256^3 com floats de 32 bits consome 64 MB. Uma grade 512^3 consome 512 MB. E isso para um único chunk. São necessárias estruturas de dados esparsas (octrees, mapas de bricks) para tornar isso viável.

A etapa de extração da malha também não é trivial. Não basta definir as posições Y dos vértices e encerrar o trabalho. É necessário um algoritmo que leia o campo escalar e produza uma malha de triângulos que aproxime a superfície na qual o campo cruza o zero.

Extração de Malha: Marching Cubes, Surface Nets e Dual Contouring

Marching Cubes

Marching Cubes é o algoritmo de extração de isosuperfícies mais antigo e amplamente implementado. Publicado por Lorensen e Cline em 1987, ele funciona examinando cada cubo da grade de voxels, na qual cada canto possui um valor escalar. Se alguns cantos estiverem dentro da superfície (negativos) e outros estiverem fora (positivos), um trecho de malha triangular será posicionado dentro desse cubo.

Cada cubo tem 8 cantos, e cada um pode estar dentro ou fora, produzindo 256 configurações possíveis (2^8). Por simetria, elas são reduzidas a 15 casos únicos. Uma tabela de consulta mapeia cada caso para um conjunto de triângulos. Os pontos de interseção das arestas são encontrados por interpolação linear ao longo das arestas nas quais o sinal muda.

Implementações recentes em GPU tornaram o Marching Cubes rápido o suficiente para escultura em tempo real. Uma implementação de 2025 na UE5 atribui cada thread da GPU a um cubo, processando milhares simultaneamente. O principal insight é que a triangulação de cada cubo é independente de seus vizinhos, o que torna o algoritmo extremamente paralelizável.

MCHex (arxiv 2511.02064, 2025) estende o Marching Cubes para a geração adaptativa de malhas hexaédricas com valores jacobianos positivos garantidos, melhorando a aproximação dos limites em malhas de simulação.

rupMC alcança desempenho dezenas de vezes superior ao de implementações seriais e quatro vezes superior ao de variantes DMC paralelas usando arquiteturas heterogêneas de CPU/GPU.

A principal limitação: o Marching Cubes tem dificuldade com detalhes acentuados. Uma aresta de 90 graus é arredondada e se transforma em uma curva suave. Para escultura de terreno, isso normalmente é aceitável (terrenos naturais são, em sua maioria, suaves), mas representa um problema para elementos arquitetônicos.

Surface Nets

Surface Nets é uma família mais recente de algoritmos que produz malhas mais suaves a partir de campos escalares discretos. Em vez de posicionar vértices nas arestas dos cubos (como o Marching Cubes), o Surface Nets posiciona um vértice por cubo que contém a superfície e conecta vértices adjacentes para formar quadriláteros.

O resultado é naturalmente mais suave. Um artigo de 2024 (arxiv 2401.14906) apresentou uma implementação paralela de alto desempenho do Surface Nets que executa de uma a duas ordens de magnitude mais rapidamente do que algoritmos sequenciais. O crate Rust fast-surface-nets gera aproximadamente 20 milhões de triângulos por segundo em um único núcleo de 2,5 GHz, usando pequenas tabelas de consulta e aceleração SIMD.

bevy-sculpter (v0.18.0, janeiro de 2026) usa Surface Nets como sua principal estratégia de geração de malha. O crate oferece escultura volumétrica baseada em SDF com quatro tipos de pincel: CSG rígido (adição/remoção instantânea), contínuo suave (para entrada mantida), desfoque (suavização da superfície) e nivelamento (define uma altura-alvo). Ele também inclui o recálculo de distâncias do SDF por meio do Fast Sweeping Method para restaurar as propriedades adequadas do campo de distância com sinal após as edições.

Surface Nets representa um bom meio-termo entre Marching Cubes (simples e rápido, mas produz malhas serrilhadas em dados binários) e Dual Contouring (preserva detalhes, mas é complexo).

Dual Contouring

Dual Contouring preserva detalhes acentuados que Marching Cubes e Surface Nets não conseguem preservar. Ele faz isso usando não apenas o sinal do campo em cada canto, mas também o gradiente (normal) nas interseções das arestas. Uma minimização QEF (Função de Erro Quadrático) posiciona o vértice dentro de cada célula na posição que melhor satisfaz todas as restrições de interseção das arestas.

O resultado: arestas e cantos acentuados são preservados na malha extraída. Um cubo com faces perpendiculares continua sendo um cubo.

A contrapartida é a complexidade. A resolução da QEF possui dependências entre células que dificultam a paralelização na GPU. Ela pode produzir malhas não manifold (arestas compartilhadas por mais de dois polígonos). E a implementação é mais trabalhosa do que a do Marching Cubes, embora Johannes Jendersie observe que uma implementação funcional de Dual Contouring tenha cerca de 200 linhas de código, em comparação com mais de 500 para uma implementação robusta de Marching Cubes.

Cubical Marching Squares (CMS) foi proposto como um meio-termo: independente entre células (adequado para GPU), mas ainda capaz de preservar alguns detalhes.

Qual usar

Para escultura de terreno voltada aos jogadores em um navegador:

Surface Nets é o candidato mais forte. Ele produz malhas suaves (terrenos com aparência natural) sem os artefatos serrilhados do Marching Cubes em dados binários. É rápido o suficiente para refazer a malha em tempo real. E é mais simples de implementar do que Dual Contouring.

Marching Cubes continua sendo uma escolha sólida quando a paralelização na GPU é a prioridade (cada cubo é independente) ou quando você precisa do suporte mais amplo possível de bibliotecas. O WebGPU SDF Editor de Reinder Nijhoff (janeiro de 2026) implementa tanto Marching Cubes quanto Surface Nets em seu pipeline de extração, executado inteiramente na GPU.

Dual Contouring é mais indicado para casos em que a precisão arquitetônica é mais importante do que o desempenho. Não é ideal para escultura de terreno em tempo real no contexto de um navegador.

O Algoritmo Transvoxel: Resolvendo a Junção de LODs

Quando terrenos de voxels são convertidos em malha com diferentes resoluções (LOD0 próximo ao jogador, LOD2 distante), surgem rachaduras nas fronteiras. Para mapas de altura, esse é um problema simples: interpole os vértices das bordas para corresponder ao vizinho de menor resolução. Nosso chunk.ts atual faz exatamente isso em stitchEdge().

Para terrenos volumétricos, o problema é muito mais difícil. A entrada de uma caverna no LOD0 pode produzir 30 triângulos na fronteira. A mesma região no LOD1 pode produzir 8 triângulos com uma topologia completamente diferente. Não existe uma forma simples de fazer uma interpolação linear entre eles.

O Algoritmo Transvoxel de Eric Lengyel (2009) resolve isso com "células de transição". Na fronteira entre dois níveis de LOD, o algoritmo considera 9 amostras de alta resolução (em vez de 8 cantos do cubo), produzindo 512 configurações possíveis que se distribuem em 73 classes de equivalência. Cada classe é mapeada para um padrão de triângulos predefinido que preenche perfeitamente a lacuna entre as duas resoluções.

O algoritmo opera sobre dados locais de voxels, portanto, a retriangulação de uma região modificada é rápida. Isso é essencial para a escultura em tempo real: quando um jogador edita o terreno próximo a uma fronteira de LOD, apenas as células de transição precisam ser reconstruídas.

Existe uma implementação em Rust no crate transvoxel. As tabelas de consulta originais estão disponíveis em transvoxel.org.

Matemática dos Pincéis

Um pincel de escultura é uma função que modifica os valores do campo escalar dentro de um raio ao redor de um ponto-alvo. A matemática é surpreendentemente semelhante em todas as implementações, do Blender e Unreal Engine aos sistemas de jogos em tempo de execução.

Funções de Atenuação

A atenuação do pincel determina como a intensidade da edição diminui do centro para a borda. O Blender 5.1 define estes perfis padrão:

Suave: f(d) = 3d^2 - 2d^3 (interpolação de Hermite, o mesmo smoothstep do nosso noise.ts)

Esfera: Forte no centro, com atenuação acentuada perto da borda. Aproximada por f(d) = sqrt(1 - d^2).

Acentuada: f(d) = (1 - d)^n com n > 2. Cria uma ponta fina.

Linear: f(d) = 1 - d, em que d é a distância normalizada a partir do centro (0 no centro, 1 na borda).

Constante: f(d) = 1 para d < 1, com corte abrupto no limite do pincel.

Quadrado inverso: Híbrido entre suave e esfera para proporcionar uma sensação natural de "argila".

Em todos os casos, d = distance_to_center / brush_radius, limitado a [0, 1]. O valor de atenuação multiplica a intensidade do pincel para produzir a modificação real do campo em cada ponto.

Espaço de Atenuação

O Blender diferencia a atenuação esférica (distância calculada no espaço 3D do mundo) da atenuação projetada (distância calculada no espaço 2D da tela). A atenuação projetada faz com que dois pontos visualmente próximos na tela afetem um ao outro da mesma forma, mesmo que estejam em profundidades muito diferentes no espaço do mundo. Para escultura de terreno, a atenuação no espaço 3D do mundo geralmente é mais intuitiva.

Operações Principais dos Pincéis

Elevar/Rebaixar (deslocamento): Adiciona ou subtrai valores do campo escalar dentro do raio do pincel, ponderados pela atenuação. Para mapas de altura: height[i] += strength * falloff(d). Para SDFs: sdf[i] -= strength * falloff(d) (a subtração torna o material mais sólido, elevando a superfície).

Suavizar (laplaciano): Substitui cada valor pela média de seus vizinhos, ponderada pela atenuação. Isso apaga detalhes e reduz o ruído. O filtro laplaciano amostra um pequeno kernel (3x3 para mapas de altura, 3x3x3 para volumes) e mistura o valor em direção à média. A suavização HC (Classes de Humphrey) é uma variante que preserva o volume melhor do que o laplaciano puro.

Nivelar: Define o valor do campo como uma altura-alvo (ou distância no espaço SDF), mesclada pela atenuação. O alvo geralmente é amostrado no centro do pincel quando o traço começa e, depois, mantido constante. Isso cria platôs planos.

Comprimir/Inflar: Move os vértices em direção à normal da superfície ou para longe dela. No espaço SDF, isso equivale ao deslocamento ao longo da direção do gradiente.

Agarrar: Translada uma região do campo, como se você estivesse puxando argila. O vetor de deslocamento corresponde ao delta do mouse projetado no espaço do mundo e é aplicado aos valores do campo dentro do raio.

Ruído: Adiciona ruído procedural ao campo dentro do raio do pincel. Útil para tornar superfícies suaves mais ásperas.

Carimbo: Aplica uma imagem 2D em tons de cinza como deslocamento, projetando-a sobre a superfície sob o cursor. A ferramenta Landscape da Unreal Engine oferece suporte a isso em pincéis de terreno.

Tesselação Adaptativa

sculpt-3D (escultura para navegador com React + Three.js) implementa tesselação adaptativa: à medida que o pincel se move pela malha, os triângulos próximos ao centro do pincel são subdivididos para fornecer mais vértices para a deformação. Isso evita o problema de "estiramento low-poly", no qual uma malha grosseira é distorcida pela escultura. A subdivisão usa divisões simétricas para manter a qualidade uniforme dos triângulos.

Em sistemas volumétricos, a tesselação adaptativa não é necessária da mesma forma, pois a malha é regenerada a partir do campo. Em vez disso, você pode aumentar localmente a resolução dos voxels perto das edições (octrees adaptativas) para obter o mesmo efeito.

Escultura com SDF: A Abordagem de Dreams

Dreams (PS4, 2020), da Media Molecule, é o sistema de escultura dentro do jogo mais ambicioso já lançado. Alex Evans apresentou a abordagem técnica na SIGGRAPH 2015.

Representação

Dreams armazena a geometria como uma função SDF composta em blocos de texturas de volume fp16 de 83^3. Cada escultura é uma lista de 1 a 100.000 "edições", em que cada edição é uma operação CSG (adicionar, subtrair, colorir) com uma forma primitiva (esfera, cubo, cilindro, cone, elipsoide, toro etc.) e um modo de mesclagem. Os modos de mesclagem usam funções de máximo suave e mínimo suave. Uma mesclagem "suave" produz transições arredondadas entre primitivas (como argila pressionada). Uma mesclagem "dura" produz cortes booleanos bem definidos. O raio de mesclagem pode ser controlado pelo usuário.

Renderização

Dreams não extrai uma malha de triângulos. Em vez disso, renderiza diretamente do SDF usando um renderizador personalizado de nuvem de pontos ("flecks"). Cada fleck é um pequeno disco orientado ao longo da normal da superfície. O SDF é amostrado para encontrar superfícies, e os flecks são distribuídos sobre elas. Isso evita completamente o gargalo da extração de malha, mas exige um renderizador personalizado.

Para um mundo em Three.js/WebGL, essa abordagem não se aplica diretamente. Precisaríamos extrair malhas. Porém, o conceito de lista de edições CSG é altamente relevante para desfazer/refazer e sincronização de rede.

Mecanismo de SDF Dinâmico de Mike Turitzin (2026)

Um motor de jogo atualmente em desenvolvimento por Mike Turitzin usa SDFs dinâmicos como representação principal. O motor oferece suporte a:

Modificações detalhadas durante o jogo: Adicionar e remover matéria de forma suave ou com bordas bem definidas. Alterações não destrutivas, como mover buracos ou criar túneis temporários que desaparecem atrás do jogador.

Mapas e atlas de bricks para cache esparso. Em vez de armazenar todo o campo SDF em uma grade 3D densa, o campo é dividido em "bricks" (pequenos blocos 3D). Somente os bricks que contêm o limite da superfície são alocados. Isso reduz drasticamente o uso de memória em cenas compostas principalmente por espaços vazios ou sólidos.

Clipmaps de geometria (Losasso & Hoppe, SIGGRAPH 2004) para LOD. Grades regulares aninhadas, com resoluções crescentes, circundam a posição da câmera. A grade mais interna tem a resolução mais detalhada; as grades externas tornam-se progressivamente menos detalhadas. Isso proporciona uma redução drástica no uso de memória e, ao mesmo tempo, permite espaços vastos. Os clipmaps são atualizados incrementalmente conforme a câmera se move, tornando-os eficientes para o streaming de mundos abertos.

Física e colisão operam diretamente sobre o SDF. O traçado de esferas (ray marching usando a distância do SDF como tamanho do passo) oferece raycasting eficiente. A detecção de colisão usa o gradiente do SDF como normal da superfície e o valor da distância como profundidade de penetração.

Teardown: destruição voxel em grande escala

Teardown (Voxagon) representa o outro extremo do espectro: cada objeto do mundo é um volume de voxels que pode ser destruído pedaço por pedaço.

Arquitetura

Os objetos são armazenados como grades de voxels com espaçamento regular. O motor não usa Marching Cubes nem SDFs para renderização. Em vez disso, faz o traçado de raios diretamente nos voxels usando um algoritmo DDA (Digital Differential Analyzer) modificado em shaders de fragmento, desenvolvido sobre OpenGL 3.3. Mipmaps formam uma estrutura de octree densa para acelerar a travessia de espaços vazios durante a interseção de raios.

Para cada objeto, o motor rasteriza sua caixa delimitadora orientada (OBB) e traça um raio através dela para encontrar interseções com voxels. Somente as faces traseiras da OBB são renderizadas, permitindo que a câmera entre no volume delimitador.

Sincronização da destruição (multijogador)

A atualização multijogador de março de 2026 do Teardown usa uma abordagem semideterminística. A destruição estrutural (abrir buracos, alterar propriedades, reconectar juntas) é processada com aritmética de inteiros de ponto fixo em um fluxo de rede confiável. Todos os clientes executam os mesmos comandos determinísticos e chegam ao mesmo estado do mundo. Alterações não estruturais (detritos, partículas) usam sincronização de estado não confiável.

Essa é uma observação importante para nosso mundo multijogador: as edições do terreno precisam ser determinísticas. Se o Jogador A esculpir uma montanha, todos os clientes deverão produzir a mesma malha a partir dos mesmos dados de campo. Os comandos de edição (posição, raio e intensidade do pincel, além do tipo de operação) devem ser os dados autoritativos, não a malha resultante.

ALICE-SDF: compactação e árvores CSG

ALICE-SDF (Adaptive Lightweight Implicit Compression Engine, v1.3.0 de março de 2026) fornece uma implementação em Rust de dados espaciais baseados em SDF, com compactação de 10 a 1.000 vezes em comparação com malhas poligonais. Ele oferece suporte a:

126 blocos de construção: 72 primitivas, 24 operações, 7 transformações e 23 modificadores. Operações de mesclagem suave (união, subtração, interseção), além de mesclagens com chanfros e degraus para biséis de borda dura e transições CSG escalonadas.

Diff/patch de árvore CSG para desfazer/refazer e sincronização de rede. Este é o recurso essencial para escultura multijogador: em vez de enviar todo o estado do campo, você envia o diff estrutural entre duas árvores CSG. O cliente aplica o patch para reconstruir o novo estado. Isso é muito mais eficiente em termos de largura de banda do que compactar por delta dados brutos de voxels.

Otimização de árvore CSG, incluindo remoção de transformações identidade, combinação de transformações aninhadas e rebaixamento de modificadores. Isso mantém a árvore compacta conforme as edições se acumulam.

Geração de malhas por Marching Cubes e Dual Contouring. A detecção de colisão da física opera diretamente sobre o SDF.

Suporte a WebAssembly permite integração com o navegador. O motor foi escrito em Rust com bindings WASM, o que o torna uma opção realista para uma aplicação Three.js.

Computação com WebGPU para terrenos

Desde o fim de 2025, a WebGPU está disponível em todos os principais navegadores. Chrome 113+, Edge 113+, Firefox 141+ e Safari 26+ já vêm com ela ativada. Isso possibilita pipelines de shaders de computação que antes estavam restritos à GPU.

Desempenho

Os shaders de computação da WebGPU geram terrenos aproximadamente 100 vezes mais rápido do que métodos executados na CPU, graças à paralelização massiva. A GPU executa milhares de cálculos simultaneamente, e a geração de terrenos é quase inteiramente paralela (cada vértice/voxel é independente).

O trabalho é organizado em três níveis: Nível de Dispatch (distribuição da carga de trabalho pela GPU), Nível de Workgroup (memória compartilhada dentro de uma unidade de processamento) e Nível de Thread (cálculos individuais). WGSL (WebGPU Shading Language) é a linguagem de shaders.

Pipeline de escultura de terreno em tempo real

Um pipeline de escultura com WebGPU seria assim:

  1. Aplicação do pincel (shader de computação): Atualizar os valores do campo escalar dentro do raio do pincel. Cada thread processa um voxel. Lê os parâmetros do pincel (posição, raio, intensidade, tipo de decaimento e operação) de um buffer uniforme e aplica a modificação.

  2. Extração da malha (shader de computação): Executar Surface Nets ou Marching Cubes na região modificada. O WebGPU SDF Editor de Nijhoff implementa isso como um pipeline de vários estágios: particionamento do espaço em 16.384 células, divisão de células baseada em octree e, depois, extração da superfície.

  3. Atualização do buffer de vértices (no lado da GPU): Gravar os vértices extraídos diretamente em um buffer de renderização sem passar pela memória da CPU.

  4. Cálculo das normais (shader de computação): Calcular as normais dos vértices a partir da malha ou do gradiente do SDF.

  5. Renderização (pipeline padrão): Desenhar a malha com materiais PBR padrão.

As etapas 1 a 4 podem ser executadas inteiramente na GPU, sem que nenhum dado retorne ao JavaScript. A CPU precisa apenas enviar os parâmetros do pincel a cada quadro.

O WebGPU SDF Editor

O WebGPU SDF Editor de Reinder Nijhoff (janeiro de 2026) demonstra essa abordagem em execução no Chrome. Ele oferece suporte a seis primitivas (cone, cilindro, cápsula, toro, caixa e esfera), três operações de mesclagem (união, subtração e interseção) com suavização configurável e grafos de cena hierárquicos. Cada primitiva ocupa 112 bytes em um único buffer da GPU.

O pipeline de renderização usa 1.024 mapas de sombra para oclusão de ambiente e antisserrilhamento temporal. Isso é executado com taxas de quadros interativas em GPUs de alto desempenho.

Escultura com mapa de altura: o caminho mais simples

Se não for necessário oferecer suporte a cavernas e saliências, a escultura com mapas de altura evita todo o pipeline volumétrico. É assim que a maioria dos jogos lançados implementa a edição de terrenos.

Padrão de implementação em tempo de execução

Unity Runtime Terrain (JohannHotzel, janeiro de 2026) demonstra o padrão convencional:

  1. Fazer um raycast da câmera através da posição do mouse para encontrar o ponto de impacto no terreno.
  2. Mapear o ponto de impacto para coordenadas do mapa de altura.
  3. Aplicar o pincel aos valores próximos do mapa de altura, ponderados pelo decaimento.
  4. Atualizar a malha definindo as posições Y dos vértices a partir do mapa de altura modificado.
  5. Reconstruir o colisor de física para corresponder à nova malha.

Para nosso terreno em Three.js, as etapas 1 a 4 correspondem diretamente à arquitetura existente. A classe Chunk já armazena mapas de altura e cria malhas a partir deles. Adicionar escultura significaria:

  • Um sistema de raycasting contra as malhas dos chunks (Raycaster do Three.js)
  • Funções de aplicação do pincel que modifiquem os valores de chunk.heightmap
  • Atualizações dos vértices da malha (definir as posições Y e recalcular as normais)
  • Recálculo do mapa de splat para a região afetada (para que a mesclagem de texturas reflita a nova inclinação/altura)
  • Transmissão da edição pela rede (posição, raio e intensidade do pincel, além da operação) aos outros clientes por meio do protocolo WebSocket existente

A abordagem de clipmap

Landow.dev descreve um "clipmap móvel" para terrenos com mapas de altura: uma única malha com densidade de subdivisão variável que acompanha o jogador. Em vez de dividir o mapa de altura em malhas separadas com diferentes níveis de LOD (como fazemos atualmente), o clipmap é uma malha contínua, densa perto da câmera e menos detalhada nas bordas.

Isso elimina completamente a costura entre níveis de LOD. A malha simplesmente tem mais triângulos onde eles são necessários e menos onde não são. A desvantagem é que a escultura exige atualizar uma única malha grande em vez de chunks individuais, o que pode ser custoso para edições extensas.

Mapas de altura SDF não destrutivos

Landow.dev também descreve uma técnica na qual o próprio mapa de altura é gerado a partir de uma composição de SDFs. Instâncias de formas (esferas, caixas e funções de ruído) são combinadas em um shader de computação usando operações CSG, e a saída é amostrada como um mapa de altura. Isso possibilita edição não destrutiva (você pode mover ou excluir qualquer instância de forma a qualquer momento), mantendo a simplicidade da renderização com mapas de altura.

Esse é um híbrido interessante: a representação dos dados é volumétrica (uma árvore CSG de SDFs), mas o caminho de renderização usa uma malha convencional de mapa de altura. Você obtém do lado do SDF operações de edição compatíveis com desfazer/refazer e adequadas para a rede, além da simplicidade de renderização e física do lado do mapa de altura. A limitação permanece: não há cavernas nem saliências.

Octrees de voxels esparsos para mundos grandes

Grades 3D densas não são escaláveis. Um mundo de 1 km em cada dimensão, com resolução de 0,5 m, precisaria de 8 bilhões de voxels. Octrees de Voxels Esparsos (SVOs) resolvem isso subdividindo o espaço recursivamente e alocando armazenamento apenas para os octantes que contêm o limite da superfície.

Uma SVO oferece naturalmente LOD hierárquico: a profundidade da árvore em cada ponto determina a resolução efetiva. Perto do jogador, a árvore é totalmente expandida (máximo detalhamento). Longe dele, é truncada em um nível menos detalhado.

Para renderização, as SVOs podem receber traçado de raios diretamente (sem necessidade de extração de malha). Um ray marcher executado na GPU intersecta raios com caixas alinhadas aos eixos em cada nível da árvore, ignorando por completo subárvores vazias. Isso elimina os problemas de desenho excessivo da renderização baseada em chunks e evita artefatos de meshing guloso.

O construtor de SVO baseado em Vulkan de AdamYuan demonstra desempenho significativo: 19 ms de tempo de construção para o Crytek Sponza, com resolução de 2^10, em uma GTX 1660 Ti.

Para escultura, a modificação de SVOs é eficiente: apenas os nós folha dentro do raio do pincel precisam ser atualizados, e a estrutura da árvore lida naturalmente com resoluções variáveis. Adicionar detalhes onde o jogador esculpe (dividindo os nós para alcançar maior resolução) e remover detalhes onde ele suaviza (mesclando os nós para obter menor resolução) decorre naturalmente da estrutura de dados.

O desafio da implantação em navegadores é que a WebGL não oferece suporte a shaders de computação e, embora a WebGPU ofereça, os algoritmos de construção e travessia de SVOs são complexos de implementar em WGSL.

Sincronização de rede para escultura multijogador

Nosso mundo já conta com multijogador por meio de Cloudflare Durable Objects (world-chunk-do.ts). Adicionar escultura significa sincronizar as modificações do terreno entre todos os clientes conectados.

Compactação delta

Enviar dados brutos de voxels é caro. Um estudo de 2024 da Universidade de Oulu obteve uma melhoria de 2 a 8 vezes no tamanho da carga útil ao combinar codificação delta com compactação DEFLATE, compactando atualizações de voxels em menos de um byte por voxel. O codec SDEC demonstra uma codificação delta compactada em bits que produz pacotes médios de 259 bytes, em comparação com 1.114 bytes usando serialização genérica.

Sincronização baseada em operações (recomendada)

Em vez de sincronizar o estado do campo, sincronize as operações. Cada ação de escultura se torna uma mensagem:

typescript
interface TerrainEditMsg {
  t: MsgType.TerrainEdit
  brush: {
    position: [number, number, number]
    radius: number
    strength: number
    falloff: 'smooth' | 'linear' | 'sharp' | 'constant'
    operation: 'raise' | 'lower' | 'smooth' | 'flatten' | 'noise'
    targetHeight?: number
  }
}

O servidor transmite essa mensagem a todos os clientes, e cada cliente aplica a mesma operação determinística de pincel aos dados locais do terreno. Essa é a mesma abordagem usada pelo Teardown para destruição estrutural: comandos determinísticos em um fluxo confiável.

O diff/patch de árvores CSG do ALICE-SDF vai além: em vez de pinceladas individuais, o diff representa a alteração estrutural de toda a árvore CSG. Isso possibilita desfazer/refazer com eficiência pela rede (enviando o patch inverso), e clientes que entrarem mais tarde poderão reconstruir todo o estado do mundo reproduzindo o registro de operações.

Prioridade e limitação de frequência

As edições de terreno próximas a jogadores conectados devem ter alta prioridade (transmissão imediata). Edições distantes de todos os jogadores podem ser agrupadas e enviadas com menor frequência. A rede de voxels do Enshrouded usa esse padrão: atualizações a 60 Hz para terrenos próximos aos jogadores e a 10 Hz para regiões em segundo plano.

A compactação ZSTD na transmissão reduz em até 60% o tamanho dos pacotes de mensagens de atualização do terreno.

Escultura colaborativa: edições simultâneas

Quando vários jogadores esculpem simultaneamente a mesma região, é necessário resolver conflitos. O cSculpt (CNR Visual Computing Lab, 2016) solucionou isso com um algoritmo de mesclagem multirresolução. Cada edição é representada em várias escalas, e edições simultâneas sobrepostas são combinadas pela mesclagem de suas representações multirresolução. Para nossos propósitos, uma abordagem mais simples funciona: a última gravação prevalece, com ordenação pelo servidor. O Durable Object atribui um timestamp a cada edição e as transmite em ordem. Todos os clientes aplicam as edições na mesma sequência. Como as pinceladas são pequenas, localizadas e aditivas/subtrativas, o resultado visual de edições simultâneas aplicadas em uma ordem ligeiramente diferente costuma ser indistinguível da ordem "correta".

INST-Sculpt: edição neural de SDFs (fronteira da pesquisa)

O INST-Sculpt (arxiv 2502.02891, fevereiro de 2025) permite a edição de SDFs neurais por meio de pinceladas. Os usuários desenham pinceladas sobre a superfície, e o sistema deforma o campo neural subjacente ao longo de vizinhanças tubulares ao redor do trajeto da pincelada. Perfis de pincel personalizados (seções transversais configuráveis) controlam o formato da deformação.

Isso é interessante para terrenos gerados por IA: se o mundo-base for representado como um SDF neural (uma pequena rede neural que mapeia coordenadas 3D para distâncias com sinal), a escultura modifica os pesos da rede em vez de dados explícitos de voxels. A representação é extremamente compacta (alguns MB para um mundo inteiro), mas sua avaliação é mais custosa do que uma tabela de consulta.

Essa tecnologia ainda está em fase de pesquisa. Atualmente, o custo de inferência de SDFs neurais em hardware de consumo é alto demais para uso em jogos em tempo real. Ainda assim, vale a pena acompanhá-la, especialmente à medida que os recursos de shaders do WebGPU melhoram e a inferência de modelos fica mais rápida.

World Creator 2026.3: o estado da arte comercial em terrenos

O World Creator (BiteTheBytes, março de 2026) representa o estado da arte comercial em ferramentas de criação de terrenos. A versão 2026.3 adicionou geração de terrenos baseada em GPU com adaptação automática do terreno (o terreno se ajusta aos objetos posicionados), distribuição de objetos focada na câmera para otimização de LOD e importação de dados reais de elevação (GeoTIFF, HGT, DTED).

Desde então, o World Creator 2026.4 (28 de abril de 2026) adicionou expressões matemáticas em campos numéricos, mesclagem das normais do terreno para integrar objetos de destaque à superfície, suporte completo a decals e escalonamento de VRAM que ajusta a quantidade máxima de objetos à memória disponível na GPU. A BiteTheBytes também lançou uma Community Edition gratuita, completa em recursos, mas sem exportação, funcionando efetivamente como uma avaliação ilimitada.

A abordagem deles usa computação em GPU para todas as operações de terreno: simulação de erosão, escavação de rios e pintura de texturas. As ferramentas de pincel são aceleradas por GPU e oferecem feedback em tempo real na viewport. Isso corresponde ao pipeline de computação WebGPU descrito acima, executado em GPUs de desktop.

O que já construímos: 24 spikes e um mundo em produção

O diretório world/spikes/ contém 24 protótipos autocontidos. Eles não são demonstrações triviais. Formam um pipeline progressivo de P&D no qual cada spike resolveu um problema específico, foi avaliado em relação a uma meta e orientou o seguinte. O sistema de escultura se baseia em todos eles, não apenas nos spikes volumétricos mais recentes.

O terreno de heightmap em produção (world/client/)

O mundo em produção usa um sistema de heightmaps divididos em chunks no Three.js WebGL:

  • noise.ts gera a altura do terreno por meio de ruído de valor FBM (5 oitavas para colinas, 4 para cristas e 3 para microdetalhes), usando uma função determinística terrainHeight(wx, wz)
  • chunk.ts constrói malhas PlaneGeometry a partir de heightmaps Float32Array com 3 níveis de LOD (32/8/4 segmentos por chunk de 64 unidades) e posiciona árvores/billboards instanciados com distribuição aleatória baseada em semente e colisores por objeto
  • chunk-manager.ts transmite chunks em anéis ao redor do jogador (raio 1 em LOD0, raio 3 em LOD1 e raio 6 em LOD2), com costura de bordas por interpolação linear em stitchEdge(), e fornece getHeight(), getNormal() e resolveCollisions() para a camada de física
  • terrain-material.ts faz a mesclagem de texturas baseada em splat com 4 camadas (grama/rocha/areia/terra) por meio de MeshStandardMaterial.onBeforeCompile, com pesos determinados por inclinação e altura, além de mesclagem de normal maps por camada
  • character-controller.ts consulta a altura do terreno a cada frame para aplicar gravidade, manter o personagem no solo e rejeitar inclinações excessivas (cosseno da inclinação máxima de 50 graus). A escultura precisa fornecer as alturas modificadas a esse sistema instantaneamente, ou o jogador cairá através do terreno editado
  • placement.ts já tem um Raycaster que detecta as malhas dos chunks para a ferramenta de posicionamento de objetos. A ferramenta de pincel deve seguir exatamente esse padrão, em vez de implementar raycasting do zero
  • protocol.ts define mensagens codificadas em MessagePack para sincronização multijogador por meio do Durable Object world-chunk-do.ts, que atualmente processa as mensagens PlayerState, PlaceObject, RemoveObject e Snapshot. As edições do terreno precisarão de um novo tipo de mensagem
  • world-chunk-do.ts (Cloudflare Worker) persiste os objetos posicionados no armazenamento do Durable Object e os transmite aos jogadores conectados em intervalos de 50 ms. Ele ainda não tem nenhum conceito de modificações do terreno

Spikes 01-11: a camada de base

Esses spikes validaram os sistemas centrais dos quais a escultura dependerá. Ignorá-los significa deixar de considerar restrições que o sistema de escultura precisa respeitar.

Spike 01 (terreno + instanciamento): O primeiro protótipo de terreno no Three.js. Estabeleceu o padrão PlaneGeometry + heightmap e o posicionamento de objetos instanciados que chunk.ts ainda usa.

Spike 02 (worker de física Rapier): Rapier 3D executado em um Web Worker com um colisor ColliderDesc.heightfield(). Implementou um controlador cinemático de personagem com subida automática de degraus, limites de inclinação e ajuste ao solo. Esse spike provou que a física pode ser executada fora da thread principal sobre um heightfield. Se esculpirmos o terreno, o heightfield da física precisará ser reconstruído ou substituído por um colisor trimesh para chunks de MC.

Spike 05 (comportamentos de LLM): Não está diretamente relacionado ao terreno, mas estabeleceu o esquema de comportamentos em JSON para objetos do jogo. É relevante porque elementos esculpidos no terreno poderiam acionar comportamentos (por exemplo, um rio escavado gera efeitos de água).

Spike 06 (streaming de chunks): Primeiro sistema de carregamento/troca de chunks com carregamento dinâmico conforme o jogador se move. Estabeleceu o padrão usado por chunk-manager.ts: regiões coloridas que são carregadas e descarregadas. A escultura precisa preservar o estado das edições quando os chunks são descarregados e recarregados.

Spike 07 (vegetação em GPU a partir de mapas de densidade): Grama e árvores instanciadas posicionadas por meio de mapas de densidade que consultam a altura e a inclinação do terreno. A escultura invalida o posicionamento da vegetação: se a altura do terreno mudar, as árvores poderão ficar flutuando ou enterradas. O mapa de densidade precisa ser regenerado nos chunks editados.

Spike 08 (custo do shader do material do terreno): Avaliou o desempenho de projeção triplanar, normal maps e mesclagem de 4 camadas. Mediu o custo exato em milissegundos de cada recurso. Constatou que projeção triplanar + normais + 4 camadas permanece dentro do orçamento a 45+ FPS. Esse orçamento é importante para terrenos esculpidos: se adicionarmos uma 5ª camada para "solo editado" ou alterarmos a mesclagem de superfícies escavadas, sabemos exatamente quanta margem existe.

Spike 09 (orçamento de sombras CSM): Mapas de sombras em cascata com 3 cascatas na resolução 1024^2. Mediu o custo das sombras em aproximadamente 1,5 ms. O terreno esculpido altera os mapas de sombras, mas o custo permanece constante independentemente do formato do terreno.

Spike 10 (clipmaps de geometria + geomorphing): Anéis de clipmap aninhados com geomorphing entre níveis de LOD para eliminar o surgimento repentino de geometria. A contagem constante de triângulos proporciona um custo previsível de GPU. O geomorphing é importante para a escultura: quando o jogador esculpe perto do limite de um LOD, a transição entre os níveis de LOD precisa refletir a edição. Se a edição só existir no anel de alta resolução, o destino do geomorph estará errado.

Spike 11 (streaming de chunks de heightmap): Streaming de chunks mais avançado, com uma grade visual mostrando os estados carregado/carregando/descarregado por nível de LOD. Estabeleceu o orçamento de streaming: máximo de chunks carregados por frame e priorização de chunks que precisam de upgrades de LOD. A escultura adiciona um novo sinal de prioridade: chunks que o jogador está editando ativamente nunca devem ser descarregados.

Spikes 12-14: integração de WebGPU + Three.js

Spike 12 (Marching Cubes em WebGPU): O primeiro spike volumétrico. Quatro chunks de SDF 64^3 com cavernas esféricas animadas, executados inteiramente na GPU. Usa WebGPU bruto: pipelines de computação para avaliação do SDF, extração por MC com a tabela de casos do Twinklebear (256 configurações, 16 entradas cada), contador atômico de vértices e desenho indireto. A meta de desempenho era <4 ms por chunk e <12 ms para os 4. Isso validou que MC em GPU é rápido o suficiente para gerar novamente as malhas em tempo real no navegador. Todos os spikes volumétricos posteriores reutilizam a tabela de casos de MC e os shaders WGSL definidos aqui.

Spike 13 (redefinição da base do Spike 12): Adaptou o caminho de desenho em WebGPU bruto do Spike 12 para execução dentro do WebGPURenderer do Three.js, acessando diretamente o device do backend. O pipeline de renderização ainda usa WebGPU bruto (drawIndirect com struct Vertex vec4+vec4). Isso provou que computação personalizada e renderização de cenas do Three.js podem coexistir no mesmo dispositivo de GPU.

Spike 14 (robustez incremental do WebGPU no Three.js): Substituiu o pipeline de renderização bruto por StorageBufferAttribute do Three.js para posições e normais. A computação de MC grava diretamente nesses buffers residentes na GPU. O buffer de drawIndirect controla quantos vértices o Three.js desenha. Esse é o padrão usado por todos os spikes posteriores: a computação permanece em WebGPU bruto, enquanto a renderização passa pelo grafo de cena do Three.js. A versão do Three.js usada nesses spikes avançou da 0.170.0 para a 0.172.0 à medida que o backend WebGPU se estabilizou.

Spikes 15-17: costura de LOD com Transvoxel

Spike 15 (estrutura inicial de emenda Transvoxel): Adicionou a arquitetura de três zonas: chunk de MC (centro volumétrico), faixa de transição (emenda entre o limite de MC e o heightmap) e anel de terreno (heightmap ao redor). As três compartilham um mesmo passe de material. Nessa etapa, a faixa de transição é uma malha provisória, não células Transvoxel reais.

Spike 16 (face +X de Transvoxel com heightmap compartilhado): Dois avanços cruciais em um único spike. Primeiro, substituiu o plano de terreno liso no SDF por um heightmap Perlin compartilhado: um Float32Array 257x257 enviado à GPU como buffer de armazenamento e consultado por interpolação bilinear no shader de computação do SDF. Agora, a superfície de MC e a malha de heightmap concordam quanto à mesma fonte de verdade. Segundo, implementou células de transição Transvoxel reais para a face +X, obtendo as tabelas de dados de referência de Eric Lengyel no GitHub (transitionCellClass, transitionVertexData, transitionCellData) e no pacote npm transvoxel-data. A CPU avalia células de transição com 9 amostras (512 configurações, 73 classes de equivalência), posiciona vértices interpolando valores de SDF nos pontos da grade e trata a inversão da ordem dos vértices nos casos espelhados.

Spike 17 (MC duplo com LOD 1x/2x): Dois chunks de MC lado a lado em resoluções diferentes. Alta resolução: 62 células com cell_scale=1.0. Baixa resolução: 31 células com cell_scale=2.0. O shader de MC recebeu os uniforms cell_scale e grid_points. Introduziu transition_shrink: os vértices do limite da face 0 do chunk de baixa resolução são deslocados 15% de cell_scale para dentro, criando uma pequena lacuna que as células de transição Transvoxel preenchem sem z-fighting. Esse é o modelo de LOD necessário para o sistema em produção: chunks próximos em resolução máxima, chunks distantes com metade da resolução e Transvoxel em todos os limites.

Spikes 18-21: casos extremos de Transvoxel e aceleração por GPU

Cada um desses quatro spikes resolveu uma falha específica da implementação de Transvoxel. Agrupá-los ocultaria os diferentes problemas.

Spike 18 (emenda 2:1 de heightmap): Aplicou Transvoxel a um limite composto apenas por heightmaps, no qual um lado tem o dobro da resolução do outro. Sem MC. A emenda entre um chunk de heightmap com 62 células e outro com 31 células é gerada a partir das tabelas de transição do Transvoxel, com redução de 15% na face de baixa resolução. Isso validou que Transvoxel funciona no caso composto apenas por heightmaps, não somente com MC.

Spike 19 (grade de canto 64/32/32/16): O caso de costura mais difícil: quatro chunks de resoluções diferentes se encontrando em um ponto de canto (64, 32, 32 e 16 células). O sistema de emendas precisa gerar células de transição ao longo de quatro arestas (A-B, A-C, B-D, C-D), com a ordem correta dos vértices para cada direção. Esse spike provou que as tabelas do Transvoxel tratam o canto com múltiplas resoluções sem lógica personalizada para casos especiais.

Spike 20 (canto Transvoxel em GPU): Transferiu para a GPU a geração das células de transição Transvoxel no layout de canto 64/32/32/16. A CPU era um gargalo ao regenerar as células de transição a cada frame para terrenos animados. A computação em GPU gera os vértices da emenda no mesmo passe que a extração por MC.

Spike 21 (canto com MC + Transvoxel em GPU): Combinou a extração completa por MC em GPU com a geração de emendas Transvoxel em GPU em uma única sequência de dispatches de computação. Tanto os chunks de MC quanto as quatro emendas são gerados na GPU, com contagens de vértices gerenciadas por contadores atômicos e desenhadas com drawIndirect. Esse é o pipeline completo de GPU para terrenos volumétricos com múltiplas resoluções e transições perfeitas entre LODs.

Spikes 22-24: a arquitetura híbrida

Spike 22 (política híbrida de MC/heightmap): O spike arquitetural essencial. Por padrão, os chunks são heightmaps. Quando a esfera animada de deformação intersecta o AABB de um chunk, esse chunk muda para o modo MC. Os demais permanecem como malhas de heightmap estáticas. Layout: chunks de 64, 32/32 e 16 células em diferentes resoluções. Emendas Transvoxel tratam todos os limites, incluindo transições de MC para heightmap. O spike monitora a quantidade de chunks de MC em comparação com chunks de HM e o overflow de vértices por frame.

Spike 23 (modos de chunk orientados por política): Carregado como um patch sobre o Spike 22. Adicionou histerese baseada na distância da câmera (os chunks não alternam rapidamente entre modos quando a câmera está perto de um limiar) e uma máscara de edição (chunks que foram deformados permanecem no modo MC mesmo que a fonte da deformação se afaste). Esse é o comportamento de "edição persistente" necessário para a escultura: depois que um jogador escava uma caverna, esse chunk permanece volumétrico para sempre.

Spike 24 (política + anéis de clipmap): O spike mais avançado. Combina o sistema de políticas de campo próximo do Spike 23 com os anéis de clipmap de geometria de campo distante do Spike 10. Atualizado para o Three.js 0.183.1. O campo próximo usa o sistema híbrido HM/MC com emendas Transvoxel nas resoluções 64/32/16. O campo distante usa anéis de clipmap com centro estático que acompanham a câmera. Essa é a arquitetura completa de renderização do terreno: escultura volumétrica baseada em chunks onde necessário e terreno barato por clipmap em todos os outros lugares.

Por que Marching Cubes, e não Surface Nets

A seção de pesquisa externa deste guia aponta o Surface Nets como o candidato mais forte para a escultura de terrenos no navegador. Mas todos os spikes do pipeline usam Marching Cubes. Isso não é por acaso.

A principal vantagem do MC é seu paralelismo extremamente simples: cada cubo é completamente independente. Os shaders de computação WGSL nos Spikes 12-24 disparam uma thread por cubo, sem nenhuma comunicação entre células. Contadores atômicos cuidam da alocação de vértices. Isso se encaixa perfeitamente nos workgroups da GPU.

O Surface Nets posiciona um vértice por célula que contém superfície e depois conecta as células vizinhas. Essa conectividade entre vizinhos cria uma dependência entre células. O crate fast-surface-nets cuida disso na CPU usando uma ordem de iteração cuidadosamente planejada. Na GPU, isso exige uma abordagem em duas passagens (encontrar os vértices e depois conectá-los) ou memória compartilhada dentro dos workgroups. Ambas são possíveis no WebGPU, mas aumentam a complexidade.

A recomendação prática: continue usando Marching Cubes no pipeline de escultura. Ele já foi comprovado em nossa base de código, os shaders WGSL existem e passaram por benchmarks, e o sistema de emendas Transvoxel foi construído em torno do posicionamento de vértices do MC baseado em arestas. Vale a pena reconsiderar o Surface Nets se o serrilhado do MC em dados binários se tornar um problema visível, mas, em terrenos SDF nos quais os valores formam gradientes suaves, o MC produz resultados limpos.

Arquitetura prática para escultura

A sequência de spikes resolveu o pipeline de renderização. O que falta é o sistema de pincéis, os efeitos em cascata pelos sistemas do jogo e a sincronização multijogador. Este é o plano, aproveitando todos os spikes.

Fase 1: escultura de heightmap (mudança mínima, alcance máximo)

Adicione ferramentas de pincel que modifiquem os heightmaps dos chunks no código de produção em world/client/. Isso funciona com o renderizador WebGL existente e não exige WebGPU.

Entrada do pincel: siga o padrão de PlacementTool em placement.ts. Ele já tem um Raycaster que detecta interseções com chunkManager.getChunkMeshes() e acompanha uma malha fantasma no ponto atingido. Um TerrainBrushTool faria o mesmo raycast, mas modificaria o heightmap do chunk em vez de posicionar um objeto. O manipulador World.onMouseDown já encaminha as ações de acordo com o estado da ferramenta.

Modificação do chunk (Chunk.applyBrush): mapeie a posição do pincel no mundo para as coordenadas da grade do heightmap. Para cada ponto da grade dentro do raio do pincel, calcule o deslocamento ponderado pelo falloff e some-o ou subtraia-o do valor do heightmap. Depois, atualize a malha: defina as posições Y dos vértices com base no heightmap modificado, recalcule as normais por diferenças centrais (o mesmo padrão terrainHeight(wx +/- eps, wz) já usado nas linhas 155-158 de chunk.ts) e gere novamente o splat map por meio de createSplatMap() em terrain-material.ts para a região afetada, atualizando a mesclagem de texturas baseada na inclinação.

Controlador do personagem: CharacterController.update() chama getHeight() a cada frame para manter o personagem no chão. ChunkManager.getHeight() delega para Chunk.sampleHeight(), que lê o Float32Array do heightmap do chunk. Como estamos modificando esse array diretamente, o controlador do personagem detecta a mudança no frame seguinte sem nenhuma integração adicional.

Invalidação de objetos: as instâncias de árvores em chunk.ts são posicionadas por meio de amostras de terrainHeight() no momento da geração. Após a escultura, as árvores na área afetada podem ficar na altura errada. A Fase 1 pode adiar essa correção (as árvores flutuam um pouco após pequenas edições). A Fase 2 precisa de um chunk.invalidateObjects() que obtenha novas amostras de altura e reconstrua as matrizes das instâncias. O mesmo vale para os colisores usados em resolveCollisions().

Física do Rapier (se integrada): o Spike 02 demonstrou que colisores de heightfield funcionam. Se o Rapier estiver ativo, o colisor de heightfield precisará ser reconstruído ou atualizado para o chunk modificado. ColliderDesc.heightfield() do Rapier recebe um Float32Array plano, portanto basta substituí-lo diretamente.

Sincronização de rede: adicione MsgType.TerrainEdit = 10 a protocol.ts:

typescript
interface TerrainEditMsg {
  t: MsgType.TerrainEdit
  cx: number
  cz: number
  brush: {
    wx: number
    wz: number
    radius: number
    strength: number
    falloff: number
    operation: number
  }
}

O WorldChunkDO transmite essa mensagem para todos os clientes e a adiciona a um registro de edições por chunk armazenado no Durable Object. Clientes que entrarem mais tarde recebem o registro de edições na mensagem Snapshot e o reproduzem para reconstruir o estado do terreno. Todos os clientes aplicam a mesma função determinística de pincel e, portanto, convergem para o mesmo heightmap.

Descarregamento e recarregamento de chunks: o Spike 06 e o Spike 11 estabeleceram o padrão de streaming. Quando um chunk é descarregado e depois recarregado, o registro de edições desse chunk precisa ser reproduzido sobre o heightmap procedural de base. O registro de edições é armazenado no servidor (Durable Object) e incluído na mensagem Snapshot.

Fase 2: escultura volumétrica com a arquitetura dos Spikes 22-24

Transfira o pipeline do Spike 24 para o mundo de produção. Quando um jogador esculpe abaixo da superfície (abrindo uma caverna ou escavando um túnel), o chunk afetado passa do modo heightmap para o modo MC.

Migração do renderizador para WebGPU: os Spikes 13-14 demonstraram que o WebGPURenderer do Three.js pode executar computação personalizada junto com o grafo de cena. O mundo de produção migra de WebGLRenderer para WebGPURenderer, com StorageBufferAttribute para chunks MC. Quando o WebGPU não estiver disponível, use como fallback o caminho da Fase 1, limitado a heightmaps.

Alocação de SDF por chunk: siga o padrão híbrido do Spike 22. Cada chunk começa como um heightmap. Na primeira pincelada volumétrica, aloque um Float32Array de 64^3, inicialize-o amostrando o heightmap (o valor SDF em cada ponto é world.y - heightmap_value) e mude para a renderização MC. O sistema de políticas do Spike 23 garante que o chunk permaneça permanentemente no modo MC (o comportamento de “edição persistente” da máscara de edição).

Heightmap compartilhado no shader SDF: use a função height_at() do Spike 16. Envie o heightmap do chunk para um buffer de armazenamento da GPU. O shader de computação SDF avalia max(height_sdf, edit_sdf), onde height_sdf = world.y - height_at(world.xz) e edit_sdf contém as modificações feitas pelo pincel. Chunks MC e chunks de heightmap usam a mesma referência de superfície em seus limites.

Emendas Transvoxel: use a pilha completa dos Spikes 15-21. Os limites entre MC e heightmap usam células de transição com a lacuna de contração. Os limites entre chunks MC com resoluções diferentes usam o padrão de LOD duplo do Spike 17. O caso especial do Spike 19 cuida das interseções quádruplas. A computação em GPU do Spike 21 gera toda a geometria das emendas no mesmo despacho.

Campo distante com clipmaps: use os anéis de clipmap do Spike 24 para o terreno fora do alcance de escultura. A escultura nunca modifica esses anéis; eles amostram o heightmap procedural de base.

Geomorphing: o geomorphing do Spike 10 elimina o surgimento abrupto nas transições de LOD. Para chunks editados, o destino do geomorph precisa incluir a edição. Se um chunk estiver em MC no LOD0 e seu vizinho no LOD1 for um heightmap, o geomorph mescla as duas representações. Isso exige amostrar o registro de edições até mesmo nos LODs mais baixos.

Orçamento de materiais: o Spike 08 testou em benchmark um material triplanar de 4 camadas com normais a mais de 45 FPS. Os chunks MC precisam do mesmo material. O splat map pode ser gerado a partir do gradiente do SDF (íngreme = rocha, plano = grama), em vez da inclinação do heightmap. Isso permanece dentro do orçamento de 4 camadas.

Invalidação da vegetação: a vegetação baseada em mapa de densidade do Spike 07 depende da altura e da inclinação do terreno. Quando um chunk passa para o modo MC, as instâncias de árvores precisam ser geradas novamente por meio de amostras da superfície SDF. Árvores em saliências ou dentro de cavernas precisam ser removidas. As matrizes da malha instanciada de chunk.ts são reconstruídas com base na nova superfície.

Fase 3: árvores de edição CSG para desfazer/refazer e sincronização de rede

Substitua a mutação direta do SDF por uma árvore de operações CSG. Cada pincelada adiciona uma primitiva (esfera, cápsula ou caixa) com uma operação (adicionar, subtrair ou mesclar suavemente). O SDF é recalculado a partir da árvore.

Benefícios:

  • Não destrutivo: qualquer edição pode ser removida da árvore para desfazê-la
  • Eficiente na rede: transmita a operação CSG, não os valores brutos do campo
  • Determinístico: todos os clientes constroem o mesmo SDF a partir da mesma sequência de operações
  • O diff/patch de árvores CSG do ALICE-SDF oferece sincronização eficiente em largura de banda e operações de desfazer/refazer pela rede

Armazenamento no Durable Object: a árvore de edições por chunk substitui o registro simples de edições da Fase 1. O WorldChunkDO armazena a estrutura da árvore CSG, não os deltas brutos do heightmap. As mensagens Snapshot incluem a árvore, e os clientes que entram mais tarde a avaliam para produzir o SDF local.

Fase 4: escultura colaborativa

Adicione suporte a edições simultâneas com reprodução de operações ordenada pelo servidor. O Durable Object registra o horário de cada edição e as transmite em ordem. Clientes que entrarem mais tarde recebem o registro de operações e reconstroem o estado do mundo. O tipo de mensagem Snapshot existente é estendido para incluir o histórico de edições do terreno por chunk.

Como as pinceladas são pequenas, localizadas e aditivas ou subtrativas, o resultado visual de edições simultâneas reproduzidas em uma ordem ligeiramente diferente geralmente é indistinguível da ordem “correta”. Uma estratégia em que a última gravação prevalece, com ordenação pelo servidor, é suficiente. A função tick() do Durable Object (que atualmente é executada em intervalos de 50 ms para o estado dos jogadores) adiciona as transmissões de edições do terreno ao mesmo ciclo.

Referências principais

Algoritmos:

  • Lorensen & Cline, "Marching Cubes" (1987)
  • Eric Lengyel, "Transvoxel Algorithm" (2009), transvoxel.org
  • Losasso & Hoppe, "Geometry Clipmaps" (SIGGRAPH 2004)
  • "A High-Performance SurfaceNets Discrete Isocontouring Algorithm" (arxiv 2401.14906, 2024)
  • MCHex (arxiv 2511.02064, 2025)

Implementações:

  • bevy-sculpter v0.18.0 (Rust, Surface Nets + pincéis SDF)
  • fast-surface-nets (Rust, 20 milhões de triângulos/s)
  • ALICE-SDF v1.3.0 (Rust + WASM, diff/patch de árvores CSG)
  • WebGPU SDF Editor (Nijhoff, janeiro de 2026)
  • SculptingPro (API de escultura em tempo de execução para Unity)
  • TerraBrush (GDExtension para escultura de terrenos no Godot)

Jogos:

  • Dreams (Media Molecule, SDF + renderização de nuvem de pontos, SIGGRAPH 2015)
  • Teardown (Voxagon, ray tracing de voxels com DDA, destruição multijogador determinística)
  • Engine SDF de Mike Turitzin (brick maps + clipmaps de geometria, janeiro de 2026)

Rede:

  • "Optimizing payload size for voxel state synchronization" (Oulu, 2024)
  • Multijogador de Teardown (sincronização semideterminística de destruição, março de 2026)
  • cSculpt (escultura colaborativa de malhas com mesclagem multirresolução)
Experimente agoraEsculpa menos, jogue mais cedo

Crie um jogo baseado em terreno sem encostar em um pincel.

Criar de graça →É grátis, roda no seu navegador, nada para instalar.