Algoritma Pemindahan Titik Tengah

14

MDPMDP

Pertanyaan ini muncul terutama karena putus asa , setelah menghabiskan beberapa jam mencoba mencari tahu masalahnya.

Jika Anda mengarahkan mata ke gambar di atas, Anda akan melihat bahwa algoritma algoritma perpindahan titik tengah saya berhasil (agak) berhasil; dalam menghasilkan pola kebisingan yang agak koheren.

Namun, itu meninggalkan grid bertitik hitam pada gambar, dan saya tidak tahu mengapa. Saya bisa meramalkan ini menjadi masalah dalam matematika, tetapi saya tidak bisa melihatnya; ini juga tidak ditunjukkan dalam sumber daya online sebagai masalah yang mungkin; jadi bantuan apa pun akan dihargai untuk memburu bug ini.

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

Jika Anda memiliki kiat atau sumber daya selain http://www.gameprogrammer.com/fractal.html dan http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2 untuk pembuatan medan berbasis fraktal, saya akan hargai mereka sebagai komentar juga.

Edit:

MDP

Ini adalah gambar baru, sesuai saran Fabian (ty), namun masih memiliki beberapa keanehan aneh, yang seharusnya dapat Anda lihat langsung ('lesung pipit' kecil di mana-mana).

Apa yang bisa menyebabkan perilaku aneh ini? Kode sumber yang diperbarui: http://www.pastie.org/1924223

Edit:

Banyak terima kasih kepada Fabian dalam menemukan kesalahan pemeriksaan batas, bagi mereka yang tertarik, berikut adalah solusi saat ini sebagai 512x512 png. Dan kode sumber saat ini (dimodifikasi oleh Fabian) . MDP

Sunting (tahun kemudian): Versi Python https://gist.github.com/dcousens/5573724#file-mdp-py

kaviar melambat
sumber
Dalam gambar, sepertinya masing-masing titik berada pada ketinggian yang sama. Apakah sudut-sudutnya juga sama tingginya?
deft_code
1
Untuk apa nilainya, gambar Anda terlihat sangat cantik. :)
ChrisE
scrand (): Saya tidak sepenuhnya yakin bahwa saya mengerti - apakah ini seharusnya mengembalikan karakter yang ditandatangani pada interval (-r / 2, r / 2]? Lesung pipit, bagi saya lagian, sepertinya hasilnya overflow. Daerah yang berdekatan tampaknya tiba-tiba menembak hitam, dan kemudian naik kembali ke putih. Secara keseluruhan, itu juga terlihat seperti ada garis yang tajam, sekali lagi menunjukkan kepada saya bahwa Anda meluap, mungkin. Maukah Anda melakukan matematika dalam presisi yang lebih tinggi (katakanlah, bilangan bulat), dan kemudian menjepit nilai-nilai ke kisaran [0,256] atau [-128.127]?
ChrisE
Masalahnya dipecahkan di bawah ini, itu karena saya terikat memeriksa rentang nilai acak, bukan nilai sebenarnya. Scrand () adalah fungsi 'noise' sementara idealnya kembali [-128, 127]
decaviatedcaviar
Ah, keren! Senang mendengarnya bekerja sekarang.
ChrisE

Jawaban:

12

Algoritma secara rekursif menambahkan nilai, tetapi nilainya bisa positif atau negatif (biasanya + -1 / (2 ^ oktaf))

Jika Anda mulai dari nol dan hanya menambahkan nilai positif, maka Anda hanya bisa naik, dan itulah sebabnya Anda melihat simpul ditarik ke bawah.

coba mulai di 127 daripada nol untuk empat sudut, dan coba juga tanda char (lalu periksa batas Anda baik atas dan bawah)

EDIT

