Metode Sederhana Untuk Membuat Topeng Peta Pulau


21


Saya mencari cara yang bagus dan mudah untuk membuat topeng untuk peta pulau dengan C #.


Pada dasarnya saya menggunakan dengan heightmap acak yang dihasilkan dengan suara perlin, di mana medannya TIDAK dikelilingi oleh air.

masukkan deskripsi gambar di sini

Langkah selanjutnya adalah membuat topeng, untuk memastikan sudut dan perbatasan hanya air.

masukkan deskripsi gambar di sini

Lalu saya bisa mengurangi topeng dari gambar perlin noise untuk mendapatkan pulau.

masukkan deskripsi gambar di sini

dan bermain-main dengan kontras ..

masukkan deskripsi gambar di sini

dan kurva gradien, saya bisa mendapatkan peta ketinggian pulau seperti yang saya inginkan ..

masukkan deskripsi gambar di sini

(ini hanya contoh saja)

jadi seperti yang Anda lihat, "tepian" pulau hanya terpotong, yang bukan masalah besar jika nilai warnanya tidak terlalu putih, karena saya hanya akan membagi skala abu-abu menjadi 4 lapisan (air, pasir, rumput dan batu).

Pertanyaan saya adalah, bagaimana saya bisa menghasilkan topeng yang terlihat bagus seperti pada gambar kedua?


MEMPERBARUI

Saya telah menemukan teknik ini, tampaknya menjadi titik awal yang baik bagi saya, tetapi saya tidak yakin seberapa tepat saya bisa menerapkannya untuk mendapatkan hasil yang diinginkan. http://mrl.nyu.edu/~perlin/experiments/puff/


PEMBARUAN 2

ini solusi terakhir saya.

Saya telah mengimplementasikan makeMask()fungsi di dalam loop normalisasi saya seperti ini:

        //normalisation
        for( int i = 0; i < width; i++ ) {
            for( int j = 0; j < height; j++ ) {
                perlinNoise[ i ][ j ] /= totalAmplitude;
                perlinNoise[ i ][ j ] = makeMask( width, height, i, j, perlinNoise[ i ][ j ] );
            }
        }

dan ini adalah fungsi terakhir:

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 10 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return oldValue;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return oldValue * factor;
        }
    }

    private static float getFactor( int val, int min, int max ) {
        int full = max - min;
        int part = val - min;
        float factor = (float)part / (float)full;
        return factor;
    }

    public static int getDistanceToEdge( int x, int y, int width, int height ) {
        int[] distances = new int[]{ y, x, ( width - x ), ( height - y ) };
        int min = distances[ 0 ];
        foreach( var val in distances ) {
            if( val < min ) {
                min = val;
            }
        }
        return min;
    }

ini akan memberikan output seperti pada gambar # 3.

dengan sedikit perubahan dalam kode, Anda bisa mendapatkan output yang diinginkan seperti pada gambar # 2 ->

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 20 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return 1;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return ( oldValue + oldValue ) * factor;
        }
    }

ada kemungkinan Anda dapat menautkan ke sumber lengkap Anda?
tabah

Jawaban:


12

Hasilkan kebisingan biasa dengan bias untuk nilai yang lebih tinggi ke arah tengah. Jika Anda menginginkan bentuk pulau persegi seperti yang Anda tunjukkan dalam contoh Anda, saya akan menggunakan jarak ke tepi terdekat sebagai faktor Anda.

masukkan deskripsi gambar di sini

Dengan faktor itu, Anda bisa menggunakan sesuatu seperti berikut saat menghasilkan suara topeng:

maxDVal = (minIslandSolid - maxIslandSolid)

getMaskValueAt(x, y)
    d = distanceToNearestEdge(x,y)
    if(d < maxIslandSolid)
        return isSolid(NonSolidValue) //always non-solid for outside edges
    else if(d < minIslandSolid)
        //noisy edges
        return isSolid((1 - (d/maxDVal)) * noiseAt(x,y))
    else
        return isSolid(SolidValue) //always return solid for center of island

Di mana distanceToNearestEdgemengembalikan jarak ke tepi terdekat peta dari posisi itu. Dan isSolidmemutuskan apakah nilai antara 0 dan 1 adalah padat (apa pun cut off Anda). Ini fungsi yang sangat sederhana, mungkin terlihat seperti ini:

