Sebagian karena popularitas
Minecraft , baru-baru ini ada minat yang tumbuh dalam gagasan permainan yang terjadi di dunia yang dibangun dadu yang terbuat dari bantuan 3D dan diisi dengan elemen seperti gua, tebing, dan sebagainya. Dunia seperti itu adalah aplikasi ideal untuk kebisingan yang dihasilkan dengan gaya perpustakaan
ANL saya. Artikel ini muncul dari diskusi tentang
upaya saya sebelumnya
untuk menerapkan teknik ini. Sejak itu, perubahan kecil telah muncul dalam struktur perpustakaan.
Dalam posting sebelumnya, saya berbicara tentang menggunakan fitur noise 3D untuk mengimplementasikan medan gaya Minecraft. Setelah itu, perpustakaan berkembang sedikit, jadi saya memutuskan untuk kembali ke topik ini. Karena saya harus menjawab banyak pertanyaan tentang sistem ini, saya akan mencoba untuk berbicara lebih banyak tentang konsep yang terlibat. Untuk membuat konsep dasar lebih jelas, saya akan mulai dengan gagasan menghasilkan medan 2D yang digunakan dalam game seperti Terraria dan King Arthur's Gold, dan kemudian memperluas sistem ke contoh 3D seperti Minecraft. Ini akan memungkinkan saya untuk menunjukkan konsep dengan lebih efektif menggunakan gambar sebagai contoh.
Sistem ini dikembangkan dengan mempertimbangkan tujuan abstrak berikut: kita harus dapat meneruskan koordinat titik atau sel tertentu ke sistem, dan menentukan jenis blok apa yang harus berada di lokasi ini. Kami ingin sistem menjadi "kotak hitam": kami memberikan satu poin, mengembalikan jenis blok. Tentu saja, ini hanya berlaku untuk generasi awal dunia. Blok dalam permainan tersebut dapat diubah oleh tindakan pemain, dan akan merepotkan untuk mencoba menggambarkan perubahan tersebut menggunakan sistem yang sama. Perubahan seperti itu harus dilacak dengan cara lain. Sistem ini menghasilkan dunia asli, murni dan tidak tersentuh oleh tangan pemain dan karakter lainnya.
Mungkin teknik ini tidak cocok untuk sistem pemodelan seperti rumput atau entitas biologis lainnya, mengingat bahwa sistem seperti itu sendiri adalah entitas kompleks yang tidak begitu mudah untuk dimodelkan secara implisit. Hal yang sama berlaku untuk sistem seperti salju yang turun, pembentukan es, dll ... Teknik yang dijelaskan dalam artikel ini adalah
metode implisit , yaitu. yang dapat diperkirakan pada suatu titik, dan yang nilainya pada titik tertentu tidak tergantung pada nilai-nilai sekitarnya. Jenis sistem biologis dan lainnya untuk melakukan simulasi yang akurat biasanya perlu mempertimbangkan nilai-nilai lingkungan. Misalnya: berapa banyak sinar matahari yang jatuh pada balok? Apakah ada air di dekat sini? Pertanyaan-pertanyaan ini dan lainnya perlu dijawab untuk mensimulasikan pertumbuhan dan penyebaran sistem biologis, serta, pada tingkat yang lebih rendah, jenis lain dari sistem terkait iklim. Juga, teknik ini tidak cocok untuk memodelkan air. Dalam sistem ini tidak ada konsep aliran, pengetahuan mekanika fluida atau gravitasi. Air adalah topik kompleks yang membutuhkan banyak perhitungan rumit.
Jadi, kita hanya memodelkan bumi dan batu. Kami membutuhkan fungsi yang memberi tahu Anda apa lokasi yang seharusnya: bumi, pasir, udara, emas, besi, batu bara, dll ... Tapi kami akan mulai dengan yang paling sederhana. Kita membutuhkan fungsi yang akan mengetahui apakah blok itu padat atau berlubang (diisi dengan udara). Fungsi ini harus mensimulasikan bumi di sekitar kita. Yaitu, langit di atas, bumi di bawah. Jadi, mari kita lakukan tugas alkitabiah, dan pisahkan langit dari bumi. Untuk melakukan ini, kita mempelajari fungsi
Gradient . Fungsi Gradient diberikan segmen garis dalam ruang dimensi-N (yaitu, dalam ruang koordinat apa pun, baik 2D, 3D, atau lebih tinggi), dan menghitung bidang gradien di sepanjang segmen ini. Koordinat masuk diproyeksikan ke segmen ini dan nilai gradiennya dihitung tergantung di mana letaknya relatif terhadap titik akhir segmen. Poin yang diproyeksikan adalah nilai yang diberikan dalam interval (-1.1). Dan ini akan menjadi awal yang baik bagi kita. Kita dapat mendefinisikan fungsi Gradient di sepanjang sumbu Y. Di bagian atas interval, kita membandingkan bidang gradien dengan -1 (udara), dan di bagian bawah dengan 1 (bumi).
terraintree =
{
{name = "ground_gradient", ketik = "gradient", x1 = 0, x2 = 0, y1 = 0, y2 = 1}
}
(Saya akan menjelaskan entri tersebut secara singkat. Kode untuk contoh-contoh ini ditulis dalam tabel pernyataan Lua. Untuk informasi lebih lanjut tentang format, lihat bagian tentang
integrasi Lua . Intinya, format ini dirancang untuk diuraikan oleh kelas khusus yang membaca iklan dan mengubahnya menjadi pohon contoh modul noise. Saya lebih suka ini format lebih verbose langkah-demi-langkah format C ++, karena lebih kompak dan lebih bersih. Menurut saya, kode sumber lebih mudah dibaca dan dikompresi daripada kode C ++. Untuk sebagian besar, deklarasi mudah dibaca dan dimengerti. Modul memiliki nama, sumber ditentukan nama atau nilai. Kode Lua yang digunakan untuk menguraikan deklarasi tabel termasuk dalam kode sumber jika Anda ingin menggunakan deklarasi ini secara langsung.)
Dalam kasus 2D, fungsi Gradient menerima segmen garis lurus dalam bentuk (x1, x2, y1, y2), dan dalam kasus 3D, format diperluas ke (x1, x2, y1, y2, z1, z2). Titik yang dibentuk oleh (x1, y1) menunjukkan awal dari segmen garis yang terkait dengan 0. Titik yang terbentuk (x2, y2) adalah akhir dari segmen yang terkait dengan 1. Artinya, di sini kita memetakan segmen garis (0,1) -> ( 0,0) dengan gradien. Oleh karena itu, gradien akan berada di antara wilayah fungsi Y = 1 dan Y = 0. Yaitu, jalur ini membentuk dimensi dunia dalam Y. Bagian mana pun dari dunia akan berada di jalur ini. Kita dapat menjepret wilayah mana pun di sepanjang X (hampir tak terhingga, tapi di sini ketepatan
double
membatasi kita), tetapi semuanya menarik, mis. permukaan bumi akan berada dalam pita ini. Perilaku ini dapat diubah, tetapi di dalamnya kita memiliki tingkat fleksibilitas yang besar. Hanya saja, jangan lupa bahwa nilai apa pun yang berada di atas atau di bawah pita ini cenderung tidak menarik, karena nilai-nilai di atas kemungkinan besar adalah udara, dan nilai-nilai di bawah ini adalah tanah. (Seperti yang akan segera Anda lihat, pernyataan ini mungkin ternyata salah.) Untuk sebagian besar gambar dalam seri ini, saya akan mencocokkan wilayah kuadrat yang diberikan oleh kuadrat (0,1) -> (1,0) dalam ruang 2D. Karena itu, pada awalnya dunia kita terlihat seperti ini:
Sejauh ini tidak ada yang menarik; Selain itu, gambar ini tidak menjawab pertanyaan "apakah titik yang diberikan solid atau kosong?". Untuk menjawab pertanyaan ini, kita perlu menerapkan
Fungsi Langkah (fungsi yang didefinisikan secara terpisah). Alih-alih gradien yang halus, kita membutuhkan pemisahan yang jelas, di mana semua lokasi di satu sisi berlubang, dan semua lokasi di sisi lain padat. Di
ANL, ini dapat diimplementasikan menggunakan fungsi
Select . Fungsi Select menerima dua fungsi atau nilai yang masuk (dalam hal ini mereka akan sama dengan "solid" dan "hollow" (Open)), dan memilihnya berdasarkan nilai fungsi kontrol (dalam hal ini, Gradient). Modul Select memiliki dua parameter tambahan,
ambang dan
falloff , yang memengaruhi proses ini. Pada tahap ini,
falloff tidak diinginkan, jadi kami akan membuatnya sama dengan 0. Parameter
ambang batas menentukan ke mana garis pemisah antara Solid dan Open akan menuju. Segala sesuatu yang akan lebih besar dari nilai ini dalam fungsi Gradient akan berubah menjadi Padat, dan segala sesuatu yang kurang dari ambang akan menjadi Terbuka. Karena Gradient membandingkan interval dengan nilai dari 0 dan 1, akan logis untuk menempatkan ambang pada 0,5. Jadi kami membagi ruang menjadi dua. Nilai 1 akan menjadi lokasi yang solid, dan nilai 0 akan kosong. Artinya, kita mendefinisikan fungsi bidang bumi sebagai berikut:
terraintree =
{
{name = "ground_gradient", ketik = "gradient", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_select", ketik = "pilih", rendah = 0, tinggi = 1, ambang batas = 0,5, kontrol = "ground_gradient"}
}
Membandingkan area fungsi yang sama seperti sebelumnya, kita mendapatkan sesuatu yang serupa:
Gambar ini dengan jelas menjawab pertanyaan apakah titik yang diberikan padat atau kosong. Kita dapat memanggil fungsi dengan koordinat yang memungkinkan dari ruang 2D, dan hasilnya adalah 1 atau 0, tergantung di mana titik tersebut relatif terhadap permukaan bumi. Namun, fungsi seperti itu tidak terlalu menarik, itu hanya garis datar yang membentang hingga tak terbatas. Untuk menghidupkan kembali gambar, kami menggunakan teknik yang disebut "turbulensi".
"Turbulensi" adalah penunjukan yang kompleks dari konsep menambahkan nilai pada koordinat fungsi yang masuk. Bayangkan kita menyebut fungsi bumi di atas dengan koordinat (0,1). Itu terletak di atas bidang tanah, karena pada Y = 1 gradien memiliki nilai 0, yang kurang dari ambang = 0,5. Artinya, titik ini akan dihitung sebagai Terbuka. Tetapi bagaimana jika, sebelum menjalankan fungsi bumi, kita entah bagaimana mengubah titik ini? Misalkan kita kurangi nilai acak dari koordinat Y, misalnya, 3. Kita kurangi 3 dan dapatkan koordinatnya (0, -2). Jika sekarang kita memanggil fungsi ground untuk titik ini, maka titik tersebut akan dianggap solid, karena Y = -2 terletak di bawah segmen Gradient yang sesuai dengan 1. Tiba-tiba, titik berongga (0,1) menjadi padat. Kami akan mendapatkan balok batu yang menggantung di udara. Ini dapat dilakukan dengan titik mana saja dalam fungsi dengan menambahkan atau mengurangi angka acak dari koordinat Y dari titik yang masuk sebelum memanggil fungsi ground_select. Berikut adalah gambar dari fungsi ground_select yang menunjukkan ini. Sebelum memanggil fungsi ground_select, nilai dalam interval (-0,25, 0,25) ditambahkan ke koordinat Y dari setiap titik.

Ini lebih menarik daripada garis datar, tetapi tidak sangat mirip dengan bumi, karena setiap titik bergerak ke nilai yang sepenuhnya acak, yang menciptakan pola kacau. Namun, jika kita menggunakan fungsi acak kontinu, misalnya,
Fraktal dari perpustakaan
ANL , maka alih-alih pola acak kita mendapatkan sesuatu yang lebih terkendali. Karena itu, mari kita hubungkan fraktal ke bidang bumi dan lihat apa yang terjadi.
terraintree =
{
{name = "ground_gradient", ketik = "gradient", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_shape_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 6, frekuensi = 2},
{name = "ground_scale", ketik = "scaleoffset", skala = 0,5, offset = 0, sumber = "ground_shape_fractal"},
{name = "ground_perturb", type = "translationomain", source = "ground_gradient", ty = "ground_scale"},
{name = "ground_select", ketik = "pilih", rendah = 0, tinggi = 1, ambang batas = 0,5, kontrol = "ground_perturb"}
}
Di sini perlu diperhatikan beberapa aspek. Pertama, kita mendefinisikan modul Fraktal, dan rantai itu ke modul
ScaleOffset . Modul ScaleOffset menskalakan nilai fraktal output ke tingkat yang lebih nyaman. Bagian dari bantuan mungkin bergunung-gunung dan membutuhkan skala yang lebih besar, dan bagian lain - lebih rata dan dengan skala yang lebih kecil. Kita akan berbicara tentang berbagai jenis medan nanti, tetapi untuk sekarang kita akan menggunakannya untuk demonstrasi. Nilai-nilai output dari fungsi sekarang akan memberikan gambar berikut:
Ini lebih menarik daripada sekadar noise acak, bukan? Setidaknya, itu lebih mirip tanah, meskipun bagian dari bentang alamnya terlihat tidak biasa, dan pulau-pulau terbangnya benar-benar aneh. Alasan untuk ini adalah bahwa setiap titik individu dari peta output secara acak bergeser dengan nilai yang berbeda ditentukan oleh fraktal. Untuk menggambarkan hal ini, perlihatkan output fraktal yang melakukan distorsi:

Pada gambar di atas, semua titik hitam memiliki nilai -0,25, dan semua titik putih memiliki nilai 0,25. Artinya, di mana fraktal berwarna hitam, titik yang sesuai dari fungsi bumi akan bergeser "turun" sebesar 0,25. (0,25 berarti 1/4 layar). Karena satu titik dapat sedikit diimbangi, dan titik lainnya di atasnya dalam ruang dapat lebih digeser, ada kemungkinan tonjolan batu dan pulau terbang. Tonjolan di alam cukup alami, berbeda dengan pulau-pulau terbang. (Kecuali jika kita berada di film "Avatar.") Jika permainan Anda membutuhkan lanskap yang fantastis, itu bagus, tetapi jika Anda memerlukan model yang lebih realistis, kita perlu sedikit menyesuaikan fungsi fraktal. Untungnya, fungsi
ScaleDomain dapat melakukan
ini .
Kami ingin membuat fungsi berperilaku seperti fungsi peta ketinggian. Bayangkan sebuah peta ketinggian 2D di mana setiap titik di peta mewakili ketinggian suatu titik dalam kisi titik kisi yang dinaikkan atau turun. Nilai putih peta menunjukkan bukit tinggi, hitam - lembah rendah. Kita membutuhkan perilaku yang serupa, tetapi untuk mencapainya, kita perlu menyingkirkan salah satu dimensi. Dalam kasus peta ketinggian, kami membuat ketinggian 3D dari peta ketinggian 2D. Demikian pula, dalam kasus medan 2D, kita membutuhkan peta ketinggian 1D. Setelah dibuat sehingga semua titik fraktal dengan koordinat Y yang sama memiliki nilai yang sama, kita dapat menggeser semua poin dengan koordinat X yang sama dengan jumlah yang sama, sehingga pulau-pulau terbang menghilang. Untuk melakukan ini, Anda bisa menggunakan ScaleDomain, mengatur ulang koefisien scaley. Yaitu, sebelum memanggil fungsi ground_shape_fractal, kami memanggil ground_scale_y untuk mengatur koordinat y ke 0. Ini memastikan bahwa nilai Y tidak mempengaruhi output fraktal, pada dasarnya mengubahnya menjadi fungsi noise 1D. Untuk melakukan ini, kami akan melakukan perubahan berikut:
terraintree =
{
{name = "ground_gradient", ketik = "gradient", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_shape_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 6, frekuensi = 2},
{name = "ground_scale", ketik = "scaleoffset", skala = 0,5, offset = 0, sumber = "ground_shape_fractal"},
{name = "ground_scale_y", ketik = "scaledomain", source = "ground_scale", scaley = 0},
{name = "ground_perturb", type = "translationomain", source = "ground_gradient", ty = "ground_scale_y"},
{name = "ground_select", ketik = "pilih", rendah = 0, tinggi = 1, ambang batas = 0,5, kontrol = "ground_perturb"}
}
Kami akan rantai fungsi ScaleDomain dengan ground_scale, dan kemudian memodifikasi data ground_perturb asli menjadi fungsi ScaleDomain. Ini akan mengubah fraktal yang menggeser bumi dan mengubahnya menjadi seperti ini:
Sekarang jika kita melihat outputnya, kita mendapatkan hasilnya:
Jauh lebih baik. Pulau-pulau terbang telah sepenuhnya menghilang, dan reliefnya lebih seperti gunung dan bukit. Sayangnya, kami kehilangan tonjolan dan tebing. Sekarang seluruh bumi terus menerus dan miring. Jika mau, Anda dapat memperbaikinya dengan beberapa cara.
Pertama, Anda dapat menggunakan fungsi
TranslateDomain lain, ditambah dengan fungsi
Fraktal lainnya. Jika kita menerapkan sedikit turbulensi fraktal ke arah X, kita dapat sedikit merusak tepi dan permukaan pegunungan, dan ini mungkin akan cukup untuk membentuk tebing dan tepian. Mari kita melihatnya dalam aksi.
terraintree =
{
{name = "ground_gradient", ketik = "gradient", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_shape_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 6, frekuensi = 2},
{name = "ground_scale", ketik = "scaleoffset", skala = 0,5, offset = 0, sumber = "ground_shape_fractal"},
{name = "ground_scale_y", ketik = "scaledomain", source = "ground_scale", scaley = 0},
{name = "ground_perturb", type = "translationomain", source = "ground_gradient", ty = "ground_scale_y"},
{name = "ground_overhang_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 6, frekuensi = 2},
{name = "ground_overhang_scale", ketik = "scaleoffset", sumber = "ground_overhang_fractal", skala = 0,2, offset = 0},
{name = "ground_overhang_perturb", type = "translationomain", source = "ground_perturb", tx = "ground_overhang_scale"},
{name = "ground_select", ketik = "pilih", rendah = 0, tinggi = 1, ambang = 0,5, kontrol = "ground_overhang_perturb"}
}
Dan inilah hasilnya:
Cara kedua: Anda cukup mengatur parameter
scaley dari fungsi
ground_scale_y ke nilai lebih besar dari 0. Jika Anda meninggalkan skala kecil di Y, kita akan mendapatkan sebagian kecil dari variasi, namun, semakin besar skalanya, semakin kuat lega akan menyerupai versi sebelumnya tanpa penskalaan.
Hasilnya terlihat jauh lebih menarik daripada gunung miring biasa. Namun, tidak peduli betapa menariknya mereka, pemain akan tetap bosan dengan menjelajahi relief dengan pola yang sama, membentang sejauh beberapa kilometer. Selain itu, kelegaan seperti itu akan sangat tidak realistis. Di dunia nyata, ada banyak variabilitas yang membuat medannya lebih menarik. Mari kita lihat apa yang bisa dilakukan untuk membuat dunia lebih beragam.
Melihat contoh kode sebelumnya, Anda dapat melihat pola tertentu di dalamnya. Kami memiliki fungsi gradien, yang dikendalikan oleh fungsi-fungsi yang memberikan bentuk pada bumi, setelah itu fungsi yang didefinisikan secara terpisah diterapkan dan bumi menjadi penuh. Artinya, akan lebih logis untuk mempersulit bantuan pada tahap pembentukan bumi. Alih-alih satu pemindahan fraktal sepanjang Y dan pemindahan lain sepanjang X, kita dapat mencapai tingkat kerumitan yang diperlukan (dengan mempertimbangkan kinerja: setiap fraktal membutuhkan biaya komputasi tambahan, jadi kita harus mencoba untuk bersikap konservatif.) Kita dapat menentukan bentuk bumi, yaitu gunung, kaki gunung , dataran rendah datar, tanah terlantar, dll ... dan gunakan output dari berbagai fungsi
Pilih yang dirantai dengan fraktal frekuensi rendah untuk menguraikan area masing-masing jenis. Jadi mari kita lihat bagaimana Anda bisa menerapkan berbagai jenis medan.
Untuk mengilustrasikan prinsip tersebut, kami membedakan tiga jenis bantuan: dataran tinggi (bukit miring halus), gunung dan dataran rendah (kebanyakan datar). Untuk beralih di antara mereka, kami menggunakan sistem berbasis-pilih dan menggabungkannya ke dalam kanvas yang kompleks. Jadi di sini kita pergi ...
Kaki:Bersama mereka, semuanya sederhana. Kita dapat mengambil skema yang digunakan di atas, sedikit mengurangi amplitudo bukit, bahkan mungkin membuatnya lebih subtraktif daripada aditif. untuk menurunkan ketinggian rata-rata. Kami juga dapat mengurangi jumlah oktaf untuk menghaluskannya.
{name = "lowland_shape_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 2, frekuensi = 1},
{name = "lowland_autocorrect", type = "autocorrect", sumber = "lowland_shape_fractal", rendah = 0, tinggi = 1},
{name = "lowland_scale", ketik = "scaleoffset", source = "lowland_autocorrect", skala = 0,2, offset = -0,25},
{name = "lowland_y_scale", type = "scaledomain", source = "lowland_scale", scaley = 0},
{name = "lowland_terrain", type = "translationomain", source = "ground_gradient", ty = "lowland_y_scale"},
Dataran tinggi:Dengan mereka juga, semuanya sederhana. (Sebenarnya, tidak satu pun dari jenis medan ini yang sulit.) Namun, kami menggunakan dasar yang berbeda untuk membuat bukit terlihat seperti bukit pasir.
{name = "highland_shape_fractal", type = "fractal", fractaltype = anl.RIDGEDMULTI, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 2, frekuensi = 2},
{name = "highland_autocorrect", type = "autocorrect", sumber = "highland_shape_fractal", rendah = 0, tinggi = 1},
{name = "highland_scale", ketik = "scaleoffset", sumber = "highland_autocorrect", skala = 0,45, offset = 0},
{name = "highland_y_scale", type = "scaledomain", source = "highland_scale", scaley = 0},
{name = "highland_terrain", type = "translationomain", source = "ground_gradient", ty = "highland_y_scale"},
Pegunungan: {name = "mountain_shape_fractal", type = "fractal", fractaltype = anl.BILLOW, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 4, frekuensi = 1},
{name = "mountain_autocorrect", ketik = "koreksi otomatis", source = "mountain_shape_fractal", rendah = 0, tinggi = 1},
{name = "mountain_scale", type = "scaleoffset", source = "mountain_autocorrect", skala = 0,75, offset = 0,25},
{name = "mountain_y_scale", type = "scaledomain", source = "mountain_scale", scaley = 0,1},
{name = "mountain_terrain", type = "translationomain", source = "ground_gradient", ty = "mountain_y_scale"},
Tentu saja, Anda dapat mendekati proses ini bahkan lebih kreatif, tetapi secara umum polanya akan seperti itu. Kami menyoroti karakteristik jenis bantuan dan memilih fungsi kebisingan untuknya. Untuk semua ini, prinsip yang sama berlaku; Perbedaan utamanya adalah skala. Sekarang, untuk menghubungkan keduanya, kami akan menyiapkan fraktal tambahan yang akan mengontrol fungsi
Select . Lalu kami rantai modul Pilih untuk menghasilkan seluruh medan.
{name = "terrain_type_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 3, frekuensi = 0,5},
{name = "terrain_autocorrect", type = "autocorrect", source = "terrain_type_fractal", rendah = 0, tinggi = 1},
{name = "terrain_type_cache", ketik = "cache", source = "terrain_autocorrect"},
{name = "highland_mountain_select", type = "select", low = "highland_terrain", high = "mountain_terrain", control = "terrain_type_cache", ambang batas = 0,55, falloff = 0,15},
{name = "highland_lowland_select", type = "select", low = "lowland_terrain", high = "highland_mountain_select", control = "terrain_type_cache", ambang batas = 0,25, falloff = 0,15},
Jadi, di sini kita mendefinisikan tiga jenis medan utama: dataran rendah, dataran tinggi dan pegunungan. Kami menggunakan satu fraktal untuk memilih salah satunya, sehingga ada transisi alami (dataran rendah-> dataran tinggi-> pegunungan). Kemudian kami menggunakan fraktal lain untuk secara acak memasukkan tanah tandus ke dalam peta. Inilah yang terlihat seperti rangkaian modul jadi:
terraintree =
{
{name = "lowland_shape_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 2, frekuensi = 1},
{name = "lowland_autocorrect", type = "autocorrect", sumber = "lowland_shape_fractal", rendah = 0, tinggi = 1},
{name = "lowland_scale", ketik = "scaleoffset", source = "lowland_autocorrect", skala = 0,2, offset = -0,25},
{name = "lowland_y_scale", type = "scaledomain", source = "lowland_scale", scaley = 0},
{name = "lowland_terrain", type = "translationomain", source = "ground_gradient", ty = "lowland_y_scale"},
{name = "ground_gradient", ketik = "gradient", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "highland_shape_fractal", type = "fractal", fractaltype = anl.RIDGEDMULTI, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 2, frekuensi = 2},
{name = "highland_autocorrect", type = "autocorrect", sumber = "highland_shape_fractal", rendah = 0, tinggi = 1},
{name = "highland_scale", ketik = "scaleoffset", sumber = "highland_autocorrect", skala = 0,45, offset = 0},
{name = "highland_y_scale", type = "scaledomain", source = "highland_scale", scaley = 0},
{name = "highland_terrain", type = "translationomain", source = "ground_gradient", ty = "highland_y_scale"},
{name = "mountain_shape_fractal", type = "fractal", fractaltype = anl.BILLOW, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 4, frekuensi = 1},
{name = "mountain_autocorrect", ketik = "koreksi otomatis", source = "mountain_shape_fractal", rendah = 0, tinggi = 1},
{name = "mountain_scale", type = "scaleoffset", source = "mountain_autocorrect", skala = 0,75, offset = 0,25},
{name = "mountain_y_scale", type = "scaledomain", source = "mountain_scale", scaley = 0,1},
{name = "mountain_terrain", type = "translationomain", source = "ground_gradient", ty = "mountain_y_scale"},
{name = "terrain_type_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 3, frekuensi = 0,5},
{name = "terrain_autocorrect", type = "autocorrect", source = "terrain_type_fractal", rendah = 0, tinggi = 1},
{name = "terrain_type_cache", ketik = "cache", source = "terrain_autocorrect"},
{name = "highland_mountain_select", type = "select", low = "highland_terrain", high = "mountain_terrain", control = "terrain_type_cache", ambang batas = 0,55, falloff = 0,15},
{name = "highland_lowland_select", type = "select", low = "lowland_terrain", high = "highland_mountain_select", control = "terrain_type_cache", ambang batas = 0,25, falloff = 0,15},
{name = "ground_select", ketik = "pilih", rendah = 0, tinggi = 1, ambang = 0,5, kontrol = "highland_lowland_select"}
}
Berikut adalah beberapa contoh relief yang dihasilkan:
Anda mungkin memperhatikan bahwa variabilitas yang agak tinggi diperoleh. Di beberapa tempat, gunung-gunung yang menjulang tinggi muncul, di tempat lain ada dataran landai yang halus. Sekarang kita perlu menambahkan gua sehingga kita bisa menjelajahi keajaiban dunia bawah.
Untuk gua, saya menggunakan sistem multiplikatif yang diterapkan ke
ground_select . Yaitu, saya membuat fungsi yang menghasilkan 1 atau 0, dan mengalikannya dengan output dari
ground_select . Berkat ini, setiap titik fungsi menjadi berongga dimana nilai fungsi gua adalah 0. Yaitu, di mana saya ingin mendapatkan gua, fungsi gua harus kembali 0, dan di mana gua tidak boleh, fungsi harus 1. Adapun bentuk gua, saya ingin membangun sistem gua berdasarkan 1-oktaf
Ridged Multifractal .
{name = "cave_shape", type = "fractal", fractaltype = anl.RIDGEDMULTI, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 1, frekuensi = 2},
Hasilnya kira-kira seperti ini:
Jika kita menerapkan fungsi
Select sebagai fungsi yang didefinisikan secara terpisah, seperti yang kita lakukan dengan gradien bumi, mengimplementasikannya sehingga bagian bawah dari ambang pilih adalah 1 (tidak ada gua), dan bagian atas adalah 0 (ada gua), hasilnya akan terlihat seperti ini :
{name = "cave_shape", type = "fractal", fractaltype = anl.RIDGEDMULTI, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 1, frekuensi = 2},
{name = "cave_select", ketik = "pilih", rendah = 1, tinggi = 0, kontrol = "cave_shape", ambang batas = 0.8, falloff = 0},
Hasil:
Tentu saja, itu terlihat cukup halus, jadi tambahkan beberapa suara fraktal untuk merusak area.
{name = "cave_shape", type = "fractal", fractaltype = anl.RIDGEDMULTI, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 1, frekuensi = 2},
{name = "cave_select", ketik = "pilih", rendah = 1, tinggi = 0, kontrol = "cave_shape", ambang batas = 0.8, falloff = 0},
{name = "cave_perturb_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 6, frekuensi = 3},
{name = "cave_perturb_scale", type = "scaleoffset", source = "cave_perturb_fractal", skala = 0,25, offset = 0},
{name = "cave_perturb", type = "translationomain", source = "cave_select", tx = "cave_perturb_scale"},
Hasil:Ini membuat gua sedikit bising dan membuat mereka tidak begitu mulus. Sekarang mari kita lihat apa yang terjadi jika Anda menerapkan gua ke relief:Dengan bereksperimen dengan nilai ambang di cave_select , kita dapat membuat gua lebih tipis atau lebih tebal. Tetapi hal utama yang perlu kita coba adalah memastikan bahwa gua-gua tidak menggerogoti serpihan besar permukaan seperti itu. Untuk melakukan ini, kita dapat kembali ke fungsi highland_lowland_select , yang, seperti kita ingat, adalah fungsi relief terakhir yang mendistorsi gradien bumi. Apa yang berguna dalam fungsi ini adalah bahwa ia masih berupa gradien, meningkatkan nilai ketika fungsinya semakin dalam ke tanah. Kita dapat menggunakan gradien untuk melemahkan fungsi gua sehingga gua meningkat saat mereka masuk lebih dalam ke tanah. Untungnya bagi kita, redaman ini dapat dicapai hanya dengan mengalikan output dari fungsi highland_lowland_selectke output cave_shape , dan kemudian meneruskan hasilnya ke seluruh rantai fungsi. Selanjutnya, kami akan membuat perubahan penting di sini - kami akan menambahkan fungsi Cache . Fungsi caching menyimpan hasil fungsi untuk koordinat masuk yang diberikan, dan jika fungsi dipanggil berulang kali dengan koordinat yang sama, itu akan mengembalikan salinan yang di-cache, dan tidak akan menghitung hasilnya lagi. Ini berguna dalam situasi di mana satu fungsi kompleks ( highland_lowland_select ) dalam rantai fungsi disebut beberapa kali. Tanpa cache, seluruh rantai fungsi kompleks dihitung ulang dengan setiap panggilan. Untuk menambahkan cache, pertama-tama kita perlu melakukan perubahan berikut:{name = "highland_lowland_select", type = "select", low = "lowland_terrain", high = "highland_mountain_select", control = "terrain_type_cache", ambang batas = 0,25, falloff = 0,15},
{name = "highland_lowland_select_cache", ketik = "cache", source = "highland_lowland_select"},
{name = "ground_select", ketik = "pilih", rendah = 0, tinggi = 1, ambang batas = 0,5, kontrol = "highland_lowland_select_cache"},
Jadi kami menambahkan Cache, dan kemudian mengarahkan input ke ground_select sehingga diambil dari cache, dan tidak langsung dari fungsinya. Kemudian kita dapat mengubah kode gua untuk menambahkan redaman:{name = "cave_shape", type = "fractal", fractaltype = anl.RIDGEDMULTI, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 1, frekuensi = 4},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape", source_1="cave_attenuate_bias"},
{name="cave_perturb_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
{name="cave_perturb_scale", type="scaleoffset", source="cave_perturb_fractal", scale=0.5, offset=0},
{name="cave_perturb", type="translatedomain", source="cave_shape_attenuate", tx="cave_perturb_scale"},
{name="cave_select", type="select", low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},
Pertama-tama, kami menambahkan fungsi Bias . Ini untuk kenyamanan, karena memungkinkan kita untuk menyesuaikan interval fungsi redaman gradien. Kemudian fungsi cave_shape_attenuate ditambahkan , yang merupakan Combiner dari tipe anl :: MULT . Dia mengalikan gradien dengan cave_shape . Kemudian hasil operasi ini diteruskan ke fungsi cave_perturb . Hasilnya terlihat seperti ini:Kita melihat bahwa semakin dekat ke permukaan bumi telah menjadi lebih tipis. (Jangan perhatikan bagian paling atas, ini hanya sebuah artefak dari nilai gradien negatif, itu tidak mempengaruhi gua yang sudah jadi. Jika ini menjadi masalah - katakanlah jika kita menggunakan fungsi ini untuk hal lain, maka kita dapat membatasi gradien ke interval (0, 1).) Agak sulit untuk melihat bagaimana ini bekerja dalam kaitannya dengan medan, jadi mari kita bergerak dan menyatukan semuanya untuk melihat apa yang terjadi. Inilah keseluruhan rangkaian fungsi yang telah kita buat sejauh ini.terraintree =
{
{name = "ground_gradient", ketik = "gradient", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "lowland_shape_fractal", type = "fractal", fractaltype = anl.BILLOW, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 2, frekuensi = 0,25},
{name="lowland_autocorrect", type="autocorrect", source="lowland_shape_fractal", low=0, high=1},
{name="lowland_scale", type="scaleoffset", source="lowland_autocorrect", scale=0.125, offset=-0.45},
{name="lowland_y_scale", type="scaledomain", source="lowland_scale", scaley=0},
{name="lowland_terrain", type="translatedomain", source="ground_gradient", ty="lowland_y_scale"},
{name="highland_shape_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=4, frequency=2},
{name="highland_autocorrect", type="autocorrect", source="highland_shape_fractal", low=-1, high=1},
{name="highland_scale", type="scaleoffset", source="highland_autocorrect", scale=0.25, offset=0},
{name="highland_y_scale", type="scaledomain", source="highland_scale", scaley=0},
{name="highland_terrain", type="translatedomain", source="ground_gradient", ty="highland_y_scale"},
{name="mountain_shape_fractal", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=8, frequency=1},
{name="mountain_autocorrect", type="autocorrect", source="mountain_shape_fractal", low=-1, high=1},
{name="mountain_scale", type="scaleoffset", source="mountain_autocorrect", scale=0.45, offset=0.15},
{name="mountain_y_scale", type="scaledomain", source="mountain_scale", scaley=0.25},
{name="mountain_terrain", type="translatedomain", source="ground_gradient", ty="mountain_y_scale"},
{name="terrain_type_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=3, frequency=0.125},
{name="terrain_autocorrect", type="autocorrect", source="terrain_type_fractal", low=0, high=1},
{name="terrain_type_y_scale", type="scaledomain", source="terrain_autocorrect", scaley=0},
{name="terrain_type_cache", type="cache", source="terrain_type_y_scale"},
{name="highland_mountain_select", type="select", low="highland_terrain", high="mountain_terrain", control="terrain_type_cache", threshold=0.55, falloff=0.2},
{name="highland_lowland_select", type="select", low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
{name="highland_lowland_select_cache", type="cache", source="highland_lowland_select"},
{name="ground_select", type="select", low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},
{name="cave_shape", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape", source_1="cave_attenuate_bias"},
{name="cave_perturb_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
{name="cave_perturb_scale", type="scaleoffset", source="cave_perturb_fractal", scale=0.5, offset=0},
{name="cave_perturb", type="translatedomain", source="cave_shape_attenuate", tx="cave_perturb_scale"},
{name="cave_select", type="select", low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},
{name = "ground_cave_multiply", type = "combiner", operation = anl.MULT, source_0 = "cave_select", source_1 = "ground_select"}
}
Berikut adalah contoh kartu acak yang berasal dari fungsi ini:Sekarang semuanya terlihat cukup bagus. Semua gua adalah gua yang agak besar jauh di bawah tanah, tetapi lebih dekat ke permukaan mereka biasanya berubah menjadi terowongan kecil. Ini membantu menciptakan suasana misteri. Menjelajahi permukaan, Anda menemukan pintu masuk kecil ke gua. Kemana dia pergi? Seberapa dalam itu meluas? Kita tidak dapat mengetahui hal ini, tetapi dalam proses mempelajarinya mulai berkembang, berubah menjadi sistem gua yang luas yang dipenuhi dengan kegelapan dan bahaya. Dan menjarah, tentu saja. Selalu ada banyak jarahan.Anda dapat mengubah sistem ini dengan berbagai cara, mendapatkan hasil yang berbeda. Kita dapat mengubah parameter ambang untuk cave_select dan parameter untuk cave_attenuate_bias , atau mengganti cave_attenuate_biasfungsi lain untuk mencocokkan interval gradien ke nilai lain yang lebih sesuai dengan kebutuhan Anda. Anda juga dapat menambahkan fraktal lain yang mendistorsi sistem gua di sepanjang sumbu Y untuk menghilangkan kemungkinan terowongan halus yang tidak wajar di sepanjang sumbu X (disebabkan oleh kenyataan bahwa bentuk gua hanya terdistorsi sepanjang sumbu X). Anda juga dapat menambahkan fraktal baru sebagai sumber pelemahan tambahan, tentukan sumber ketiga untuk cave_shape_attenuate , yang menskala pelemahan berdasarkan wilayah, sehingga gua di beberapa daerah lebih padat (misalnya, di pegunungan), dan lebih jarang atau sama sekali tidak ada di tempat lain. Pilihan regional ini dapat dibuat dari fungsi terrain_type_fractaluntuk mengetahui di mana daerah pegunungan berada. Itu semua bermuara pada hanya memikirkan apa yang Anda inginkan, mencari tahu apa efek fungsi yang berbeda akan memiliki pada output, dan bereksperimen dengan parameter sampai Anda mendapatkan hasil yang diinginkan. Ini bukan ilmu pasti, dan seringkali efek yang diinginkan dapat dicapai dengan cara yang berbeda.Kekurangan
Metode pembuatan medan ini memiliki kekurangan. Proses menghasilkan suara bisa sangat lambat. Penting untuk mengurangi jumlah fraktal, jumlah oktaf fraktal yang Anda gunakan, dan operasi lambat lainnya, jika mungkin. Cobalah untuk menggunakan fraktal beberapa kali dan cache semua fungsi yang disebut beberapa kali. Dalam contoh ini, saya bebas menggunakan fraktal, membuat fraktal untuk masing-masing dari tiga jenis relief. Menggunakan ScaleOffset untuk mengubah interval dan mengambil satu fraktal sebagai dasar untuk semuanya, saya akan menghemat banyak waktu prosesor. Dalam 2D, semuanya tidak terlalu buruk, tetapi ketika Anda mendapatkan 3D dan mencoba membandingkan jumlah data, waktu pemrosesan akan meningkat secara signifikan.Pergi ke 3D
Semua ini bagus jika Anda membuat game seperti Terraria atau King Arthur's Gold , tetapi bagaimana jika Anda membutuhkan sesuatu seperti Minecraft atau Infiniminer? Perubahan apa yang perlu kita lakukan untuk rantai fungsi? Padahal, jumlahnya tidak banyak. Fungsi yang ditunjukkan di atas akan bekerja hampir tanpa modifikasi untuk bantuan 3D. Ini akan cukup bagi Anda untuk membandingkan volume 3D menggunakan variasi 3D generator, dan juga membandingkan sumbu Y dengan sumbu vertikal volume, dan bukan dengan wilayah 2D. Namun, satu perubahan tetap diperlukan, yaitu, cara untuk mewujudkan gua. Seperti yang Anda lihat, Ridged Multifractal sangat bagus untuk sistem gua 2D, tetapi dalam 3D memotong banyak kulit melengkung, bukan terowongan, dan efeknya ternyata salah. Artinya, dalam 3D perlu untuk menentukan dua bentuk gua fraktal, keduanya 1-oktaf Ridract Multifractal noise, tetapi dengan biji yang berbeda. Menggunakan Select, atur ke 1 atau 0, dan gandakan. Dengan demikian, di persimpangan fraktal, sebuah gua akan muncul,dan segala sesuatu yang lain akan tetap solid, dan penampilan terowongan akan menjadi lebih alami daripada menggunakan fraktal tunggal.terraintree3d=
{
{name="ground_gradient", type="gradient", x1=0, x2=0, y1=0, y2=1},
{name="lowland_shape_fractal", type="fractal", fractaltype=anl.BILLOW, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=2, frequency=0.25},
{name="lowland_autocorrect", type="autocorrect", source="lowland_shape_fractal", low=0, high=1},
{name="lowland_scale", type="scaleoffset", source="lowland_autocorrect", scale=0.125, offset=-0.45},
{name="lowland_y_scale", type="scaledomain", source="lowland_scale", scaley=0},
{name="lowland_terrain", type="translatedomain", source="ground_gradient", ty="lowland_y_scale"},
{name="highland_shape_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=4, frequency=2},
{name="highland_autocorrect", type="autocorrect", source="highland_shape_fractal", low=-1, high=1},
{name="highland_scale", type="scaleoffset", source="highland_autocorrect", scale=0.25, offset=0},
{name="highland_y_scale", type="scaledomain", source="highland_scale", scaley=0},
{name="highland_terrain", type="translatedomain", source="ground_gradient", ty="highland_y_scale"},
{name="mountain_shape_fractal", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=8, frequency=1},
{name="mountain_autocorrect", type="autocorrect", source="mountain_shape_fractal", low=-1, high=1},
{name="mountain_scale", type="scaleoffset", source="mountain_autocorrect", scale=0.45, offset=0.15},
{name="mountain_y_scale", type="scaledomain", source="mountain_scale", scaley=0.25},
{name="mountain_terrain", type="translatedomain", source="ground_gradient", ty="mountain_y_scale"},
{name="terrain_type_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=3, frequency=0.125},
{name="terrain_autocorrect", type="autocorrect", source="terrain_type_fractal", low=0, high=1},
{name="terrain_type_y_scale", type="scaledomain", source="terrain_autocorrect", scaley=0},
{name="terrain_type_cache", type="cache", source="terrain_type_y_scale"},
{name="highland_mountain_select", type="select", low="highland_terrain", high="mountain_terrain", control="terrain_type_cache", threshold=0.55, falloff=0.2},
{name="highland_lowland_select", type="select", low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
{name="highland_lowland_select_cache", type="cache", source="highland_lowland_select"},
{name="ground_select", type="select", low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape1", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_shape2", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape1", source_1="cave_attenuate_bias", source_2="cave_shape2"},
{name = "cave_perturb_fractal", type = "fractal", fractaltype = anl.FBM, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oktaf = 6, frekuensi = 3},
{name = "cave_perturb_scale", type = "scaleoffset", source = "cave_perturb_fractal", skala = 0,5, offset = 0},
{name = "cave_perturb", type = "translationomain", source = "cave_shape_attenuate", tx = "cave_perturb_scale"},
{name = "cave_select", ketik = "pilih", rendah = 1, tinggi = 0, kontrol = "cave_perturb", ambang = 0,48, falloff = 0},
{name = "ground_cave_multiply", type = "combiner", operation = anl.MULT, source_0 = "cave_select", source_1 = "ground_select"}
}
Contoh hasil:Tampaknya beberapa pengaturan memerlukan penyetelan. Mungkin perlu mengurangi atenuasi atau membuat gua lebih tipis, mengurangi jumlah oktaf dalam fraktasi lega, sehingga lega menjadi lebih halus, dll ... Saya ulangi, itu semua tergantung pada hasil yang ingin Anda dapatkan.