Algorithme de déplacement médian

14

MDPMDP

Cette question est venue principalement du désespoir , après avoir passé plusieurs heures à essayer de comprendre le problème.

Si vous jetez vos yeux sur l'image ci-dessus, vous devriez voir que mon algorithme d'algorithme de déplacement de point médian fonctionne (quelque peu) avec succès; en produisant un motif de bruit quelque peu cohérent.

Cependant, il laisse une grille en pointillés noirs sur l'image, et je ne sais pas pourquoi. Je peux prévoir que cela pose problème en mathématiques, mais je ne le vois tout simplement pas; cela n'a pas non plus été signalé dans les ressources en ligne comme un problème possible; donc toute aide sera appréciée pour traquer ce bug.

unsigned char** mdp(unsigned char** base, unsigned base_n, unsigned char r) {
    size_t n = (2 * base_n) - 1;

    unsigned char** map = new unsigned char*[n];
    for (unsigned i = 0; i < n; ++i) map[i] = new unsigned char[n];

    // Resize
    // 1 0 1
    // 0 0 0
    // 1 0 1
    for (size_t i = 0; i < n; i += 2) {
        for (size_t j = !(i % 2 == 0); j < n; j += 2) {
            map[i][j] = base[i / 2][j / 2];
        }
    }

    // Diamond algorithm
    // 0 0 0
    // 0 X 0
    // 0 0 0
    for (size_t i = 1; i < n; i += 2) {
        for (size_t j = 1; j < n; j += 2) {
            unsigned char& map_ij = map[i][j];

            unsigned char a = map[i - 1][j - 1];
            unsigned char b = map[i - 1][j + 1];
            unsigned char c = map[i + 1][j - 1];
            unsigned char d = map[i + 1][j + 1];
            map_ij = (a + b + c + d) / 4;

            unsigned char rv = std::rand() % r;
            if (map_ij + r < 255) map_ij += rv; // EDIT: <-- thanks! the bug! `map_ij + rv`, not `r`
            else map_ij = 255;
        }
    }

    // Square algorithm
    // 0 1 0
    // 1 0 1
    // 0 1 0
    for (size_t i = 0; i < n; ++i) {
        for (size_t j = (i % 2 == 0); j < n; j += 2) {
            unsigned char& map_ij = map[i][j];

            // get surrounding values
            unsigned char a = 0, b = a, c = a, d = a;
            if (i != 0) a = map[i - 1][j];
            if (j != 0) b = map[i][j - 1];
            if (j + 1 != n) c = map[i][j + 1];
            if (i + 1 != n) d = map[i + 1][j];

            // average calculation
            if (i == 0) map_ij = (b + c + d) / 3;
            else if (j == 0) map_ij = (a + c + d) / 3;
            else if (j + 1 == n) map_ij = (a + b + d) / 3;
            else if (i + 1 == n) map_ij = (a + b + c) / 3;
            else map_ij = (a + b + c + d) / 4;

            unsigned char rv = std::rand() % r;
            if (map_ij + r < 255) map_ij += rv;
            else map_ij = 255;
        }

    }

    return map;
}

Si vous avez des conseils ou des ressources autres que http://www.gameprogrammer.com/fractal.html et http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2 pour la génération de terrain basé sur les fractales, je le ferais les apprécier aussi comme commentaires.

Éditer:

MDP

C'est la nouvelle image, selon la suggestion de Fabians (ty), mais elle a encore des bizarreries étranges, que vous devriez pouvoir voir tout de suite (petites «fossettes» partout).

Qu'est-ce qui pourrait être à l'origine de ce comportement étrange? Code source mis à jour: http://www.pastie.org/1924223

Éditer:

Un grand merci à Fabian pour avoir trouvé l'erreur de vérification des limites, pour les personnes intéressées, voici la solution actuelle en 512x512 png. Et le code source actuel (modifié par Fabian) . MDP

Edit (des années plus tard): version Python https://gist.github.com/dcousens/5573724#file-mdp-py

caviar décéléré
la source
Sur les photos, il semble que chacun des points soit à la même hauteur. Les coins sont-ils également à la même hauteur?
deft_code
1
Pour ce que ça vaut, vos images sont très jolies. :)
ChrisE
scrand (): Je ne suis pas tout à fait sûr de comprendre - est-ce censé retourner un caractère signé sur l'intervalle (-r / 2, r / 2]? Les fossettes, de toute façon, me semblent un peu comme le résultat de débordement. Les zones adjacentes semblent soudainement tirer du noir, puis remonter au blanc. précision plus élevée (par exemple, entier), puis en fixant les valeurs à la plage [0,256] ou [-128,127]?
ChrisE
Le problème a été résolu ci-dessous, c'était parce que je vérifiais les limites par rapport à la plage de la valeur aléatoire, pas à sa valeur réelle. Le scrand () est une fonction de 'bruit' temporaire Retournant [-128, 127]
deceleratedcaviar
Ah cool! Heureux d'entendre que ça fonctionne maintenant.
ChrisE