isSolid (float value) mengembalikan nilai <solidCutOffValue

Di mana solidCutOffValuenilai apa pun yang Anda gunakan untuk memutuskan antara solid atau tidak. Itu bisa .5untuk perpecahan bahkan, atau .75untuk lebih padat atau .25untuk kurang padat.

Akhirnya, ini sedikit (1 - (d/maxDVal)) * noiseAt(x,y). Pertama, kita mendapatkan faktor antara 0 dan 1 dengan ini:

(1 - (d/maxDVal))

masukkan deskripsi gambar di sini

Di mana 0di tepi luar dan 1di tepi dalam. Ini berarti kebisingan kami lebih cenderung solid di bagian dalam dan non-solid di bagian luar. Ini adalah faktor yang kami terapkan pada kebisingan yang kami dapatkan noiseAt(x,y).

Berikut ini adalah representasi yang lebih visual dari apa nilai-nilai itu, karena nama-nama itu mungkin menyesatkan nilai-nilai aktual:

masukkan deskripsi gambar di sini


Terima kasih untuk jawaban cepat itu, saya akan mencoba menerapkan teknik ini. berharap untuk mendapatkan hasil yang diinginkan dengan ini.
Ace

Mungkin perlu beberapa penyesuaian untuk mendapatkan tepi bising seperti yang Anda inginkan. Tetapi ini harus menjadi dasar yang kuat untuk Anda. Semoga berhasil!
MichaelHouse

sejauh ini saya mendapatkan implementasi basis untuk bekerja, bisakah Anda memberi saya contoh fungsi IsSolid? saya tidak tahu bagaimana saya bisa mendapatkan nilai antara 0 dan 1 berdasarkan jarak min dan max dari tepi. lihat pembaruan saya untuk kode saya sejauh ini.
Ace

Saya memiliki beberapa logika campur aduk di sana. Saya sudah memperbaikinya agar lebih masuk akal. Dan memberikan contohisSolid
MichaelHouse

Untuk mendapatkan nilai antara 0 dan 1, Anda tinggal mencari tahu berapa nilai maksimumnya dan membagi nilai saat ini dengan itu. Kemudian saya mengurangi itu dari satu, karena saya ingin nol berada di tepi luar dan 1 berada di tepi dalam.
MichaelHouse

14

Jika Anda bersedia meluangkan sedikit daya komputasi untuk ini, maka Anda dapat menggunakan teknik serupa dengan apa yang dilakukan penulis blog ini. ( NB: Jika Anda ingin langsung menyalin kodenya, itu ada di ActionScript). Pada dasarnya, ia menghasilkan poin kuasi-acak (yaitu terlihat relatif seragam) dan kemudian menggunakannya untuk membuat poligon Voronoi .

Poligon Voronoi

Dia kemudian mengatur poligon luar untuk air dan beralih melalui sisa poligon, membuatnya air jika persentase tertentu dari poligon yang berdekatan adalah air . Anda kemudian pergi dengan topeng poligon yang secara kasar mewakili sebuah pulau.

Peta Poligon

Dari sini Anda dapat menerapkan noise ke tepi, menghasilkan sesuatu yang menyerupai ini (warnanya dari langkah lain yang tidak terkait):

Peta Polygon dengan Tepi Bising

Anda kemudian dibiarkan dengan topeng berbentuk pulau yang tampak realistis, yang akan melayani tujuan Anda. Anda dapat memilih untuk menggunakannya sebagai masker untuk kebisingan Perlin Anda, atau Anda kemudian dapat menghasilkan nilai ketinggian berdasarkan jarak ke laut dan menambahkan kebisingan (meskipun itu tampaknya tidak perlu).


Terima kasih atas balasan Anda, tetapi ini adalah solusi pertama (yang hanya cukup banyak) yang saya dapatkan dari pencarian di web. solusi ini tampaknya sangat bagus, tetapi saya ingin mencoba cara "sederhana".
Ace

@ Ace Cukup adil, mungkin sedikit berlebihan untuk apa pun yang akan Anda lakukan: P Tetap saja, perlu diingat jika Anda membutuhkannya.
Polar

Senang seseorang yang terhubung dengan ini - halaman itu selalu ada dalam daftar "posting yang sangat fantastis tentang bagaimana seseorang mengimplementasikan sesuatu."
Tim Holt

