Skip to content

Construir un mundo abierto en el navegador, parte 28: Hierba hasta el horizonte y terreno que se oculta a sí mismo

Por Oleg Sidorkin, CTO y cofundador de Cinevva

¿Acabas de llegar? Consulta la guía de la serie. Explica qué es un spike y enlaza todas las partes.

La parte 27 construyó la isla y le dio un suelo que no parece repetitivo. Esta parte trata los dos elementos que hacen que un terreno parezca habitado en vez de vacío. El Spike 56 se ocupa de la hierba, el detalle superficial que convierte una ladera texturizada en un lugar por el que caminarías, y el problema consiste en lograr que un campo parezca un campo en lugar de un conjunto de trazos dispersos. El Spike 57 aborda lo contrario a dibujar más: no dibujar lo que una colina ya está ocultando. Y el resultado interesante es que la forma rápida de comprobarlo también resultó ser la correcta.

Hierba que parece un campo, no confeti

Abrir el Spike 56 en una pestaña nueva ↗ · Ver código fuente

La decisión fundamental es geométrica. Una única brizna fina y afilada ocupa menos de un píxel desde la mayoría de los ángulos de cámara, así que medio millón de briznas planas parecen confeti verde disperso en vez de un campo. La solución es una mata de quads cruzados: tres quads afilados rotados sesenta grados entre sí alrededor del eje vertical local, de modo que, desde cualquier dirección de visión, al menos uno quede casi perpendicular a la cámara y cada instancia cubra aproximadamente tres anchuras de brizna de área real en pantalla. Esa es la diferencia entre ver trazos verdes y ver hierba.

Un kernel de cómputo de WebGPU coloca cada mata una sola vez durante la inicialización. Convierte mediante hash el índice de instancia en cinco flujos aleatorios descorrelacionados, elige una posición XZ dentro del área, muestrea la altura del terreno a partir del mismo FBM que utiliza la malla de suelo de la CPU —un port a TSL con un mod que conserva el signo para que los valores coincidan exactamente y las briznas descansen sobre la superficie en vez de flotar sobre ella—, calcula una normal mediante diferencias centrales y genera una anchura, una altura y un tono para cada mata. En el lado del renderizado se usa un grafo de vértices TSL que omite por completo la matriz de instancia y escribe directamente en el espacio de recorte: escala el quad cruzado unitario, rota el eje vertical local hasta alinearlo con la normal del terreno mediante la fórmula de Rodrigues y lo traslada a la posición de la mata. El LOD por distancia se obtiene gratis y sin una pasada de descarte, porque el grafo de vértices multiplica la altura de la mata por 1smoothstep(fadeNear,fadeFar,dist); así, las matas lejanas se aplanan hasta tener altura cero y dejan de consumir relleno. El viento consta de dos octavas de seno y coseno sobre las coordenadas XZ de la mata en el mundo más el tiempo, se aplica horizontalmente y queda limitado por la fracción de altura para que la base permanezca anclada mientras se mueven las puntas. Además, una ligera inclinación dependiente de la cámara orienta cada mata hacia el espectador para que se abra en perspectiva en vez de parecer una tarjeta plana.

La fórmula de color procede del shader URP de Breath of the Wild creado por NedMakesGames: sombreado plano con el color de la brizna interpolado desde un tono de raíz hasta otro de punta a lo largo de la fracción de altura; a su vez, los tonos de raíz y punta se interpolan entre dos paletas mediante el valor de tono de cada mata. Esto produce el aspecto moteado de dos tonos que tiene un campo de hierba real de BotW. La iluminación difusa compara la normal del terreno con el sol e incluye un mínimo de luz ambiental, porque las normales de cada quad cruzado son demasiado ruidosas para sombrearlas individualmente con un estilo visual estilizado. Todo el campo se dibuja en una sola llamada y se coloca y anima por completo en la GPU.

Cuatro formas de descartar lo que oculta una colina

Abrir el Spike 57 en una pestaña nueva ↗ · Ver código fuente

El Spike 57 compara cuatro métodos de descarte sobre el mismo mundo procedimental que transmite por streaming el cliente de producción, escalado verticalmente 1,8 veces para que las colinas oculten de verdad el follaje. T0 solo tiene en cuenta la distancia, igual que el sistema de visibilidad de chunks que ya utiliza la versión publicada. T1 añade descarte por frustum, la mejora barata más importante, que elimina todo lo que queda fuera del cono de visión. T2 añade una prueba de horizonte que tiene en cuenta el terreno: recorre un rayo desde el ojo hasta cada instancia y la rechaza si el mapa de alturas supera el rayo en algún punto del trayecto, de modo que un árbol situado detrás de una cresta se descarta aunque esté dentro del frustum. T3 conserva la misma prueba de horizonte, pero la acelera mediante una pirámide de alturas máximas. T4 traslada toda la prueba de distancia, frustum y horizonte a un kernel de cómputo TSL que escribe una escala de visibilidad por instancia, leída por el material del follaje para colapsar los vértices ocultos. Un contador que mantiene la visibilidad durante dos fotogramas suaviza el parpadeo de un solo fotograma que se produce cuando un punto de muestreo cae justo dentro o fuera de un texel al mover ligeramente la cámara. Se mantiene corto deliberadamente, porque una duración mayor ocultaría errores algorítmicos en lugar de corregirlos.

