中点変位アルゴリズム


14

MDPMDP

この問題は、問題を解明するために数時間を費やした後、主に全くの絶望から生じました。

上の図に目を向けると、中点変位アルゴリズムアルゴリズムが(ある程度)正常に機能していることがわかります。ある程度コヒーレントなノイズパターンを生成します。

ただし、画像上に黒い点線のグリッドが残っているため、その理由はわかりません。私はこれが数学の問題であることを予見できますが、私はそれを見ることができません。また、オンラインリソースでこれが問題の可能性として指摘されていませんでした。そのため、このバグを追跡するための助けをいただければ幸いです。

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;
}

フラクタルベースの地形生成に関するhttp://www.gameprogrammer.com/fractal.htmlおよびhttp://www.lighthouse3d.com/opengl/terrain/index.php?mpd2以外のヒントやリソースがある場合、コメントとしても感謝します。

編集:

MDP

これは、ファビアンの提案(ty)によると、新しいイメージですが、まだいくつかの奇妙な癖があり、すぐに見ることができるはずです(小さな「くぼみ」が至る所にあります)。

この奇妙な動作の原因は何ですか?更新されたソースコード:http : //www.pastie.org/1924223

編集:

境界チェックエラーを見つけてくれたFabianに感謝します。興味のある方のために、現在のソリューションは512x512 pngです。そして、現在のソースコード(ファビアンによって変更)MDP

編集(数年後): Pythonバージョンhttps://gist.github.com/dcousens/5573724#file-mdp-py


写真では、各ドットが同じ高さにあるように見えます。角も同じ高さですか?
deft_code

1
それが価値があるものとして、あなたの画像はとてもきれいに見えます。:)
ChrisE

scrand():私が理解していることは完全にはわかりませんが、これは間隔(-r / 2、r / 2]で符号付き文字を返すことになっていますか?とにかくディンプルは、結果のよう隣接するエリアが突然黒く撮影された後、白に戻って上昇しているように見えます。より高い精度(整数など)で、値を[0,256]または[-128,127]の範囲にクランプしますか?
-ChrisE

問題は以下で解決しました。実際の値ではなく、ランダムな値の範囲に対して境界チェックを行っていたためです。scrand()は、一時的な「ノイズ」関数は、理想的に復帰した-128、127]
deceleratedcaviar

あ、かっこいい!現在、機能していることをうれしく思います。
-ChrisE

回答:


12

アルゴリズムは値を再帰的に追加しますが、値は正でも負でもかまいません(通常は+ -1 /(2 ^ octave))

ゼロから始めて正の値のみを追加する場合、上にしか移動できないため、頂点がプルダウンされているのがわかります。

四隅でゼロではなく127から始めて、またsigned charを試してみてください(そして上と下の両方の境界をチェックします)

編集

したがって、各オクターブで半分の効果を得るには、メイン(64 >> i)でさらに2つの変更が必要です。また、出力関数(final [] []をimgdta []にマッピングする関数)入れる必要があります

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

if elseブロックではなく。

別のこと、理由はわかりませんが、チェックを完全に削除すると、境界チェックが失敗します(38行目と65行目)境界チェックを行う前に、「64 / i」で得られるノイズの多い画像が必要かどうかを確認します。

別の編集

それが何であるかを見つけただけで、境界チェックで「rv」ではなく「r」と比較しています。修正済みのコードは次のとおりです。http//pastie.org/1927076


これは間違いなくそれを実行するためのより良い方法でしたが、まだ葉巻はありません。私のイメージにはまだいくつかの「癖」が残っているようです。元の投稿を更新しました。
減速

確かではありませんが、行93は間違っているように見えます。64/ iは64 >> iである必要があります(各オクターブの効果の半分として)
リチャードファビアン

ああ!どうもありがとう。信じられない。それがその二番目の問題にとっては馬鹿げたものだとわかっていた。その場しのぎのTGAコードが大好きでした。
減速

3

飛び出す2つのこと:

  1. 固定小数点でこれを行う説得力のある理由はありますか?それ自体には何の問題もありませんし、それを使用する理由はたくさんあります(特に巨大なマップに移行することを計画している場合はメモリ要件)が、間違いなく浮動小数点バージョンのアルゴリズムから始めます動作させた後、固定小数点に変換します。それは、他に何もないとしても、もっともらしいエラーの原因を1つ除去する必要があります(特に、クランプが問題を引き起こしている可能性があり、rvをいつ追加/減算するかの条件)。
  2. 私が見るコードから知るのは難しいですが、あなたのランダム化された高さの変位がレベルでスケーリングしているようには見えません、そしてそれは(1)の問題と組み合わさっていくつかの問題を引き起こしているかもしれません-あなたはすべきではありません各レベルで同じ量だけ移動します。

ああ、1つの非アルゴリズム的なこと:mdp()関数で割り当てを行わないことを強くお勧めします。既に割り当てられている2つの異なる配列を渡し、一方から他方に向かって「インプレース」反復を実行します。他に何もなければ、これは、毎回新しい配列を割り当てる必要がなく、レイヤーを実行する際に前後にピンポンできるようにします。


いくつかの良い点は、明らかに私はアルゴリズムを正しくしようとしているだけで、実装は理想からはほど遠いです、私はこの点でメモリをクリーンアップさえしません:P。
減速

この時点で、パスのスケーリングは64 / iであり、明らかに後で変更しますが、現在経験しているディンプル効果を実際には説明していません。:S
減速

0

上記に加えて、現在、割り当てているメモリは削除していません。これを修正するには、104行目を次のように変更します。

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;
}

そして、tgaファイルに書き出した後にこれを追加します。

for (unsigned i = 0; i < n; ++i)
  delete[] final[i];
delete[] final;

私はメモリをクリーンアップする方法を非常によく知っていますが、これはアルゴリズムのプロトタイプのみであり、ベクトルを使用するように書き直されるので、それが正しいことを確認したかっただけです。
減速
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.