Problema de generación de terreno de diamante cuadrado

11

He implementado un algoritmo de diamante cuadrado de acuerdo con este artículo: http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2

El problema es que consigo estos acantilados en todo el mapa. Ocurre en los bordes, cuando el terreno se subdivide recursivamente:

ingrese la descripción de la imagen aquí

Aquí está la fuente:

void DiamondSquare(unsigned x1,unsigned y1,unsigned x2,unsigned y2,float range)
    {      
    int c1 = (int)x2 - (int)x1;
    int c2 = (int)y2 - (int)y1;
    unsigned hx = (x2 - x1)/2;
    unsigned hy = (y2 - y1)/2;
    if((c1 <= 1) || (c2 <= 1))
            return;

// Diamond stage
float a = m_heightmap[x1][y1];
float b = m_heightmap[x2][y1];
float c = m_heightmap[x1][y2];
float d = m_heightmap[x2][y2];
float e = (a+b+c+d) / 4 + GetRnd() * range;

m_heightmap[x1 + hx][y1 + hy] = e;

// Square stage
float f = (a + c + e + e) / 4 + GetRnd() * range;
m_heightmap[x1][y1+hy] = f;
float g = (a + b + e + e) / 4 + GetRnd() * range;
m_heightmap[x1+hx][y1] = g;
float h = (b + d + e + e) / 4 + GetRnd() * range;
m_heightmap[x2][y1+hy] = h;
float i = (c + d + e + e) / 4 + GetRnd() * range;
m_heightmap[x1+hx][y2] = i;

DiamondSquare(x1, y1, x1+hx, y1+hy, range / 2.0);   // Upper left
DiamondSquare(x1+hx, y1, x2, y1+hy, range / 2.0);   // Upper right
DiamondSquare(x1, y1+hy, x1+hx, y2, range / 2.0);   // Lower left
DiamondSquare(x1+hx, y1+hy, x2, y2, range / 2.0);       // Lower right

}

Parámetros: (x1, y1), (x2, y2): coordenadas que definen una región en un mapa de altura (valor predeterminado (0,0) (128,128)). rango - básicamente máx. altura. (predeterminado 32)

La ayuda sería muy apreciada.

kafka
fuente
Sin mirar detenidamente su código, parece que probablemente tenga las esquinas incorrectas en las llamadas incorrectas en las 4 llamadas recursivas al final. El mapa parece que cada cuadrado gira / voltea antes de calcular el siguiente conjunto, subdividiendo el mapa en acantilados extraños. Parece que el borde inferior del cuadrado superior derecho coincide con el borde derecho del cuadrado superior izquierdo, y así sucesivamente.
DampeS8N
No estoy seguro de lo que quieres decir. El centro del sistema de coordenadas está en la esquina superior izquierda, el eje x apunta a la derecha e y hacia abajo. Entonces, en la primera iteración (x1 = 0, y1 = 0), (x2 = 128, y2 = 128) y (x1 + hx = 64, y1 + hy = 64) es el centro del cuadrado. El cuadrado se divide así en 4 subcuadrados: ((0,0) (64,64)), ((64,0) (128,64)), ((0,64) (64,128)) y ((64, 64) (128.128)). A mí me parece bien ...
kafka

Respuestas:

12

En cada nivel de subdivisión, el paso "cuadrado" se basa en los resultados del "paso de diamante". Pero también tiene en cuenta el paso de diamante producido en la celda adyacente, que no tiene en cuenta. Reescribiría la función DiamondSquare para iterar Breadth-first, en lugar de profundidad-first como la tiene actualmente.

Su primer problema es que, dado que vuelve a calcular los bordes cuadrados dos veces, ignora la contribución del punto central adyacente. Por ejemplo, en el artículo al que hace referencia,

P = (J + G + K + E)/4 + RAND(d)

pero su código efectivamente lo hace

P = (J + G + J + E)/4 + RAND(d)

es decir, tiene en cuenta el punto central actual dos veces, no el punto central adyacente. Es por eso que debe ir primero en amplitud, de modo que tenga calculados los puntos centrales anteriores.

Aquí está mi código y la salida:.

void DiamondSquare(unsigned x1, unsigned y1, unsigned x2, unsigned y2, float range, unsigned level) {
    if (level < 1) return;

    // diamonds
    for (unsigned i = x1 + level; i < x2; i += level)
        for (unsigned j = y1 + level; j < y2; j += level) {
            float a = m_heightmap[i - level][j - level];
            float b = m_heightmap[i][j - level];
            float c = m_heightmap[i - level][j];
            float d = m_heightmap[i][j];
            float e = m_heightmap[i - level / 2][j - level / 2] = (a + b + c + d) / 4 + GetRnd() * range;
        }

    // squares
    for (unsigned i = x1 + 2 * level; i < x2; i += level)
        for (unsigned j = y1 + 2 * level; j < y2; j += level) {
            float a = m_heightmap[i - level][j - level];
            float b = m_heightmap[i][j - level];
            float c = m_heightmap[i - level][j];
            float d = m_heightmap[i][j];
            float e = m_heightmap[i - level / 2][j - level / 2];

            float f = m_heightmap[i - level][j - level / 2] = (a + c + e + m_heightmap[i - 3 * level / 2][j - level / 2]) / 4 + GetRnd() * range;
            float g = m_heightmap[i - level / 2][j - level] = (a + b + e + m_heightmap[i - level / 2][j - 3 * level / 2]) / 4 + GetRnd() * range;
        }

    DiamondSquare(x1, y1, x2, y2, range / 2, level / 2);
}

http://i.imgur.com/laBhN.png

Palanqueta
fuente
Sí, también estaba pensando en el enfoque de amplitud primero. Estos fractales siempre me están causando problemas. Fue lo mismo con el ruido Perlin y los sistemas L. Eres increíble.
kafka
3

Una posibilidad es que esté tomando un acceso directo con su implementación que el algoritmo en su página vinculada no.

Para la etapa cuadrada, está calculando la altura de los puntos con

float f = (a + c + e + e) / 4 + GetRnd() * range;
m_heightmap[x1][y1+hy] = f;

que indica el algoritmo de la página para usar si está ajustando su mapa. Esto da la apariencia de que está utilizando el valor de altura del "siguiente cuadrado sobre" para calcular este. En el primer caso más simple, el punto central (con altura 'e') se usa en los lados izquierdo y derecho para calcular f.

Sin embargo, el algoritmo al que hace referencia le hace usar los valores reales de los otros cuadrados / diamantes para ayudarlo a calcular el valor de la altura de este punto cuadrado. En su algoritmo, el punto de segundo nivel se calcula con la siguiente fórmula:

N = (K + A + J + F)/4 + RAND(d)

¿Nota la falta de duplicación de un valor allí?

Creo que es posible que desee intentar usar las versiones no envolventes de las fórmulas dadas, creo que se repetirán mejor.

F = (A + C + E)/3 + ...
    instead of
F = (A + C + E + E)/4 + ...
fnord
fuente
Gracias, fue una observación útil. Creo que aprendí mi lesión a no saltar directamente a la codificación, cuando veo las ecuaciones.
kafka
Eres muy bienvenido. Yo también lo hago mucho tiempo ... "Mira, algo que puedo codificar. Debo codificar. ¡Ahora!"
fnord