+1. Ini sangat keren. Terima kasih untuk ini, pasti akan sangat membantu saya!
Andre

1

Salah satu metode yang sangat sederhana adalah dengan membuat gradien radial atau bola terbalik dengan pusat pada lebar / 2 dan tinggi / 2. Untuk masking Anda ingin mengurangi gradien dari noise daripada mengalikannya. Ini memberi Anda pantai yang tampak lebih realistis dengan kelemahan bahwa pulau-pulau tersebut belum tentu terhubung.

Anda dapat melihat perbedaan antara mengurangi dan mengalikan derau dengan gradien di sini: http://www.vfxpedia.com/index.php?title=Tips_and_Techniques/Natural_Phenomena/Smoke

Jika Anda tidak yakin cara membuat gradien radial, Anda dapat menggunakan ini sebagai titik awal:

    public static float[] CreateInverseRadialGradient(int size, float heightScale = 1)
    {
        float radius = size / 2;

        float[] heightMap = new float[size * size];

        for (int iy = 0; iy < size; iy++)
        {
            int stride = iy * size;
            for (int ix = 0; ix < size; ix++)
            {
                float centerToX = ix - radius;
                float centerToY = iy - radius;

                float distanceToCenter = (float)Math.Sqrt(centerToX * centerToX + centerToY * centerToY);
                heightMap[iy * size + ix] = distanceToCenter / radius * heightScale;
            }
        }

        return heightMap;
    }

Jangan lupa untuk mengukur gradien Anda ke ketinggian yang sama dengan peta ketinggian Anda dan Anda masih harus memperhitungkan garis air Anda.

Masalah dengan metode ini adalah bahwa bidang tinggi Anda akan dipusatkan di sekitar tengah peta. Metode ini, bagaimanapun, akan membantu Anda memulai dengan menambahkan fitur dan membuat lanskap Anda lebih beragam karena Anda dapat menggunakan tambahan untuk menambahkan fitur ke peta ketinggian Anda.


Terima kasih untuk jawabannya. saya tidak yakin apakah saya mengerti Anda benar, tetapi apakah Anda memperhatikan bahwa topeng tidak mempengaruhi data peta ketinggian asli sama sekali, itu hanya terpengaruh pada negatif, jadi itu hanya mendefinisikan piksel yang ditampilkan (dalam%) atau tidak . tetapi juga saya sudah mencobanya dengan gradians sederhana, dan saya tidak senang dengan hasilnya.
Ace

1

Saran kedua saya ollipekka: apa yang ingin Anda lakukan adalah mengurangi fungsi bias yang sesuai dari heightmap Anda, sehingga ujung-ujungnya dijamin bawah air.

Ada banyak fungsi bias yang cocok, tetapi yang cukup sederhana adalah:

f(x, y) = 1 / (x * (1-x) * y * (1-y)) - 16

di mana x dan y adalah nilai koordinat, diskalakan untuk terletak di antara 0 dan 1. Fungsi ini mengambil nilai 0 di tengah peta (di x = y = 0,5) dan cenderung tak terhingga di tepinya. Jadi, mengurangkannya (diskalakan oleh faktor konstan yang sesuai) dari peta ketinggian Anda memastikan bahwa nilai ketinggian juga akan cenderung minus tanpa batas di dekat tepi peta. Pilih saja sembarang ketinggian yang Anda inginkan dan sebut saja permukaan laut.

Seperti yang dicatat ollipekka, pendekatan ini tidak akan menjamin bahwa pulau itu akan bersebelahan. Namun, penskalaan fungsi bias oleh faktor skala yang cukup kecil harus membuatnya sebagian besar rata di area tengah peta (sehingga tidak terlalu mempengaruhi medan Anda), dengan bias signifikan hanya muncul di dekat tepi. Dengan demikian, melakukan hal itu akan memberi Anda sebuah pulau persegi yang sebagian besar bersebelahan dengan, paling banyak, mungkin beberapa pulau kecil di dekat tepiannya.

Tentu saja, jika Anda tidak keberatan dengan kemungkinan medan yang terputus, faktor penskalaan yang agak lebih besar akan memberi Anda lebih banyak air dan bentuk pulau yang lebih alami. Menyesuaikan permukaan laut dan / atau skala peta ketinggian asli Anda juga dapat digunakan untuk memvariasikan ukuran dan bentuk pulau yang dihasilkan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.