Técnicas de pinceles 3D y esculpido de mundos dentro del juego
Queremos que los jugadores puedan esculpir el mundo. No colocar elementos prefabricados sobre una cuadrícula. No activar y desactivar bloques. Sino transformar de verdad el terreno: abrir cauces, levantar montañas, suavizar laderas y excavar cuevas. El tipo de trabajo que ZBrush y el modo Esculpir de Blender permiten hacer a los artistas, pero ejecutándose a 60 fps dentro de un juego multijugador para navegador.
Es un problema de ingeniería difícil que abarca la representación de datos, la extracción de mallas, el cómputo en GPU, las matemáticas de los pinceles y la sincronización de red. Esta guía documenta todo lo que descubrimos.
Los dos mundos de la representación del terreno
Todo sistema de esculpido comienza con una decisión sobre cómo almacenar los datos del terreno. Esa decisión determina qué tipos de edición son posibles, con qué rapidez se ejecutan y cuánta memoria consumen.
Mapas de altura
Un mapa de altura almacena un valor de elevación por cada punto de la cuadrícula. Puede entenderse como una imagen en escala de grises en la que el brillo equivale a la elevación. Nuestro terreno actual de world/client funciona exactamente así: noise.ts genera la altura mediante ruido de valores FBM y cada Chunk almacena un mapa de altura Float32Array que se proyecta sobre una PlaneGeometry.
Los mapas de altura son rápidos. El muestreo consiste en una sola consulta al array con interpolación bilineal. El LOD es trivial, porque basta con reducir la resolución de la cuadrícula. La mezcla de texturas basada en salpicaduras se asigna directamente a la cuadrícula UV. Las colisiones físicas se reducen a consultar una altura.
La limitación es la topología. Un mapa de altura solo puede representar una altura por cada coordenada (x, z). No admite cuevas, voladizos, arcos ni túneles. Si un jugador esculpe un acantilado que se repliega sobre sí mismo, un mapa de altura no puede almacenarlo. Para un terreno compuesto principalmente por colinas y montañas, esto es suficiente. Para el esculpido libre, donde los jugadores pueden excavar el suelo, es un callejón sin salida.
Representaciones volumétricas (campos escalares 3D)
La alternativa consiste en almacenar un valor en cada punto del espacio 3D. Si el valor es negativo dentro del material sólido y positivo fuera —o viceversa—, se obtiene un campo de distancia con signo (SDF). Si el valor solo representa una densidad —por encima de cierto umbral es sólido y por debajo está vacío—, se obtiene un campo de densidad.
Las representaciones volumétricas admiten cualquier topología: cuevas, voladizos, islas flotantes y túneles que atraviesan montañas. El precio es el consumo de memoria y la complejidad. Una cuadrícula de 256^3 con valores de coma flotante de 32 bits ocupa 64 MB. Una cuadrícula de 512^3 ocupa 512 MB. Y eso es para un solo chunk. Para que resulte práctico, se necesitan estructuras de datos dispersas, como octrees o mapas de bloques.
El paso de extracción de la malla tampoco es trivial. No basta con establecer las posiciones Y de los vértices. Se necesita un algoritmo que lea el campo escalar y genere una malla de triángulos que aproxime la superficie donde el campo cruza el valor cero.
Extracción de mallas: Marching Cubes, Surface Nets y Dual Contouring
Marching Cubes
Marching Cubes es el algoritmo de extracción de isosuperficies más antiguo y con más implementaciones. Publicado por Lorensen y Cline en 1987, funciona examinando cada cubo de la cuadrícula de vóxeles, donde cada esquina tiene un valor escalar. Si algunas esquinas están dentro de la superficie —valor negativo— y otras fuera —valor positivo—, se coloca un fragmento de malla triangular dentro de ese cubo.
Cada cubo tiene 8 esquinas, cada una dentro o fuera, lo que produce 256 configuraciones posibles (2^8). Mediante simetría, estas se reducen a 15 casos únicos. Una tabla de consulta asigna a cada caso un conjunto de triángulos. Los puntos de intersección de las aristas se calculan mediante interpolación lineal a lo largo de las aristas donde cambia el signo.
Las implementaciones recientes para GPU han logrado que Marching Cubes sea lo bastante rápido para el esculpido en tiempo real. Una implementación de 2025 para UE5 asigna cada hilo de la GPU a un cubo y procesa miles de ellos simultáneamente. La idea clave es que la triangulación de cada cubo es independiente de sus vecinos, lo que hace que el algoritmo sea extremadamente paralelizable.
MCHex (arxiv 2511.02064, 2025) amplía Marching Cubes para generar mallas hexaédricas adaptativas con valores jacobianos positivos garantizados, lo que mejora la aproximación de los límites en mallas de simulación.
rupMC alcanza un rendimiento decenas de veces superior al de las implementaciones secuenciales y es 4 veces más rápido que las variantes DMC paralelas mediante arquitecturas heterogéneas de CPU/GPU.
La principal limitación es que Marching Cubes tiene dificultades con los rasgos afilados. Una arista de 90 grados se redondea hasta convertirse en una curva suave. Para esculpir terreno esto suele ser aceptable —el terreno natural es mayormente suave—, pero supone un problema para los elementos arquitectónicos.
Surface Nets
Surface Nets es una familia de algoritmos más reciente que produce mallas más suaves a partir de campos escalares discretos. En lugar de colocar vértices sobre las aristas de los cubos, como Marching Cubes, Surface Nets coloca un vértice por cada cubo que contiene la superficie y después conecta los vértices adyacentes para formar cuadriláteros.
El resultado es naturalmente más suave. Un artículo de 2024 (arxiv 2401.14906) presentó una implementación paralela de Surface Nets de alto rendimiento que se ejecuta entre uno y dos órdenes de magnitud más rápido que los algoritmos secuenciales. El crate de Rust fast-surface-nets genera aproximadamente 20 millones de triángulos por segundo en un solo núcleo de 2,5 GHz mediante pequeñas tablas de consulta y aceleración SIMD.
bevy-sculpter (v0.18.0, enero de 2026) utiliza Surface Nets como estrategia principal de generación de mallas. El crate ofrece esculpido volumétrico basado en SDF con cuatro tipos de pincel: CSG duro —adición o eliminación instantánea—, continuo suave —para mantener pulsado el control—, desenfoque —suavizado de la superficie— y aplanado —ajuste a una altura objetivo—. También incluye la recuperación de distancias del SDF mediante el método Fast Sweeping para restaurar las propiedades correctas del campo de distancia con signo después de las ediciones.
Surface Nets ofrece un buen punto intermedio entre Marching Cubes —sencillo y rápido, pero produce mallas con aliasing a partir de datos binarios— y Dual Contouring —conserva los rasgos, pero es complejo—.
Dual Contouring
Dual Contouring conserva los rasgos afilados que Marching Cubes y Surface Nets no pueden mantener. Para ello utiliza no solo el signo del campo en cada esquina, sino también el gradiente —la normal— en las intersecciones de las aristas. Una minimización QEF —función de error cuadrático— coloca el vértice dentro de cada celda en la posición que mejor satisface todas las restricciones de intersección de las aristas.
El resultado es que las aristas y esquinas pronunciadas se conservan en la malla extraída. Un cubo con caras perpendiculares sigue siendo un cubo.
El inconveniente es la complejidad. La resolución de la QEF tiene dependencias entre celdas que dificultan la paralelización en GPU. Puede producir mallas no múltiples —con aristas compartidas por más de dos polígonos—. Y la implementación es más compleja que Marching Cubes, aunque Johannes Jendersie señala que una implementación funcional de Dual Contouring requiere unas 200 líneas de código, frente a más de 500 para una implementación sólida de Marching Cubes.
Cubical Marching Squares (CMS) se ha propuesto como solución intermedia: es independiente entre celdas —y, por tanto, apto para GPU—, pero sigue conservando algunos rasgos.
Cuál utilizar
Para el esculpido de terreno orientado a jugadores en un navegador:
Surface Nets es el candidato más sólido. Produce mallas suaves —con terrenos de aspecto natural— sin los artefactos de aliasing de Marching Cubes sobre datos binarios. Es lo bastante rápido para regenerar mallas en tiempo real. Y su implementación es más sencilla que la de Dual Contouring.
Marching Cubes sigue siendo una opción sólida cuando la prioridad es el paralelismo en GPU —cada cubo es independiente— o cuando se necesita la máxima compatibilidad con bibliotecas. El editor SDF para WebGPU de Reinder Nijhoff (enero de 2026) implementa tanto Marching Cubes como Surface Nets en su proceso de extracción, ejecutado íntegramente en la GPU.
Dual Contouring se reserva mejor para aquellos casos en los que la precisión arquitectónica importa más que el rendimiento. No es ideal para esculpir terreno en tiempo real dentro de un navegador.
El algoritmo Transvoxel: cómo resolver la unión entre niveles de LOD
Cuando el terreno de vóxeles se convierte en malla con distintas resoluciones —LOD0 cerca del jugador y LOD2 en la distancia—, aparecen grietas en los límites. Para los mapas de altura, es un problema sencillo: se interpolan los vértices del borde para que coincidan con el vecino de menor resolución. Nuestro chunk.ts actual hace exactamente esto en stitchEdge().
Para el terreno volumétrico, el problema es mucho más difícil. La entrada de una cueva en LOD0 podría producir 30 triángulos en el límite. La misma región en LOD1 podría producir 8 triángulos con una topología completamente distinta. No existe una forma sencilla de interpolar linealmente entre ambas.
El algoritmo Transvoxel de Eric Lengyel (2009) resuelve este problema mediante «celdas de transición». En el límite entre dos niveles de LOD, el algoritmo considera 9 muestras de alta resolución —en lugar de las 8 esquinas de un cubo—, lo que produce 512 configuraciones posibles agrupadas en 73 clases de equivalencia. Cada clase se asigna a un patrón de triángulos predefinido que rellena perfectamente el espacio entre las dos resoluciones.
El algoritmo opera sobre datos locales de vóxeles, por lo que volver a triangular una región modificada es rápido. Esto es fundamental para el esculpido en tiempo real: cuando un jugador edita terreno cerca del límite de un LOD, solo es necesario reconstruir las celdas de transición.
Existe una implementación en Rust como el crate transvoxel. Las tablas de consulta originales están disponibles en transvoxel.org.
Matemáticas de los pinceles
Un pincel de esculpido es una función que modifica los valores del campo escalar dentro de un radio alrededor de un punto objetivo. Las matemáticas son sorprendentemente similares en todas las implementaciones, desde Blender hasta Unreal Engine y los sistemas de juego en tiempo de ejecución.
Funciones de atenuación
La atenuación del pincel determina cómo disminuye la intensidad de la edición desde el centro hasta el borde. Blender 5.1 define estos perfiles estándar:
Suave: f(d) = 3d^2 - 2d^3 (interpolación de Hermite, el mismo smoothstep que en nuestro noise.ts)
Esfera: Intensa en el centro, con una atenuación pronunciada cerca del borde. Se aproxima como f(d) = sqrt(1 - d^2).
Afilada: f(d) = (1 - d)^n con n > 2. Crea una punta fina.
Lineal: f(d) = 1 - d, donde d es la distancia normalizada desde el centro —0 en el centro y 1 en el borde—.
Constante: f(d) = 1 para d < 1, con un corte abrupto en el límite del pincel.
Cuadrática inversa: Híbrido entre suave y esfera que proporciona una sensación natural, similar a la arcilla.
En todos los casos, d = distance_to_center / brush_radius, limitado al intervalo [0, 1]. El valor de atenuación multiplica la intensidad del pincel para producir la modificación real del campo en cada punto.
Espacio de atenuación
Blender distingue entre la atenuación esférica —distancia calculada en el espacio 3D del mundo— y la atenuación proyectada —distancia calculada en el espacio 2D de la pantalla—. La atenuación proyectada significa que dos puntos que parecen cercanos en pantalla se afectan por igual, aunque se encuentren a profundidades muy distintas en el espacio del mundo. Para esculpir terreno, la atenuación en el espacio 3D del mundo suele resultar más intuitiva.
Operaciones principales de los pinceles
Elevar/bajar (desplazamiento): Suma o resta valores del campo escalar dentro del radio del pincel, ponderados por la atenuación. Para mapas de altura: height[i] += strength * falloff(d). Para SDF: sdf[i] -= strength * falloff(d) —restar hace que el material sea más sólido y eleva la superficie—.
Suavizar (laplaciano): Sustituye cada valor por el promedio de sus vecinos, ponderado por la atenuación. Esto elimina detalles y reduce el ruido. El filtro laplaciano muestrea un kernel pequeño —3x3 para mapas de altura y 3x3x3 para volúmenes— y mezcla el valor hacia la media. El suavizado HC —Humphrey's Classes— es una variante que conserva mejor el volumen que el laplaciano puro.
Aplanar: Ajusta el valor del campo a una altura objetivo —o una distancia en el espacio SDF—, mezclándolo según la atenuación. El objetivo suele muestrearse en el centro del pincel cuando comienza el trazo y después se mantiene constante. Esto crea mesetas planas.
Pellizcar/inflar: Mueve los vértices hacia la normal de la superficie o en dirección contraria. En el espacio SDF, esto equivale a desplazar a lo largo de la dirección del gradiente.
Agarrar: Traslada una región del campo como si se tirara de arcilla. El vector de desplazamiento es el delta del ratón proyectado en el espacio del mundo y se aplica a los valores del campo dentro del radio.
Ruido: Añade ruido procedimental al campo dentro del radio del pincel. Resulta útil para dar rugosidad a superficies lisas.
Estampar: Aplica una imagen 2D en escala de grises como desplazamiento y la proyecta sobre la superficie situada bajo el cursor. La herramienta Landscape de Unreal Engine admite esta función para pinceles de terreno.
Teselación adaptativa
sculpt-3D —esculpido para navegador con React y Three.js— implementa teselación adaptativa: a medida que el pincel se desplaza por la malla, los triángulos cercanos a su centro se subdividen para proporcionar más vértices que deformar. Esto evita el problema del «estiramiento low-poly», en el que una malla de baja resolución se distorsiona al esculpirla. La subdivisión utiliza divisiones simétricas para mantener uniforme la calidad de los triángulos.
En los sistemas volumétricos, la teselación adaptativa no se necesita de la misma manera porque la malla se regenera a partir del campo. En su lugar, se puede aumentar localmente la resolución de los vóxeles cerca de las ediciones —mediante octrees adaptativos— para obtener el mismo efecto.
Esculpido con SDF: el enfoque de Dreams
Dreams de Media Molecule (PS4, 2020) es el sistema de esculpido dentro de un juego más ambicioso que se ha publicado. Alex Evans presentó el enfoque técnico en SIGGRAPH 2015.
Representación
Dreams almacena la geometría como una función SDF compuesta en bloques de texturas volumétricas fp16 de 83^3. Cada escultura es una lista de entre 1 y 100.000 «ediciones», donde cada edición es una operación CSG —añadir, restar o colorear— con una forma primitiva —esfera, cubo, cilindro, cono, elipsoide, toroide, etc.— y un modo de mezcla. Los modos de fusión utilizan funciones de máximo suave y mínimo suave. Una fusión «suave» produce transiciones redondeadas entre primitivas, como si se unieran piezas de arcilla. Una fusión «dura» produce cortes booleanos nítidos. El usuario puede controlar el radio de fusión.
Renderizado
Dreams no extrae una malla de triángulos. En su lugar, renderiza directamente desde el SDF mediante un renderizador de nubes de puntos personalizado («flecks»). Cada fleck es un disco diminuto orientado según la normal de la superficie. Se muestrea el SDF para encontrar superficies y se distribuyen flecks sobre ellas. Esto evita por completo el cuello de botella de la extracción de mallas, pero requiere un renderizador personalizado.
Este enfoque no puede aplicarse directamente a un mundo de Three.js/WebGL. Tendríamos que extraer mallas. Sin embargo, el concepto de una lista de ediciones CSG resulta muy relevante para deshacer/rehacer y para la sincronización de red.
Motor SDF dinámico de Mike Turitzin (2026)
Un motor de juegos que Mike Turitzin está desarrollando actualmente utiliza SDF dinámicos como representación principal. El motor admite:
Modificaciones detalladas durante la partida: añadir y eliminar materia de forma suave o con bordes definidos. Cambios no destructivos, como mover agujeros o crear túneles temporales que desaparecen detrás del jugador.
Mapas y atlas de bricks para almacenamiento en caché disperso. En lugar de almacenar todo el campo SDF en una cuadrícula 3D densa, el campo se divide en «bricks» (pequeños bloques 3D). Solo se asignan los bricks que contienen el límite de la superficie. Esto reduce drásticamente el uso de memoria en escenas compuestas principalmente por espacio vacío o sólido.
Clipmaps de geometría (Losasso y Hoppe, SIGGRAPH 2004) para el LOD. Alrededor de la cámara se disponen cuadrículas regulares anidadas con resoluciones crecientes. La cuadrícula más interna tiene la resolución más alta y las exteriores son progresivamente menos detalladas. Esto reduce drásticamente el uso de memoria y, al mismo tiempo, permite trabajar con espacios enormes. Los clipmaps se actualizan de forma incremental a medida que se mueve la cámara, por lo que resultan eficientes para el streaming de mundos abiertos.
La física y las colisiones operan directamente sobre el SDF. El trazado de esferas (ray marching con la distancia del SDF como tamaño de paso) permite realizar raycasts de forma eficiente. La detección de colisiones utiliza el gradiente del SDF como normal de la superficie y el valor de distancia como profundidad de penetración.
Teardown: destrucción de vóxeles a gran escala
Teardown (Voxagon) representa el otro extremo del espectro: cada objeto del mundo es un volumen de vóxeles que puede destruirse pieza a pieza.
Arquitectura
Los objetos se almacenan como cuadrículas de vóxeles con espaciado regular. El motor no utiliza Marching Cubes ni SDF para el renderizado. En su lugar, traza rayos directamente contra los vóxeles mediante un algoritmo DDA (Digital Differential Analyzer) modificado en shaders de fragmentos, implementado sobre OpenGL 3.3. Los mipmaps forman una estructura de octree densa que acelera el recorrido del espacio vacío durante la intersección de los rayos.
Para cada objeto, el motor rasteriza su caja delimitadora orientada (OBB) y traza un rayo a través de ella para encontrar intersecciones con los vóxeles. Solo se renderizan las caras posteriores de la OBB, lo que permite que la cámara entre en el volumen delimitador.
Sincronización de la destrucción (multijugador)
La actualización multijugador de Teardown de marzo de 2026 utiliza un enfoque semideterminista. La destrucción estructural (abrir agujeros, cambiar la propiedad o reconectar uniones) se gestiona mediante aritmética de enteros de punto fijo a través de un flujo de red fiable. Todos los clientes ejecutan los mismos comandos deterministas y llegan al mismo estado del mundo. Los cambios no estructurales (escombros y partículas) utilizan una sincronización de estado no fiable.
Esta es una conclusión importante para nuestro mundo multijugador: las ediciones del terreno deben ser deterministas. Si el jugador A esculpe una montaña, todos los clientes deben producir la misma malla a partir de los mismos datos de campo. Los comandos de edición (posición, radio y fuerza del pincel, y tipo de operación) deben ser los datos autoritativos, no la malla resultante.
ALICE-SDF: compresión y árboles CSG
ALICE-SDF (Adaptive Lightweight Implicit Compression Engine, v1.3.0 de marzo de 2026) proporciona una implementación en Rust de datos espaciales basados en SDF con una compresión de entre 10 y 1000 veces superior a la de las mallas poligonales. Admite:
126 bloques de construcción: 72 primitivas, 24 operaciones, 7 transformaciones y 23 modificadores. Operaciones de fusión suave (unión, sustracción e intersección), así como fusiones de bisel y escalones para crear biseles de bordes duros y transiciones CSG escalonadas.
Diff/patch de árboles CSG para deshacer/rehacer y sincronizar a través de la red. Esta es la función clave para el esculpido multijugador: en lugar de enviar el estado completo del campo, se envía la diferencia estructural entre dos árboles CSG. El cliente aplica el parche para reconstruir el nuevo estado. Esto consume mucho menos ancho de banda que aplicar compresión delta a datos de vóxeles sin procesar.
Optimización de árboles CSG, incluida la eliminación de transformaciones identidad, la combinación de transformaciones anidadas y la degradación de modificadores. Esto mantiene compacto el árbol a medida que se acumulan las ediciones.
Generación de mallas mediante Marching Cubes y Dual Contouring. La detección de colisiones físicas opera directamente sobre el SDF.
Compatibilidad con WebAssembly para facilitar la integración en el navegador. El motor está escrito en Rust y dispone de bindings para WASM, por lo que es una opción realista para una aplicación Three.js.
Cómputo con WebGPU para terrenos
Desde finales de 2025, WebGPU está disponible en todos los navegadores principales. Chrome 113+, Edge 113+, Firefox 141+ y Safari 26+ lo incluyen habilitado. Esto permite utilizar pipelines de shaders de cómputo que anteriormente solo estaban disponibles en la GPU.
Rendimiento
Gracias al paralelismo masivo, los shaders de cómputo de WebGPU generan terrenos aproximadamente 100 veces más rápido que los métodos basados en CPU. La GPU ejecuta miles de cálculos simultáneamente y la generación de terreno es casi totalmente paralela, ya que cada vértice o vóxel es independiente.
El trabajo se organiza en tres niveles: nivel de despacho (distribución de la carga de trabajo por la GPU), nivel de grupo de trabajo (memoria compartida dentro de una unidad de procesamiento) y nivel de hilo (cálculos individuales). WGSL (WebGPU Shading Language) es el lenguaje de shaders.
Pipeline de esculpido de terreno en tiempo real
Un pipeline de esculpido con WebGPU tendría este aspecto:
Aplicación del pincel (shader de cómputo): actualizar los valores del campo escalar dentro del radio del pincel. Cada hilo se encarga de un vóxel. Lee los parámetros del pincel (posición, radio, fuerza, tipo de atenuación y operación) desde un búfer uniforme y aplica la modificación.
Extracción de la malla (shader de cómputo): ejecutar Surface Nets o Marching Cubes en la región modificada. El editor SDF para WebGPU de Nijhoff lo implementa como un pipeline de varias etapas: partición espacial en 16 384 celdas, subdivisión de celdas basada en octrees y, después, extracción de la superficie.
Actualización del búfer de vértices (en la GPU): escribir los vértices extraídos directamente en un búfer de renderizado sin hacer un recorrido de ida y vuelta por la memoria de la CPU.
Cálculo de normales (shader de cómputo): calcular las normales de los vértices a partir de la malla o del gradiente del SDF.
Renderizado (pipeline estándar): dibujar la malla con materiales PBR estándar.
Los pasos 1-4 pueden ejecutarse íntegramente en la GPU sin devolver ningún dato a JavaScript. La CPU solo tiene que enviar los parámetros del pincel en cada fotograma.
El editor SDF para WebGPU
El editor SDF para WebGPU de Reinder Nijhoff (enero de 2026) demuestra este enfoque en Chrome. Admite seis primitivas (cono, cilindro, cápsula, toro, caja y esfera), tres operaciones de fusión (unión, sustracción e intersección) con fusión suave configurable y grafos de escena jerárquicos. Cada primitiva ocupa 112 bytes en un único búfer de la GPU.
El pipeline de renderizado utiliza 1024 mapas de sombras para la oclusión ambiental y antialiasing temporal. Funciona a tasas de fotogramas interactivas en GPU de gama alta.
Esculpido de mapas de alturas: el camino más sencillo
Si no es necesario admitir cuevas ni salientes, el esculpido de mapas de alturas evita por completo el pipeline volumétrico. Así es como la mayoría de los juegos publicados gestionan la edición de terrenos.
Patrón de implementación en tiempo de ejecución
Unity Runtime Terrain (JohannHotzel, enero de 2026) muestra el patrón estándar:
- Lanzar un rayo desde la cámara a través de la posición del ratón para encontrar el punto de impacto sobre el terreno.
- Convertir el punto de impacto a coordenadas del mapa de alturas.
- Aplicar el pincel a los valores cercanos del mapa de alturas, ponderados según la atenuación.
- Actualizar la malla estableciendo las posiciones Y de los vértices a partir del mapa de alturas modificado.
- Reconstruir el colisionador físico para que coincida con la nueva malla.
En nuestro terreno de Three.js, los pasos 1-4 encajan directamente en la arquitectura existente. La clase Chunk ya almacena mapas de alturas y construye mallas a partir de ellos. Añadir el esculpido supondría:
- Un sistema de raycasting contra las mallas de los chunks (
Raycasterde Three.js) - Funciones de aplicación del pincel que modifiquen los valores de
chunk.heightmap - Actualizaciones de los vértices de la malla (establecer las posiciones Y y recalcular las normales)
- Recálculo del mapa splat para la región afectada, de modo que la mezcla de texturas refleje la nueva pendiente o altura
- Difusión de la edición por la red (posición, radio y fuerza del pincel, y operación) a los demás clientes mediante el protocolo WebSocket existente
El enfoque de clipmaps
Landow.dev describe un «clipmap errante» para terrenos basados en mapas de alturas: una única malla con una densidad de subdivisión variable que sigue al jugador. En lugar de dividir el mapa de alturas en mallas separadas con distintos niveles de LOD, como hacemos ahora, el clipmap es una malla continua con alta densidad cerca de la cámara y menor densidad en los bordes.
Esto elimina por completo la necesidad de unir niveles de LOD. La malla simplemente tiene más triángulos donde se necesitan y menos donde no. La contrapartida es que el esculpido exige actualizar una única malla grande en lugar de chunks individuales, lo que puede resultar costoso para ediciones extensas.
Mapas de alturas SDF no destructivos
Landow.dev también describe una técnica en la que el propio mapa de alturas se genera a partir de una composición SDF. Las instancias de formas (esferas, cajas y funciones de ruido) se combinan en un shader de cómputo mediante operaciones CSG, y el resultado se muestrea como un mapa de alturas. Esto permite una edición no destructiva —se puede mover o eliminar cualquier instancia de forma en cualquier momento— sin renunciar a la sencillez del renderizado mediante mapas de alturas.
Se trata de un híbrido muy atractivo: la representación de datos es volumétrica (un árbol CSG de SDF), pero la ruta de renderizado utiliza una malla estándar de mapa de alturas. El lado SDF aporta las operaciones de edición fáciles de deshacer, rehacer y sincronizar por red, mientras que el lado del mapa de alturas ofrece un renderizado y una física sencillos. La limitación permanece: no admite cuevas ni salientes.
Octrees de vóxeles dispersos para mundos grandes
Las cuadrículas 3D densas no escalan. Un mundo de 1 km por lado con una resolución de 0,5 m necesitaría 8000 millones de vóxeles. Los octrees de vóxeles dispersos (SVO) resuelven este problema subdividiendo recursivamente el espacio y asignando almacenamiento únicamente a los octantes que contienen el límite de la superficie.
Un SVO proporciona LOD jerárquico de forma natural: la profundidad del árbol en cada punto determina la resolución efectiva. Cerca del jugador, el árbol está totalmente expandido y ofrece el máximo detalle. A lo lejos, se trunca en un nivel menos detallado.
Para el renderizado, los SVO pueden trazarse directamente mediante rayos, sin necesidad de extraer una malla. Un ray marcher de GPU intersecta los rayos con cajas alineadas con los ejes en cada nivel del árbol y omite por completo los subárboles vacíos. Esto elimina los problemas de sobredibujado del renderizado basado en chunks y evita los artefactos del mallado voraz.
El constructor de SVO basado en Vulkan de AdamYuan demuestra un rendimiento notable: 19 ms para construir Crytek Sponza con una resolución de 2^10 en una GTX 1660 Ti.
La modificación de SVO es eficiente para el esculpido: solo es necesario actualizar los nodos hoja que estén dentro del radio del pincel, y la estructura del árbol gestiona de forma natural las distintas resoluciones. La propia estructura de datos permite añadir detalle donde esculpe el jugador —dividiendo los nodos para obtener una resolución mayor— y reducirlo donde suaviza —fusionando los nodos para obtener una resolución menor—.
El reto para el despliegue en navegadores es que WebGL no admite shaders de cómputo y, aunque WebGPU sí lo hace, los algoritmos de construcción y recorrido de SVO son complejos de implementar en WGSL.
Sincronización de red para el esculpido multijugador
Nuestro mundo ya ofrece multijugador mediante Cloudflare Durable Objects (world-chunk-do.ts). Añadir el esculpido implica sincronizar las modificaciones del terreno entre todos los clientes conectados.
Compresión delta
Enviar datos de vóxeles sin procesar resulta costoso. Un estudio de 2024 de la Universidad de Oulu logró mejorar el tamaño de la carga útil entre 2 y 8 veces combinando la codificación delta con la compresión DEFLATE, y empaquetó las actualizaciones de vóxeles en menos de un byte por vóxel. El códec SDEC demuestra que la codificación delta empaquetada en bits produce paquetes con un tamaño medio de 259 bytes, frente a los 1114 bytes de una serialización genérica.
Sincronización basada en operaciones (recomendada)
En lugar de sincronizar el estado del campo, se sincronizan las operaciones. Cada acción de esculpido se convierte en un mensaje:
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
}
}El servidor difunde este mensaje a todos los clientes, y cada cliente aplica la misma operación determinista del pincel a sus datos locales del terreno. Es el mismo enfoque que utiliza Teardown para la destrucción estructural: comandos deterministas transmitidos por un flujo fiable.
El sistema diff/patch de árboles CSG de ALICE-SDF lleva esta idea más lejos: en lugar de representar pinceladas individuales, la diferencia representa el cambio estructural de todo el árbol CSG. Esto permite deshacer y rehacer de forma eficiente a través de la red —enviando el parche inverso—, y los clientes que se conecten más tarde pueden reconstruir el estado completo del mundo reproduciendo el registro de operaciones.
Prioridad y limitación de frecuencia
Las ediciones del terreno cercanas a jugadores conectados deben tener una prioridad alta y difundirse de inmediato. Las ediciones alejadas de cualquier jugador pueden agruparse y enviarse con menor frecuencia. La red de vóxeles de Enshrouded utiliza este patrón: actualizaciones a 60 Hz para el terreno próximo a los jugadores y a 10 Hz para las regiones en segundo plano.
La compresión ZSTD durante la transmisión reduce hasta un 60 % el tamaño de los paquetes de mensajes de actualización del terreno.
Esculpido colaborativo: ediciones simultáneas
Cuando varios jugadores esculpen simultáneamente la misma región, es necesario resolver los conflictos. cSculpt (CNR Visual Computing Lab, 2016) resolvió este problema con un algoritmo de fusión multirresolución. Cada edición se representa a múltiples escalas, y las ediciones simultáneas que se solapan se fusionan combinando sus representaciones multirresolución. Para nuestros fines, funciona un enfoque más sencillo: prevalece la última escritura según el orden del servidor. El Durable Object asigna una marca de tiempo a cada edición y las difunde en orden. Todos los clientes aplican las ediciones en la misma secuencia. Como las pinceladas son pequeñas, localizadas y aditivas o sustractivas, el resultado visual de reordenar ligeramente las ediciones simultáneas suele ser indistinguible del orden «correcto».
INST-Sculpt: edición neuronal de SDF (frontera de investigación)
INST-Sculpt (arxiv 2502.02891, febrero de 2025) permite editar SDF neuronales mediante pinceladas. Los usuarios dibujan trazos sobre la superficie y el sistema deforma el campo neuronal subyacente a lo largo de zonas tubulares alrededor del recorrido del trazo. Los perfiles de pincel personalizados (secciones transversales configurables) controlan la forma de la deformación.
Esto resulta interesante para terrenos generados por IA: si el mundo base se representa como un SDF neuronal (una pequeña red neuronal que transforma coordenadas 3D en distancias con signo), el esculpido modifica los pesos de la red en lugar de datos explícitos de vóxeles. La representación es extremadamente compacta (unos pocos MB para un mundo entero), pero evaluarla resulta más costoso que consultar una tabla.
Esta tecnología todavía se encuentra en fase de investigación. Actualmente, el coste de inferencia de los SDF neuronales en hardware de consumo es demasiado alto para usarlos en juegos en tiempo real. Sin embargo, merece la pena seguir su evolución, especialmente a medida que mejoren las capacidades de los shaders de WebGPU y se acelere la inferencia de modelos.
World Creator 2026.3: lo último en terrenos comerciales
World Creator (BiteTheBytes, marzo de 2026) representa lo último en herramientas comerciales para la creación de terrenos. La versión 2026.3 añadió generación de terrenos basada en GPU con adaptación automática del terreno (el terreno se ajusta a los objetos colocados), distribución de objetos centrada en la cámara para optimizar el LOD e importación de datos de elevación reales (GeoTIFF, HGT y DTED).
Desde entonces, World Creator 2026.4 (28 de abril de 2026) añadió expresiones matemáticas en campos numéricos, mezcla de normales del terreno para integrar objetos principales en la superficie, compatibilidad completa con calcomanías y escalado según la VRAM que ajusta el número máximo de objetos a la memoria de GPU disponible. BiteTheBytes también publicó una Community Edition gratuita con todas las funciones, pero sin posibilidad de exportar, lo que en la práctica supone una prueba ilimitada.
Su enfoque utiliza cómputo en GPU para todas las operaciones del terreno: simulación de erosión, excavación de ríos y pintura de texturas. Las herramientas de pincel están aceleradas por GPU y ofrecen respuesta en tiempo real en el visor. Esto coincide con el proceso de cómputo de WebGPU descrito anteriormente, ejecutado en GPU de escritorio.
Lo que ya hemos creado: 24 prototipos y un mundo en producción
El directorio world/spikes/ contiene 24 prototipos independientes. No son demostraciones de juguete. Forman un proceso progresivo de I+D en el que cada prototipo resolvió un problema específico, midió su rendimiento respecto a un objetivo y sirvió de base para el siguiente. El sistema de esculpido se apoya en todos ellos, no solo en los prototipos volumétricos posteriores.
El terreno de mapa de alturas en producción (world/client/)
El mundo activo utiliza un sistema segmentado de mapas de alturas en Three.js WebGL:
noise.tsgenera la altura del terreno mediante ruido de valores FBM (5 octavas para colinas, 4 para crestas y 3 para microdetalle) con una función deterministaterrainHeight(wx, wz)chunk.tscrea mallasPlaneGeometrya partir de mapas de alturasFloat32Arraycon 3 niveles de LOD (32/8/4 segmentos por chunk de 64 unidades) y coloca árboles y carteles instanciados mediante una distribución aleatoria con semilla y colisionadores individuales por objetochunk-manager.tstransmite chunks en anillos alrededor del jugador (radio 1 en LOD0, radio 3 en LOD1 y radio 6 en LOD2), conecta los bordes mediante interpolación lineal enstitchEdge()y proporcionagetHeight(),getNormal()yresolveCollisions()a la capa de físicasterrain-material.tsrealiza una mezcla de texturas basada en mapas de salpicado con 4 capas (hierba/roca/arena/tierra) medianteMeshStandardMaterial.onBeforeCompile, con pesos determinados por la pendiente y la altura, además de mezcla de mapas de normales por capacharacter-controller.tsmuestrea la altura del terreno en cada fotograma para gestionar la gravedad, el contacto con el suelo y el rechazo de pendientes (coseno máximo de pendiente de 50 grados). El esculpido debe suministrar de inmediato las alturas modificadas a este sistema o el jugador atravesará el terreno editadoplacement.tsya dispone de unRaycasterque detecta las mallas de los chunks para la herramienta de colocación de objetos. La herramienta de pincel debería seguir exactamente este patrón en lugar de implementar la detección por rayos desde ceroprotocol.tsdefine mensajes codificados con MessagePack para la sincronización multijugador mediante el Durable Objectworld-chunk-do.ts, que actualmente gestiona mensajesPlayerState,PlaceObject,RemoveObjectySnapshot. Las ediciones del terreno necesitarán un nuevo tipo de mensajeworld-chunk-do.ts(Cloudflare Worker) conserva los objetos colocados en el almacenamiento del Durable Object y los difunde a los jugadores conectados a intervalos de 50 ms. Todavía no contempla las modificaciones del terreno
Prototipos 01-11: la capa base
Estos prototipos validaron los sistemas fundamentales de los que dependerá el esculpido. Omitirlos implica pasar por alto restricciones que el sistema de esculpido debe respetar.
Prototipo 01 (Terreno + instanciación): El primer prototipo de terreno en Three.js. Estableció el patrón de PlaneGeometry + mapa de alturas y la colocación de objetos instanciados que chunk.ts todavía utiliza.
Prototipo 02 (Worker de físicas Rapier): Rapier 3D ejecutándose en un Web Worker con un colisionador ColliderDesc.heightfield(). Se creó un controlador de personaje cinemático con subida automática de escalones, límites de pendiente y ajuste al suelo. Este prototipo demostró que las físicas pueden ejecutarse fuera del hilo principal usando un campo de alturas. Si esculpimos el terreno, habrá que reconstruir el campo de alturas de las físicas o sustituirlo por un colisionador de malla triangular para los chunks MC.
Prototipo 05 (Comportamientos con LLM): No está relacionado directamente con el terreno, pero estableció el esquema JSON de comportamiento para los objetos del juego. Es relevante porque las formas esculpidas del terreno podrían activar comportamientos (por ejemplo, un río excavado genera efectos de agua).
Prototipo 06 (Transmisión de chunks): Primer sistema de carga e intercambio de chunks, con carga dinámica a medida que se mueve el jugador. Estableció el patrón que utiliza chunk-manager.ts: regiones coloreadas que se cargan y descargan. El esculpido debe conservar el estado de las ediciones cuando los chunks se descarguen y vuelvan a cargarse.
Prototipo 07 (Vegetación en GPU a partir de mapas de densidad): Hierba y árboles instanciados colocados mediante mapas de densidad que muestrean la altura y la pendiente del terreno. El esculpido invalida la colocación de la vegetación: si cambia la altura del terreno, los árboles pueden acabar flotando o enterrados. El mapa de densidad debe regenerarse para los chunks editados.
Prototipo 08 (Coste del shader de material del terreno): Midió el rendimiento de la proyección triplanar, los mapas de normales y la mezcla de 4 capas. Calculó el coste exacto en ms de cada función. Determinó que la proyección triplanar + normales + 4 capas se mantiene dentro del presupuesto a más de 45 FPS. Este presupuesto es importante para el terreno esculpido: si añadimos una quinta capa para «tierra editada» o cambiamos la mezcla de las superficies excavadas, sabemos exactamente cuánto margen queda.
Prototipo 09 (Presupuesto de sombras CSM): Mapas de sombras en cascada con 3 cascadas a una resolución de 1024^2. Midió el coste de las sombras en unos 1,5 ms. El terreno esculpido modifica los mapas de sombras, pero el coste permanece constante independientemente de la forma del terreno.
Prototipo 10 (Clipmaps de geometría + geomorphing): Anillos de clipmap anidados con geomorphing entre niveles de LOD para eliminar las apariciones repentinas. El número constante de triángulos permite predecir el coste para la GPU. El geomorphing es importante para el esculpido: cuando el jugador esculpe cerca del límite de un LOD, la transición entre los niveles de LOD debe reflejar la edición. Si la edición solo existe en el anillo de alta resolución, el objetivo del geomorphing será incorrecto.
Prototipo 11 (Transmisión de chunks de mapas de alturas): Transmisión de chunks más avanzada, con una cuadrícula visual que muestra los estados cargado/cargando/descargado de cada nivel de LOD. Estableció el presupuesto de transmisión: número máximo de chunks cargados por fotograma y priorización de los chunks que necesitan mejoras de LOD. El esculpido añade una nueva señal de prioridad: los chunks que el jugador esté editando activamente nunca deben descargarse.
Prototipos 12-14: integración de WebGPU + Three.js
Prototipo 12 (Marching Cubes con WebGPU): El primer prototipo volumétrico. Cuatro chunks SDF de 64^3 con cuevas esféricas animadas, ejecutado íntegramente en la GPU. Utiliza WebGPU sin abstracciones: procesos de cómputo para evaluar el SDF, extracción MC con la tabla de casos de Twinklebear (256 configuraciones, 16 entradas cada una), contador atómico de vértices y dibujo indirecto. El objetivo de rendimiento era <4 ms por chunk y <12 ms para los 4. Esto confirmó que MC en GPU es lo bastante rápido para volver a generar mallas en tiempo real en el navegador. Todos los prototipos volumétricos posteriores reutilizan la tabla de casos MC y los shaders WGSL definidos aquí.
Prototipo 13 (Restablecimiento de la base desde el prototipo 12): Adaptó la ruta de dibujo de WebGPU sin abstracciones del prototipo 12 para ejecutarla dentro del WebGPURenderer de Three.js, accediendo directamente al device del backend. El proceso de renderizado sigue utilizando WebGPU sin abstracciones (drawIndirect con struct Vertex vec4+vec4). Esto demostró que el cómputo personalizado y el renderizado de escenas de Three.js pueden coexistir en el mismo dispositivo GPU.
Prototipo 14 (Robustecimiento incremental de WebGPU en Three.js): Sustituyó el proceso de renderizado sin abstracciones por StorageBufferAttribute de Three.js para posiciones y normales. El cómputo MC escribe directamente en estos búferes residentes en la GPU. El búfer drawIndirect controla cuántos vértices dibuja Three.js. Este es el patrón que utilizan todos los prototipos posteriores: el cómputo permanece en WebGPU sin abstracciones y el renderizado pasa por el grafo de escena de Three.js. La versión de Three.js empleada en estos prototipos avanzó de la 0.170.0 a la 0.172.0 a medida que se estabilizaba el backend de WebGPU.
Prototipos 15-17: unión de LOD con Transvoxel
Prototipo 15 (Estructura de juntas Transvoxel): Añadió la arquitectura de tres zonas: chunk MC (centro volumétrico), franja de transición (junta entre el límite MC y el mapa de alturas) y anillo de terreno (mapa de alturas circundante). Las tres comparten una pasada de material. En esta fase, la franja de transición es una malla provisional, no contiene celdas Transvoxel reales.
Prototipo 16 (Cara +X de Transvoxel con mapa de alturas compartido): Dos avances fundamentales en un solo prototipo. Primero, sustituyó el plano de terreno del SDF por un mapa de alturas Perlin compartido: un Float32Array de 257x257 subido a la GPU como búfer de almacenamiento y muestreado mediante interpolación bilineal en el shader de cómputo del SDF. La superficie MC y la malla del mapa de alturas ahora coinciden en la misma fuente de verdad. Segundo, implementó celdas de transición Transvoxel reales para la cara +X obteniendo de GitHub las tablas de datos de referencia de Eric Lengyel (transitionCellClass, transitionVertexData, transitionCellData) y el paquete npm transvoxel-data. La CPU evalúa celdas de transición de 9 muestras (512 configuraciones, 73 clases de equivalencia), coloca vértices interpolando los valores SDF en los puntos de la cuadrícula y gestiona la inversión del orden de los vértices en los casos reflejados.
Prototipo 17 (LOD MC doble 1x/2x): Dos chunks MC adyacentes con resoluciones distintas. Alta resolución: 62 celdas con cell_scale=1.0. Baja resolución: 31 celdas con cell_scale=2.0. El shader MC incorporó los uniformes cell_scale y grid_points. Introdujo transition_shrink: los vértices del límite de la cara 0 del chunk de baja resolución se retraen un 15 % de cell_scale, creando un espacio estrecho que las celdas de transición Transvoxel rellenan sin z-fighting. Este es el modelo de LOD que necesita el sistema de producción: chunks cercanos a resolución completa, chunks lejanos a media resolución y Transvoxel en todos los límites.
Prototipos 18-21: casos límite de Transvoxel y aceleración por GPU
Cada uno de estos cuatro prototipos resolvió un fallo específico de la implementación de Transvoxel. Agruparlos oculta sus distintos problemas.
Prototipo 18 (Junta 2:1 de mapas de alturas): Aplicó Transvoxel a un límite compuesto únicamente por mapas de alturas, donde un lado tiene el doble de resolución que el otro. Sin MC. La junta entre un chunk de mapa de alturas de 62 celdas y otro de 31 se genera a partir de las tablas de transición de Transvoxel, con una retracción del 15 % en la cara de baja resolución. Esto confirmó que Transvoxel funciona para el caso compuesto únicamente por mapas de alturas, no solo con MC.
Prototipo 19 (Cuadrícula de esquina 64/32/32/16): El caso de unión más difícil: cuatro chunks de distintas resoluciones que convergen en un punto de esquina (64, 32, 32 y 16 celdas). El sistema de juntas debe generar celdas de transición a lo largo de cuatro bordes (A-B, A-C, B-D y C-D) con el orden de vértices correcto para cada dirección. Este prototipo demostró que las tablas de Transvoxel gestionan la esquina multirresolución sin lógica personalizada para casos especiales.
Prototipo 20 (Esquina Transvoxel en GPU): Trasladó a la GPU la generación de celdas de transición Transvoxel para la disposición de esquina 64/32/32/16. La CPU suponía un cuello de botella al regenerar las celdas de transición en cada fotograma para el terreno animado. El cómputo en GPU genera los vértices de la junta en la misma pasada que la extracción MC.
Prototipo 21 (Esquina MC + Transvoxel en GPU): Combinó la extracción MC completa en GPU con la generación de juntas Transvoxel en GPU en una única secuencia de envíos de cómputo. Tanto los chunks MC como las cuatro juntas se generan en la GPU, con recuentos de vértices gestionados mediante contadores atómicos y dibujados con drawIndirect. Este es el proceso completo en GPU para terrenos volumétricos multirresolución con transiciones de LOD sin discontinuidades.
Prototipos 22-24: la arquitectura híbrida
Prototipo 22 (Política híbrida MC/mapa de alturas): El prototipo arquitectónico clave. De forma predeterminada, los chunks son mapas de alturas. Cuando la esfera de deformación animada interseca el AABB de un chunk, ese chunk cambia al modo MC. Los demás permanecen como mallas estáticas de mapas de alturas. Disposición: chunks de 64, 32/32 y 16 celdas con distintas resoluciones. Las juntas Transvoxel gestionan todos los límites, incluidas las transiciones de MC a mapa de alturas. El prototipo registra el número de chunks MC frente al número de chunks HM y el desbordamiento de vértices por fotograma.
Prototipo 23 (Modos de chunk controlados por políticas): Cargado como parche sobre el prototipo 22. Añadió histéresis según la distancia a la cámara (los chunks no alternan rápidamente entre modos cuando la cámara está cerca de un umbral) y una máscara de edición (los chunks deformados permanecen en modo MC aunque se aleje la fuente de deformación). Este es el comportamiento de «edición persistente» que necesita el esculpido: una vez que un jugador excava una cueva, ese chunk permanece volumétrico para siempre.
Prototipo 24 (Política + anillos de clipmap): El prototipo más avanzado. Combina el sistema de políticas de campo cercano del prototipo 23 con los anillos de clipmap de geometría de campo lejano del prototipo 10. Se actualizó a Three.js 0.183.1. El campo cercano utiliza un híbrido HM/MC con juntas Transvoxel a resoluciones 64/32/16. El campo lejano utiliza anillos de clipmap con centro estático que siguen a la cámara. Esta es la arquitectura completa de renderizado del terreno: esculpido volumétrico por chunks donde resulte necesario y terreno económico mediante clipmaps en el resto del mundo.
Por qué Marching Cubes y no Surface Nets
La sección de investigación externa de esta guía propone Surface Nets como el candidato más sólido para esculpir terreno en el navegador. Sin embargo, todas las pruebas del pipeline usan Marching Cubes. No es casualidad.
La principal ventaja de MC es su paralelismo trivial: cada cubo es completamente independiente. Los shaders de cómputo WGSL de las pruebas 12-24 ejecutan un hilo por cubo sin ninguna comunicación entre celdas. Los contadores atómicos gestionan la asignación de vértices. Esto encaja perfectamente con los grupos de trabajo de la GPU.
Surface Nets coloca un vértice por cada celda que contiene superficie y después conecta las celdas vecinas. Esa conectividad entre celdas introduce una dependencia. El crate fast-surface-nets la gestiona en la CPU mediante un orden de iteración cuidadosamente diseñado. En la GPU, requiere un enfoque de dos pasadas —primero encontrar los vértices y después conectarlos— o memoria compartida dentro de los grupos de trabajo. Ambas opciones son posibles en WebGPU, pero añaden complejidad.
La recomendación práctica es mantener Marching Cubes para el pipeline de esculpido. Ya está probado en nuestro código, los shaders WGSL existen y se han sometido a benchmarks, y el sistema de costuras Transvoxel está diseñado en torno a la colocación de vértices basada en aristas de MC. Merece la pena reconsiderar Surface Nets si el aliasing de MC sobre datos binarios se convierte en un problema visible, pero MC produce resultados limpios en terrenos SDF, donde los valores son gradientes suaves.
Arquitectura práctica para el esculpido
La secuencia de pruebas resolvió el pipeline de renderizado. Quedan por resolver el sistema de pinceles, los efectos secundarios en cascada sobre los sistemas del juego y la sincronización multijugador. Este es el plan, basado en todas las pruebas.
Fase 1: esculpido de mapas de altura (cambios mínimos, alcance máximo)
Añadir herramientas de pincel que modifiquen los mapas de altura de los chunks en el código de producción de world/client/. Esto funciona con el renderizador WebGL existente y no requiere WebGPU.
Entrada del pincel: Seguir el patrón de PlacementTool en placement.ts. Ya cuenta con un Raycaster que colisiona con chunkManager.getChunkMeshes() y sigue una malla fantasma en el punto de impacto. Un TerrainBrushTool realizaría el mismo raycast, pero modificaría el mapa de altura del chunk en lugar de colocar un objeto. El controlador World.onMouseDown ya deriva las acciones según el estado de la herramienta.
Modificación del chunk (Chunk.applyBrush): Convertir la posición del pincel en el mundo a coordenadas de la cuadrícula del mapa de altura. Para cada punto de la cuadrícula dentro del radio del pincel, calcular el desplazamiento ponderado por la atenuación y sumarlo o restarlo al valor del mapa de altura. Después, actualizar la malla: establecer las posiciones Y de los vértices a partir del mapa de altura modificado, recalcular las normales mediante diferencias centrales —el mismo patrón terrainHeight(wx +/- eps, wz) que ya se usa en las líneas 155-158 de chunk.ts— y regenerar el mapa de mezcla mediante createSplatMap() en terrain-material.ts para la región afectada, de modo que se actualice la mezcla de texturas determinada por la pendiente.
Controlador del personaje: CharacterController.update() llama a getHeight() en cada fotograma para mantener al personaje sobre el suelo. ChunkManager.getHeight() delega en Chunk.sampleHeight(), que lee del Float32Array heightmap del chunk. Como modificamos directamente ese array, el controlador del personaje detecta el cambio en el siguiente fotograma sin necesidad de conexiones adicionales.
Invalidación de objetos: Las instancias de árboles de chunk.ts se colocan muestreando terrainHeight() en el momento de su generación. Después del esculpido, los árboles de la zona afectada podrían quedar a una altura incorrecta. La fase 1 puede aplazar este problema —los árboles flotarán ligeramente tras ediciones pequeñas—. La fase 2 necesita un método chunk.invalidateObjects() que vuelva a muestrear las alturas y reconstruya las matrices de las instancias. Lo mismo se aplica a los colisionadores utilizados en resolveCollisions().
Física de Rapier (si está integrada): La prueba 02 demostró que los colisionadores de campos de altura funcionan. Si Rapier está activo, el colisionador de campo de altura debe reconstruirse o actualizarse para el chunk modificado. ColliderDesc.heightfield() de Rapier recibe un Float32Array plano, por lo que basta con sustituirlo directamente.
Sincronización de red: Añadir MsgType.TerrainEdit = 10 a protocol.ts:
interface TerrainEditMsg {
t: MsgType.TerrainEdit
cx: number
cz: number
brush: {
wx: number
wz: number
radius: number
strength: number
falloff: number
operation: number
}
}El WorldChunkDO lo transmite a todos los clientes y lo añade a un registro de ediciones por chunk almacenado en el Durable Object. Los clientes que se conecten más tarde reciben el registro de ediciones en el mensaje Snapshot y lo reproducen para reconstruir el estado del terreno. Todos los clientes aplican la misma función de pincel determinista, por lo que convergen en el mismo mapa de altura.
Descarga y recarga de chunks: Las pruebas 06 y 11 establecieron el patrón de streaming. Cuando un chunk se descarga y posteriormente se vuelve a cargar, el registro de ediciones de ese chunk debe reproducirse sobre el mapa de altura procedural base. El registro de ediciones se almacena en el servidor —en el Durable Object— y se incluye en el mensaje Snapshot.
Fase 2: esculpido volumétrico con la arquitectura de las pruebas 22-24
Integrar el pipeline de la prueba 24 en el mundo de producción. Cuando un jugador esculpe bajo la superficie —al crear una cueva o excavar un túnel—, el chunk afectado pasa del modo de mapa de altura al modo MC.
Migración del renderizador a WebGPU: Las pruebas 13-14 demostraron que WebGPURenderer de Three.js puede alojar cómputo personalizado junto con el grafo de escena. El mundo de producción pasa de WebGLRenderer a WebGPURenderer, con StorageBufferAttribute para los chunks MC. Cuando WebGPU no esté disponible, se utiliza como alternativa la ruta de la fase 1, limitada a mapas de altura.
Asignación de SDF por chunk: Seguir el patrón híbrido de la prueba 22. Cada chunk comienza como un mapa de altura. Con la primera pincelada volumétrica, se asigna un Float32Array de 64^3, se inicializa muestreando el mapa de altura —el valor SDF de cada punto es world.y - heightmap_value— y se cambia al renderizado MC. El sistema de políticas de la prueba 23 garantiza que el chunk permanezca en modo MC de forma permanente —el comportamiento de «edición persistente» de la máscara de edición—.
Mapa de altura compartido en el shader SDF: La función height_at() de la prueba 16. Subir el mapa de altura del chunk a un búfer de almacenamiento de la GPU. El shader de cómputo SDF evalúa max(height_sdf, edit_sdf), donde height_sdf = world.y - height_at(world.xz) y edit_sdf contiene las modificaciones del pincel. Los chunks MC y los chunks de mapa de altura comparten la misma referencia del terreno en sus límites.
Costuras Transvoxel: La pila completa de las pruebas 15-21. Los límites entre MC y mapas de altura usan celdas de transición con el hueco de contracción. Los límites entre chunks MC con distintas resoluciones usan el patrón de LOD dual de la prueba 17. El caso límite de la prueba 19 gestiona las intersecciones entre cuatro regiones. El cómputo en GPU de la prueba 21 genera toda la geometría de las costuras en la misma ejecución.
Campo lejano con clipmaps: Los anillos de clipmap de la prueba 24 para el terreno situado fuera del alcance de esculpido. El esculpido nunca modifica estos anillos; estos muestrean el mapa de altura procedural base.
Geomorfismo: El geomorfismo de la prueba 10 elimina las apariciones bruscas en las transiciones de LOD. En los chunks editados, el objetivo del geomorfismo debe incluir la edición. Si un chunk es MC en LOD0 y su vecino en LOD1 es un mapa de altura, el geomorfismo interpola entre ambas representaciones. Esto requiere muestrear el registro de ediciones incluso en los LOD inferiores.
Presupuesto de materiales: La prueba 08 midió un rendimiento de más de 45 FPS con triplanar de 4 capas y normales. Los chunks MC necesitan el mismo material. El mapa de mezcla puede generarse a partir del gradiente SDF —pendiente pronunciada = roca, superficie plana = hierba— en lugar de la pendiente del mapa de altura. Esto se mantiene dentro del presupuesto de 4 capas.
Invalidación de vegetación: La vegetación basada en mapas de densidad de la prueba 07 depende de la altura y la pendiente del terreno. Cuando un chunk pasa al modo MC, las instancias de árboles deben regenerarse muestreando la superficie SDF. Los árboles situados sobre salientes o dentro de cuevas deben descartarse. Las matrices de las mallas instanciadas de chunk.ts se reconstruyen a partir de la nueva superficie.
Fase 3: árboles de edición CSG para deshacer, rehacer y sincronizar por red
Sustituir la mutación directa del SDF por un árbol de operaciones CSG. Cada pincelada añade una primitiva —esfera, cápsula o caja— con una operación —añadir, sustraer o mezcla suave—. El SDF se vuelve a calcular a partir del árbol.
Ventajas:
- No destructivo: cualquier edición puede eliminarse del árbol para deshacerla
- Eficiente en red: se transmite la operación CSG, no los valores sin procesar del campo
- Determinista: todos los clientes construyen el mismo SDF a partir de la misma secuencia de operaciones
- El sistema de diferencias y parches de árboles CSG de ALICE-SDF permite una sincronización eficiente en ancho de banda, además de deshacer y rehacer a través de la red
Almacenamiento en Durable Object: El árbol de ediciones por chunk sustituye al registro plano de ediciones de la fase 1. El WorldChunkDO almacena la estructura del árbol CSG, no deltas sin procesar del mapa de altura. Los mensajes Snapshot incluyen el árbol y los clientes que se conecten más tarde lo evalúan para generar el SDF local.
Fase 4: esculpido colaborativo
Añadir compatibilidad con ediciones simultáneas mediante la reproducción de operaciones ordenadas por el servidor. El Durable Object asigna una marca de tiempo a cada edición y las transmite en orden. Los clientes que se conecten más tarde reciben el registro de operaciones y reconstruyen el estado del mundo. El tipo de mensaje Snapshot existente se amplía para incluir el historial de ediciones del terreno por chunk.
Como las pinceladas son pequeñas, localizadas y aditivas o sustractivas, el resultado visual de reordenar ligeramente las ediciones simultáneas suele ser indistinguible del orden «correcto». Una estrategia en la que prevalece la última escritura, con ordenación en el servidor, es suficiente. La función tick() del Durable Object —que actualmente se ejecuta en intervalos de 50 ms para el estado de los jugadores— añade las transmisiones de ediciones del terreno al mismo bucle.
Referencias clave
Algoritmos:
- Lorensen y Cline, "Marching Cubes" (1987)
- Eric Lengyel, "Transvoxel Algorithm" (2009), transvoxel.org
- Losasso y Hoppe, "Geometry Clipmaps" (SIGGRAPH 2004)
- "A High-Performance SurfaceNets Discrete Isocontouring Algorithm" (arxiv 2401.14906, 2024)
- MCHex (arxiv 2511.02064, 2025)
Implementaciones:
- bevy-sculpter v0.18.0 (Rust, Surface Nets + pinceles SDF)
- fast-surface-nets (Rust, 20 millones de triángulos/s)
- ALICE-SDF v1.3.0 (Rust + WASM, diferencias y parches de árboles CSG)
- WebGPU SDF Editor (Nijhoff, enero de 2026)
- SculptingPro (API de esculpido en tiempo de ejecución para Unity)
- TerraBrush (GDExtension de esculpido de terreno para Godot)
Juegos:
- Dreams (Media Molecule, SDF + renderizado de nubes de puntos, SIGGRAPH 2015)
- Teardown (Voxagon, trazado de rayos DDA sobre vóxeles, destrucción multijugador determinista)
- Motor SDF de Mike Turitzin (mapas de bloques + clipmaps de geometría, enero de 2026)
Red:
- "Optimizing payload size for voxel state synchronization" (Oulu, 2024)
- Multijugador de Teardown (sincronización semideterminista de la destrucción, marzo de 2026)
- cSculpt (esculpido colaborativo de mallas con fusión multirresolución)
Consigue un juego basado en el terreno sin tocar un pincel.