La pirámide es donde el spike demuestra su valor, y la lección consiste en hacer que la prueba coincida con lo que realmente aparece en pantalla. El terreno renderizado es una malla de triángulos con vértices situados en una cuadrícula fija de dos metros, y entre los vértices el rasterizador interpola linealmente. Por tanto, la altura máxima real renderizada dentro de cualquier rectángulo es el máximo de los vértices que contiene, nunca el pico del ruido continuo situado entre ellos. Si la pirámide de oclusión muestrea el ruido en una cuadrícula más fina, encuentra picos fantasma que la malla nunca muestra y empieza a bloquear rayos que la cámara puede ver atravesar claramente. Por eso, los texeles de la base de la pirámide se sitúan exactamente sobre la cuadrícula de vértices y cada uno almacena el máximo de sus cuatro vértices de esquina. Los niveles superiores son reducciones máximas 2x2 convencionales, lo que hace que la pirámide sea exacta respecto a la malla renderizada. Para la prueba del rayo en cada paso, el código muestrea deliberadamente la malla por puntos mediante interpolación bilineal en vez de consultar la pirámide, porque una consulta AABB más pequeña que un texel devuelve el máximo de todo el texel y eleva la altura varios metros en crestas pronunciadas. Ese es precisamente el tipo de oclusión excesiva que ocultaría elementos visibles.

El resultado sorprendente es que T2, la referencia por fuerza bruta, es el método incorrecto. Como T2 muestrea directamente por puntos el ruido continuo, detecta picos entre vértices de la malla que nunca se renderizan, por lo que produce una ligera oclusión excesiva y oculta follaje que el jugador sí puede ver. El método de la pirámide es más rápido, gracias a la reducción AABB de O(logN) para las pruebas a nivel de chunk, y también más correcto geométricamente, porque solo puede devolver alturas que la malla muestra realmente. Esa es la idea central del spike: la estructura acelerada no es una concesión de calidad a cambio de velocidad, sino la versión que coincide con la realidad. El descarte de chunks ejecuta una prueba de cinco puntos por chunk —las cuatro esquinas superiores más el centro a la altura máxima del chunk— y excluye del conjunto de oclusores la huella de cada propio chunk para que nunca se ocluya a sí mismo. El método recomendado para producción es T4: enviar los metadatos de instancia y el campo de alturas a búferes de almacenamiento y ejecutar el mismo bucle en un shader de cómputo con dibujo indirecto, puesto que el renderizador ya está migrando a WebGPU.

Tecnología mencionada en este capítulo

Hierba de GPU con quads cruzados. Tres quads afilados rotados sesenta grados entre sí por mata garantizan que uno quede casi perpendicular desde cualquier ángulo, de modo que medio millón de instancias parezcan un campo en vez de confeti de menos de un píxel. Un kernel de cómputo coloca cada mata —altura del terreno FBM muestreada con el mismo mod que conserva el signo que utiliza la malla de la CPU, normal por diferencias centrales, tamaño y tono por mata— y un grafo de vértices TSL omite la matriz de instancia para escalar, rotar mediante Rodrigues hasta la normal, trasladar y aplicar viento limitado por la altura. El LOD por distancia es gratis: las matas colapsan hasta una altura cero mediante 1smoothstep(fadeNear,fadeFar,dist). El color sigue la fórmula de BotW de NedMakesGames: un degradado de la raíz a la punta sobre dos paletas mezcladas según el tono. Consulta LOD controlado por GPU.

Descarte por oclusión del horizonte del terreno. Cuatro métodos comparados sobre el mundo de producción: distancia; más frustum; más una prueba de rayos sobre el mapa de alturas que rechaza instancias situadas detrás de una cresta; más una pirámide de alturas máximas que la acelera; y un port completo a cómputo TSL. La pirámide muestrea exactamente sobre la cuadrícula de vértices de dos metros que tesela la malla —texel base = máximo de los cuatro vértices de esquina, seguido de reducciones máximas 2x2 hacia arriba—, por lo que solo devuelve alturas que el rasterizador muestra realmente.

Acelerado y correcto, no una concesión. El muestreo por puntos del ruido continuo —el método T2 por fuerza bruta— encuentra picos entre vértices de la malla que nunca se renderizan y oculta en exceso follaje visible. La pirámide basada en la cuadrícula de vértices es más rápida —reducción AABB de O(logN)— y geométricamente exacta. Por eso, las pruebas de rayos por paso muestrean la malla bilinealmente en vez de consultar el máximo por texel de la pirámide, que elevaría las alturas varios metros en crestas pronunciadas. El descarte de chunks utiliza una prueba de cinco puntos y excluye la huella de cada propio chunk para que nunca se ocluya a sí mismo. El método de producción envía los metadatos y el campo de alturas a búferes de almacenamiento para realizar el descarte en un shader de cómputo con dibujo indirecto.


Parte 28 de 29. Anterior: Parte 27 - Una isla creada con ruido y un suelo que parece suelo Siguiente: Parte 29 - Un controlador, cualquier cuerpo Guía de la serie: /es/blog/2026-02-25-open-world-browser-series-guide