Réponses:

12

L'algorithme ajoute récursivement une valeur, mais la valeur peut être positive ou négative (normalement + -1 / (2 ^ octave))

Si vous commencez à zéro et ajoutez uniquement des valeurs positives, vous ne pouvez que monter, et c'est pourquoi vous voyez les sommets abaissés.

essayez de commencer à 127 plutôt que zéro pour les quatre coins, et essayez également le caractère signé (puis vérifiez vos limites en haut et en bas)

ÉDITER

donc, deux autres choses doivent changer dans le principal (64 >> i) pour obtenir le demi-effet à chaque octave, et aussi votre fonction de sortie (celle qui mappe la finale [] [] tp l'imgdta [], vous venez de besoin de mettre

imgdta [(i * n) + j] = 128 + final [i] [j];

plutôt que le bloc if else.

une autre chose, je ne sais pas pourquoi, mais votre vérification des limites échoue (c'est les lignes 38 et 65) si vous supprimez complètement la vérification, vous remarquez également de nouveaux blobs sombres, donc je pense que vous devrez peut-être passer à un type plus grand avant de faire les limites, vérifiez si vous voulez l'image la plus bruyante que vous obtenez avec "64 / i".

UNE AUTRE MODIFICATION

vient de découvrir ce que c'était, vous comparez avec «r», pas «rv», dans la vérification des limites. Voici le code fixe: http://pastie.org/1927076

Richard Fabian
la source
C'était définitivement la meilleure façon de s'y prendre, mais pas de cigare pour l'instant, il semble que mon image ait encore des "caprices", j'ai mis à jour le message d'origine.
deceleratedcaviar
pas sûr mais la ligne 93 semble fausse, 64 / i devra peut-être être 64 >> i (car vous divisez la moitié de l'effet chaque octave)
Richard Fabian
Ahhh !! Merci beaucoup, je ne peux pas le croire, je savais que ce serait quelque chose de stupide pour ce deuxième problème. J'ai adoré votre code TGA de fortune, désolé, j'aurais dû vous éviter le problème et mettre en place l'en-tête.
deceleratedcaviar
3

Deux choses qui sautent aux yeux:

  1. Avez-vous une raison impérieuse de le faire en virgule fixe? Il n'y a rien de mal en soi, et il y a beaucoup de raisons de l'utiliser (notamment des besoins en mémoire si vous prévoyez de monter vers une carte ÉNORME), mais je commencerais certainement par une version à virgule flottante de l'algorithme et le convertir en virgule fixe après l'avoir fait fonctionner; cela devrait, à tout le moins, éliminer une source plausible d'erreurs (en particulier, je soupçonne que votre serrage peut causer des problèmes et les conditions pour ajouter / soustraire rv).
  2. Bien qu'il soit difficile de dire à partir du code que je vois, il ne semble pas que votre déplacement de hauteur aléatoire soit à l'échelle avec le niveau, et cela combiné avec le problème dans (1) peut causer certains des problèmes - vous ne devriez pas être déplacement du même montant à chaque niveau.

Oh, et une chose non algorithmique: je recommande fortement de ne pas faire d'allocations dans votre fonction mdp (); passer dans deux tableaux différents déjà alloués et faire l'itération «sur place», en passant de l'un à l'autre. Si rien d'autre, cela vous permettra de faire du ping-pong d'avant en arrière pendant que vous faites les couches plutôt que d'avoir à allouer un nouveau tableau à chaque fois.

Steven Stadnicki
la source
Quelques bons points, évidemment j'essaie juste d'obtenir l'algorithme correct, l'implémentation est loin d'être idéale, je ne nettoie même pas la mémoire à ce stade: P.
deceleratedcaviar
À ce stade, la mise à l'échelle des passes est de 64 / i, je vais évidemment changer cela plus tard, mais cela n'explique pas vraiment l'effet des fossettes actuellement expérimenté. : S
deceleratedcaviar
0

Suite à ce qui précède, vous ne supprimez pas actuellement la mémoire que vous allouez. Pour y remédier, changez la ligne 104 de:

for (unsigned i = 1; i < 6; ++i) final = mdp(final, n, 64 / i);

à

for (unsigned i = 1; i < 6; ++i) {
  signed char** new_final = mdp(final, n, 64 / i);
  for (unsigned i = 0; i < n; ++i)
    delete[] final[i];
  delete[] final;
  final = new_final;
}

et ajoutez ceci après avoir écrit dans le fichier tga:

for (unsigned i = 0; i < n; ++i)
  delete[] final[i];
delete[] final;
dma
la source
Je suis très conscient de la façon de nettoyer la mémoire, mais comme il s'agissait uniquement d'un prototype d'algorithme et qu'il serait réécrit pour utiliser des vecteurs, je voulais juste m'assurer qu'il était correct.
deceleratedcaviar