Problème de génération de terrain diamant carré

11

J'ai implémenté un algorithme de diamant carré selon cet article: http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2

Le problème est que j'ai ces falaises abruptes partout sur la carte. Cela se produit sur les bords, lorsque le terrain est subdivisé récursivement:

entrez la description de l'image ici

Voici la source:

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

}

Paramètres: (x1, y1), (x2, y2) - coordonnées qui définissent une région sur une carte de hauteur (par défaut (0,0) (128,128)). plage - essentiellement max. la taille. (par défaut 32)

De l'aide serait grandement appréciée.

kafka
la source
Sans regarder attentivement votre code, il semble que vous ayez probablement les mauvais coins dans les mauvais appels dans les 4 appels récursifs à la fin. La carte semble que chaque carré est tourné / retourné avant de calculer le jeu suivant, subdivisant ainsi la carte sur d'étranges falaises. Le bord inférieur du carré supérieur droit semble correspondre au bord droit du carré supérieur gauche, etc.
DampeS8N
Je ne sais pas ce que tu veux dire. Le centre du système de coordonnées est dans le coin supérieur gauche, l'axe x pointe vers la droite et y vers le bas. Ainsi, dans la première itération (x1 = 0, y1 = 0), (x2 = 128, y2 = 128) et (x1 + hx = 64, y1 + hy = 64) est le centre du carré. Le carré est ainsi divisé en 4 sous-carrés: ((0,0) (64,64)), ((64,0) (128,64)), ((0,64) (64,128)) et ((64, 64) (128 128)). Ça me va bien ...
kafka

Réponses:

12

Dans chaque niveau de subdivision, l'étape "carré" s'appuie sur les résultats de "l'étape diamant". Mais cela tient également compte du pas de diamant produit dans la cellule adjacente, dont vous ne tenez pas compte. Je réécrirais la fonction DiamondSquare pour itérer la largeur en premier, au lieu de la profondeur en premier comme vous l'avez actuellement.

Votre premier problème est que, puisque vous recalculez deux fois les bords carrés, il ignore la contribution du point central adjacent. Par exemple, dans l'article que vous référencez,

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

mais votre code fait effectivement

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

c'est-à-dire qu'il tient compte deux fois du point central actuel , et non du point central correspondant. C'est pourquoi vous devez aller en premier, afin que les points centraux précédents soient calculés.

Voici mon code et la sortie:.

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
la source
Oui, je réfléchissais également dans le sens d'une approche d'abord large. Ces fractales me causent toujours des problèmes. Il en était de même pour le bruit Perlin et les systèmes L. Vous êtes génial.
kafka
3

Une possibilité est que vous preniez un raccourci avec votre implémentation que l'algorithme sur votre page liée ne fait pas.

Pour la scène carrée, vous calculez la hauteur des points avec

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

que l'algorithme de la page indique à utiliser si vous enveloppez votre carte. Cela donne l'impression que vous utilisez la valeur de hauteur du "carré suivant" pour calculer celle-ci. Dans le premier cas le plus simple, le point central (avec la hauteur «e») est utilisé à gauche et à droite pour calculer f.

Cependant, l'algorithme que vous référencez vous permet d'utiliser les valeurs réelles des autres carrés / diamants pour vous aider à calculer la valeur de la hauteur de ce point carré. Dans leur algorithme, le deuxième point de niveau est calculé avec la formule suivante:

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

Remarquez l'absence de duplication d'une valeur là-dedans?

Je pense que vous voudrez peut-être essayer d'utiliser les versions non enveloppantes des formules données, celles-ci résumeront mieux, je pense.

F = (A + C + E)/3 + ...
    instead of
F = (A + C + E + E)/4 + ...
fnord
la source
Merci, c'était une observation utile. Je pense avoir appris ma lésion à ne pas passer directement au codage, quand je vois les équations.
kafka
De rien. Je le fais moi-même beaucoup de temps aussi ... "Ecoute, quelque chose que je peux coder. Doit. Code. Maintenant!"
fnord