jadi, dua hal lagi perlu diubah di main (64 >> i) untuk mendapatkan setengah efek pada setiap oktaf, dan juga fungsi output Anda (yang memetakan [[] [] tp imgdta [] terakhir, Anda hanya perlu perlu dimasukkan

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

daripada blok if else.

Hal lain, saya tidak yakin mengapa, tetapi batas Anda gagal (itu garis 38 dan 65) jika Anda menghapus cek sepenuhnya, Anda melihat beberapa gumpalan gelap baru juga, jadi saya rasa Anda mungkin perlu mempromosikan ke tipe yang lebih besar sebelum melakukan batas periksa apakah Anda ingin gambar yang lebih berisik yang Anda dapatkan dengan "64 / i".

EDIT LAIN

baru tahu apa itu, Anda membandingkan terhadap 'r', bukan 'rv', di batas cek. Berikut kode tetapnya: http://pastie.org/1927076

Richard Fabian
sumber
Ini jelas cara yang lebih baik untuk melakukannya, tetapi belum ada cerutu, tampaknya gambar saya masih memiliki beberapa 'kebiasaan', saya memperbarui posting aslinya.
decaviatedcaviar
tidak yakin tetapi baris 93 terlihat salah, 64 / i mungkin harus 64 >> i (karena Anda setengah efek setiap oktaf)
Richard Fabian
Ahhh !! Terima kasih banyak, saya tidak bisa percaya itu, saya tahu itu akan menjadi sesuatu yang bodoh untuk masalah kedua itu. Menyukai kode TGA darurat Anda, maaf, saya seharusnya sudah menyelamatkan Anda dari masalah dan memasang tajuk.
decaviatedcaviar
3

Dua hal yang muncul:

  1. Apakah Anda memiliki alasan kuat untuk melakukan ini di titik tetap? Tidak ada yang salah dengan itu, dan ada banyak alasan untuk menggunakannya (terutama persyaratan memori jika Anda berencana naik ke peta BESAR), tapi saya pasti akan mulai dengan versi floating-point dari algoritma dan mengonversinya ke titik tetap setelah Anda membuatnya berfungsi; yang seharusnya, jika tidak ada yang lain, menghilangkan satu sumber kesalahan yang masuk akal (khususnya, saya menduga penjepitan Anda mungkin menyebabkan masalah, dan kondisi kapan harus menambah / mengurangi rv).
  2. Walaupun sulit untuk mengetahui dari kode yang saya lihat, sepertinya perpindahan ketinggian acak Anda tidak meningkat dengan level, dan dikombinasikan dengan masalah pada (1) dapat menyebabkan beberapa masalah - Anda tidak seharusnya berpindah dengan jumlah yang sama di setiap level.

Oh, dan satu hal non-algoritmik: Saya sangat menyarankan untuk tidak melakukan alokasi dalam fungsi mdp () Anda; lulus dalam dua array yang sudah dialokasikan berbeda dan melakukan iterasi 'di tempat', pergi dari satu ke yang lain. Jika tidak ada yang lain, ini akan memungkinkan Anda melakukan ping-pong bolak-balik saat Anda melakukan layer daripada harus mengalokasikan array baru setiap kali keluar.

Steven Stadnicki
sumber
Beberapa poin yang baik, jelas saya hanya mencoba untuk mendapatkan algoritma yang benar, implementasinya jauh dari ideal, saya bahkan tidak membersihkan memori pada saat ini: P.
decaviatedcaviar
Pada titik ini, penskalaan overpass adalah 64 / i, jelas saya akan mengubahnya nanti, tetapi itu tidak benar-benar menjelaskan efek lesung pipit yang saat ini dialami. : S
deceleratedcaviar
0

Lebih jauh ke atas, Anda saat ini tidak menghapus memori yang Anda alokasikan. Untuk memperbaiki ini, ubah baris 104 dari:

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

untuk

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

dan tambahkan ini setelah menulis ke file tga:

for (unsigned i = 0; i < n; ++i)
  delete[] final[i];
delete[] final;
dma
sumber
Saya sangat sadar tentang cara membersihkan memori, tetapi mengingat ini hanya prototipe algoritma, dan akan ditulis ulang menggunakan vektor, saya hanya ingin memastikan itu benar.
decaviatedcaviar