Terreno suave de Voxel

13

Como proyecto personal, estoy tratando de hacer un generador de terreno que cree un terreno parecido al terreno liso de Castle Story.

Si no lo has visto antes, aquí: ingrese la descripción de la imagen aquí

Como puede ver, es una combinación de bloques y bloques "suaves".

Lo que he intentado hacer para emular este aspecto es dar a cada bloque de superficie un mini mapa de altura. Esto generalmente funciona, pero hay algunos problemas, produciendo un terreno como este:

ingrese la descripción de la imagen aquí

El problema es que cada bloque es 1x1x1, pero a veces la altura en una ubicación particular es negativa o> 1. En ese caso, la recorto y establezco la altura en 0 o 1.

Para ilustrar mejor lo que quiero decir, aquí hay un diagrama: ingrese la descripción de la imagen aquí

Para generar la altura, básicamente hago:

genColumn(int x, int z)
{
    int highestBlockY = (int)noise2d(x, z);

    bool is_surface = true;

    for(int y = max_height - 1; y >= 0; y--)
    {
        Block b;

        if(is_surface)
        {
            b = Block.Grass;
            b.HasHeightMap = true;

            // generate heightmap
            for(int ix = 0; ix < 5; ix++)
            {
                for(int iz = 0; iz < 5; iz++)
                {
                    float heightHere = noise2d(x + ix / 4, z + iz / 4) - y;

                    // clip heights
                    if(heightHere > 1)
                        heightHere = 1;

                    if(heightHere < 0)
                        heightHere = 0;

                    b.HeightMap[ix][iz] = heightHere;
                }
            }

            is_surface = false;
        }
        else
        {
            b = Block.Dirt;
        }

        setBlock(x, y, z, b);
    }
}

¿Quizás me estoy acercando a esto incorrectamente usando el valor de ruido perlin "verdadero"?

Cualquier ayuda sería muy apreciada!

intitulado
fuente

Respuestas:

11

Castle Story se ve así debido a limitaciones técnicas: si hubiera un mapa de altura por cada vóxel en todo el volumen, en lugar de solo un mapa de altura por cada vóxel de superficie , el costo de almacenamiento sería mucho mayor, del orden de O (n ^ 3 ) que puede ser prohibitivo, en oposición a un O más favorable (n ^ 2), donde n es la longitud lateral de un espacio vóxel cúbico que representa su mundo. Tenga en cuenta que la información del mapa de altura para los vóxeles subterráneos está implícita en la estructura de la cuadrícula, por lo que la información del mapa de altura solo debe almacenarse explícitamente para los vóxeles que se encuentran en la superficie. Entonces, los chicos de Castle Story han ajustado vértices a intersticios de cuadrícula para ahorrar en costos de almacenamiento y complejidad de construcción de malla ... sigue leyendo.

En primer lugar, veamos sus opciones:

ingrese la descripción de la imagen aquí

(1) te dificultará las cosas, ya que en una sola columna de vóxel solo quieres información de minimapa de altura para el vóxel superior : consulta el primer párrafo para ver los motivos. Mirando (1), la columna de la derecha tiene información de mapa de altura tanto para el superior como para el segundo desde arriba (y esto podría aplicarse al tercero desde arriba, etc., si la pendiente fuera lo suficientemente extrema). Esto no está bien.

(2) Es probablemente una mejor opción entonces; es decir, garantizar que los vértices de las esquinas se ajusten a los intersticios de la cuadrícula vóxel. Pero, ¿cómo abordamos el problema de la pendiente extrema? Bueno, necesitamos elegir un gradiente donde simplemente encajamos en una columna vertical y así asegurarnos de que no tengamos gradientes que puedan cortar en n voxels superiores. Un gradiente de 45 grados es el límite natural por las razones que explico a continuación. Entonces, en lugar de (3), tendríamos (4):

ingrese la descripción de la imagen aquí

(4) Ajustar el vértice de la esquina del vóxel al intersticio de cuadrícula más cercano es la solución. El efecto visual (alias diagonal) se puede ver en la captura de pantalla de Castle Story. La pendiente de corte para los vóxeles es un gradiente de 1: 1, o 45 grados (como se ve ortogonalmente). Trabajando hacia atrás desde la solución, veamos las razones:

  • La única forma en que podríamos tener una pendiente extrema ininterrumpida es si un vóxel se alargara verticalmente ...
  • .... Pero ninguna malla de voxel puede exceder su área límite, independientemente de su forma suavizada real; un vóxel necesita sentarse en un espacio definido en una cuadrícula 3D que explica, si no es por su forma exacta, al menos por su cuadro delimitador alineado con el eje.

Otra razón para abordarlo de esta manera es que, como ya descubrió, no usar el ajuste (discretización) conduce a una gama de escenarios de suavizado de superficie geométricamente complejos, que es mejor evitar por completo ... los juegos de este tipo generalmente no requieren el grado de precisión que proporcionaría un algoritmo CSG adecuado, que es la razón principal por la que estamos usando voxels en primer lugar: los Voxels hacen que sea mucho más fácil trabajar incrementalmente con volúmenes que los algoritmos CSG de intersección de polígonos de punto flotante (continuo) .

Ingeniero
fuente
Intenté implementar esto y me encontré con cierta confusión: ¿qué quieres decir con "intersticios"? ¿Te refieres a las coordenadas de la cuadrícula integral?
título
@ThomasBradworth. "Un espacio que interviene entre las cosas". Para 2D, si tiene una cuadrícula de células ansn, tendrá (n + 1) x (n + 1) intersticios. Esto se extiende directamente a 3D. Son los "vértices" que unen cada celda, que se comparten principalmente entre múltiples celdas / vóxeles. Si lo reduce a 1D (una línea), si las celdas y los intersticios se representan como una matriz, la celda 0 está limitada por el intersticio 0 y el intersticio 1, mientras que la celda 1 está limitada por el intersticio 1 y el intersticio 2 ... etc.
Ingeniero
¡Gracias por la respuesta! Intenté hacer exactamente eso, pero obtuve algunos resultados desfavorables. Al principio, intenté simplemente redondear los valores de altura en los bordes (cuando x es 0 o 4, o z es 0 o 4), pero eso me consiguió: i.imgur.com/eQW7Y.png ( pastebin.com/Lr8vyyHB ) Luego, traté de suavizar los puntos en el medio, así que agregué una "función de reparación de alturas": pastebin.com/AazQ07Xm Eso, sin embargo, también me dio un resultado más angular, pero también malo: i.imgur.com/q1JVP .png ¿ Quizás algo está mal con mi función de ruido?
sin título
@ThomasBradsworth Lamentablemente, siento que solo has leído a medias / entendido la respuesta que tardé más de una hora en escribir y editar. Olvídate del ruido; Además, si no comprende la función de ruido, quítela de la ecuación por ahora. Su problema radica en cómo se construyen los datos individuales de la malla de vóxel, y el ruido no tiene nada que ver con eso, porque puede crear un mapa de altura en ausencia de ruido. Trabaje con el caso de prueba más simple, es decir, codifique la matriz de altura. Luego vea sus resultados y modifique su algoritmo de generación de mallas. Repita hasta que haga lo que se espera. Luego vuelva a poner el ruido y revise.
Ingeniero
¿Estás diciendo que debería almacenar los valores de altura para las esquinas y no almacenar los valores de altura de los puntos "intermedios"?
sin título