Problema de geração de terreno com diamante quadrado

11

Eu implementei um algoritmo de diamante quadrado de acordo com este artigo: http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2

O problema é que eu tenho esses penhascos íngremes por todo o mapa. Isso acontece nas bordas, quando o terreno é subdividido recursivamente:

insira a descrição da imagem aqui

Aqui está a fonte:

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 definem uma região em um mapa de altura (padrão (0,0) (128,128)). gama - basicamente max. altura. (padrão 32)

A ajuda seria muito apreciada.

kafka
fonte
Sem olhar muito para o seu código, parece que você provavelmente tem os cantos errados nas chamadas erradas nas quatro chamadas recursivas no final. O mapa parece que cada quadrado é girado / invertido antes de calcular o próximo conjunto, subdividindo o mapa em falésias estranhas. A borda inferior do quadrado superior direito parece corresponder à borda direita do quadrado superior esquerdo e assim por diante.
DampeS8N
Não sei bem o que você quer dizer. O centro do sistema de coordenadas está no canto superior esquerdo, o eixo x aponta para a direita e y para baixo. Portanto, na primeira iteração (x1 = 0, y1 = 0), (x2 = 128, y2 = 128) e (x1 + hx = 64, y1 + hy = 64) é o centro do quadrado. O quadrado é assim dividido em 4 sub-quadrados: ((0,0) (64,64)), ((64,0) (128,64)), ((0,64) (64,128)) e ((64, 64) (128,128)). Looks bem para mim ...
kafka

Respostas:

12

Em cada nível de subdivisão, a etapa "quadrada" depende dos resultados da "etapa de diamante". Mas isso também leva em consideração o passo do diamante produzido na célula adjacente, que você não está respondendo. Eu reescreveria a função DiamondSquare para iterar a largura primeiro, em vez da profundidade primeiro, como você a possui atualmente.

Seu primeiro problema é que, como você recalcula as arestas quadradas duas vezes, ele ignora a contribuição do ponto central adjacente. Por exemplo, no artigo que você faz referência,

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

mas seu código efetivamente

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

isto é , leva em consideração o ponto central atual duas vezes, não o ponto central correspondente. É por isso que você precisa ir em primeiro lugar, para que você tenha os pontos centrais anteriores calculados.

Aqui está o meu código e a saída:.

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

Jimmy
fonte
Sim, eu também estava pensando na linha da primeira abordagem. Esses fractais estão sempre me causando problemas. O mesmo aconteceu com o ruído Perlin e os sistemas L. Você é incrível.
kafka
3

Uma possibilidade é que você esteja usando um atalho com sua implementação que o algoritmo da sua página vinculada não possui.

Para o estágio quadrado, você está calculando a altura dos pontos com

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

que o algoritmo da página indica para usar se você estiver agrupando seu mapa. Isso mostra que você está usando o valor da altura do "próximo quadrado sobre" para calcular esse. No primeiro caso mais simples, o ponto central (com altura 'e') é usado nos lados esquerdo e direito para calcular f.

No entanto, o algoritmo que você faz referência usa os valores reais dos outros quadrados / diamantes para ajudá-lo a calcular o valor da altura desse ponto quadrado. Em seu algoritmo, o segundo ponto de nível é calculado com a seguinte fórmula:

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

Percebe a falta de duplicação de um valor lá?

Eu acho que você pode querer tentar usar as versões não empacotáveis ​​das fórmulas fornecidas, essas serão melhores, eu acho.

F = (A + C + E)/3 + ...
    instead of
F = (A + C + E + E)/4 + ...
fnord
fonte
Obrigado, essa foi uma observação útil. Acho que aprendi minha lesão a não pular diretamente para a codificação, quando vejo as equações.
Kafka
Você é muito bem-vindo. Eu também faço muito tempo ... "Olha, algo que eu posso codificar. Deve. Código. Agora!"
fnord