Saya menggunakan C # dan XNA. Algoritma saya saat ini untuk pencahayaan adalah metode rekursif. Namun, itu mahal , ke titik di mana satu potongan 8x128x8 dihitung setiap 5 detik.
- Apakah ada metode pencahayaan lain yang akan membuat bayangan kegelapan variabel?
- Atau apakah metode rekursif itu baik, dan mungkin saya salah melakukannya?
Sepertinya hal-hal rekursif pada dasarnya mahal (dipaksa untuk melewati sekitar 25k blok per bongkahan). Saya sedang berpikir tentang menggunakan metode yang mirip dengan ray tracing, tetapi saya tidak tahu bagaimana ini akan berhasil. Hal lain yang saya coba adalah menyimpan sumber cahaya dalam Daftar, dan untuk setiap blok mendapatkan jarak ke setiap sumber cahaya, dan menggunakannya untuk menyalakannya ke tingkat yang benar, tetapi kemudian pencahayaan akan menembus dinding.
Kode rekursi saya saat ini ada di bawah. Ini disebut dari sembarang tempat di chunk yang tidak memiliki tingkat cahaya nol, setelah membersihkan dan menambahkan kembali sinar matahari dan cahaya obor.
world.get___at
adalah fungsi yang bisa mendapatkan blok di luar chunk ini (ini ada di dalam class chunk). Location
adalah struktur saya sendiri yang seperti Vector3
, tetapi menggunakan bilangan bulat bukan nilai floating point. light[,,]
adalah lightmap untuk chunk.
private void recursiveLight(int x, int y, int z, byte lightLevel)
{
Location loc = new Location(x + chunkx * 8, y, z + chunky * 8);
if (world.getBlockAt(loc).BlockData.isSolid)
return;
lightLevel--;
if (world.getLightAt(loc) >= lightLevel || lightLevel <= 0)
return;
if (y < 0 || y > 127 || x < -8 || x > 16 || z < -8 || z > 16)
return;
if (x >= 0 && x < 8 && z >= 0 && z < 8)
light[x, y, z] = lightLevel;
recursiveLight(x + 1, y, z, lightLevel);
recursiveLight(x - 1, y, z, lightLevel);
recursiveLight(x, y + 1, z, lightLevel);
recursiveLight(x, y - 1, z, lightLevel);
recursiveLight(x, y, z + 1, lightLevel);
recursiveLight(x, y, z - 1, lightLevel);
}
Jawaban:
LR
,.|VP - LP| < LR
, di mana VP adalah vektor posisi voxel relatif terhadap asal danLP
merupakan vektor posisi cahaya relatif terhadap asal. Untuk setiap cahaya yang radius voxel saat ini ditemukan berada, naikkan faktor cahayanya dengan jarak dari pusat cahaya|VP - LP|
,. Jika Anda menormalkan vektor itu dan kemudian mendapatkan besarnya, ini akan berada dalam kisaran 0,0-> 1,0. Level cahaya maksimum yang dapat dicapai voxel adalah 1.0.Runtime adalah
O(s^3 * n)
, di manas
panjang sisi (128) dari wilayah voxel Anda dann
sejumlah sumber cahaya. Jika sumber cahaya Anda statis, ini tidak masalah. Jika sumber cahaya Anda bergerak secara real time, Anda dapat bekerja hanya pada delta daripada menghitung ulang seluruh shebang setiap pembaruan.Anda bahkan bisa menyimpan voxels yang mempengaruhi setiap cahaya, sebagai referensi di dalam cahaya itu. Dengan cara ini, ketika lampu bergerak atau dihancurkan, Anda dapat melewati daftar itu, menyesuaikan nilai cahaya yang sesuai, daripada harus melintasi seluruh kotak kubik lagi.
sumber
Minecraft sendiri tidak melakukan sinar matahari dengan cara ini.
Anda cukup mengisi sinar matahari dari atas ke bawah, setiap lapisan mengumpulkan cahaya dari tetangga voxels di lapisan sebelumnya dengan atenuasi. Sangat cepat - lintasan tunggal, tidak ada daftar, tidak ada struktur data, tidak ada rekursi.
Anda harus menambahkan obor dan lampu non-flooding lainnya di masa depan.
Ada begitu banyak cara lain untuk melakukan ini, termasuk propagasi cahaya directional mewah dll, tetapi mereka jelas lebih lambat dan Anda harus mencari tahu apakah Anda ingin berinvestasi dalam realisme tambahan mengingat hukuman tersebut.
sumber
Seseorang berkata untuk menjawab pertanyaan Anda sendiri jika Anda menemukan jawabannya, jadi ya. Menemukan metode.
Apa yang saya lakukan adalah ini: Pertama, buat array boolean 3d dari "blok yang sudah diubah" overlay di atas chunk. Kemudian, isi sinar matahari, obor, dll (hanya menyalakan blok yang menyala, belum mengisi banjir). Jika Anda mengubah sesuatu, tekan "blok yang diubah" di lokasi itu menjadi true. Juga pergi dan mengubah setiap blok padat (dan karena itu tidak perlu menghitung pencahayaan untuk) menjadi "sudah berubah".
Sekarang untuk hal-hal yang berat: Pergilah ke seluruh bagian dengan 16 lintasan (untuk setiap level cahaya), dan jika 'sudah berubah' terus saja. Kemudian dapatkan level cahaya untuk blok di sekitar itu sendiri. Dapatkan level cahaya tertinggi dari mereka. Jika level lampu itu sama dengan level lampu yang dilewati saat ini, setel blok tempat Anda berada pada level saat ini, dan atur "sudah diubah" untuk lokasi tersebut menjadi true. Terus.
Saya tahu jenisnya rumit, saya mencoba menjelaskan yang terbaik. Tetapi fakta penting adalah, ia bekerja dan cepat.
sumber
Saya menyarankan algoritma yang menggabungkan solusi multi-pass Anda dengan metode rekursif asli, dan kemungkinan besar sedikit lebih cepat daripada salah satunya.
Anda akan membutuhkan 16 daftar (atau segala jenis koleksi) blok, satu untuk setiap level cahaya. (Sebenarnya, ada beberapa cara untuk mengoptimalkan algoritma ini untuk menggunakan daftar yang lebih sedikit, tetapi cara ini paling mudah untuk dijelaskan.)
Pertama, kosongkan daftar dan atur level cahaya semua blok ke nol, dan kemudian inisialisasi sumber cahaya seperti yang Anda lakukan dalam solusi Anda saat ini. Setelah (atau selama) itu, tambahkan blok dengan level cahaya yang tidak nol ke daftar yang sesuai.
Sekarang, buka daftar blok dengan level cahaya 16. Jika ada blok yang berdekatan dengan level lampu kurang dari 15, atur level cahayanya menjadi 15 dan tambahkan ke daftar yang sesuai. (Jika mereka sudah ada di daftar lain, Anda dapat menghapusnya dari daftar itu, tetapi tidak ada salahnya meskipun Anda tidak.)
Kemudian ulangi hal yang sama untuk semua daftar lainnya, dalam urutan kecerahan yang menurun. Jika Anda menemukan bahwa sebuah blok dalam daftar sudah memiliki tingkat cahaya yang lebih tinggi daripada seharusnya karena berada di daftar itu, Anda dapat mengasumsikan itu sudah diproses dan bahkan tidak perlu repot memeriksa tetangganya. (Kemudian lagi, mungkin lebih cepat hanya memeriksa tetangga - itu tergantung pada seberapa sering itu terjadi. Anda mungkin harus mencoba keduanya dan melihat mana yang lebih cepat.)
Anda mungkin mencatat bahwa saya belum menentukan bagaimana daftar harus disimpan; benar-benar hampir semua implementasi yang masuk akal harus melakukannya, selama memasukkan blok yang diberikan dan mengekstraksi blok yang sewenang-wenang keduanya operasi cepat. Daftar tertaut harus berfungsi, tetapi demikian juga implementasi setengah jalan array variabel yang layak juga. Cukup gunakan apa pun yang terbaik untuk Anda.
Tambahan: Jika sebagian besar lampu Anda tidak terlalu sering bergerak (dan tidak juga dinding), mungkin lebih cepat untuk menyimpan, untuk setiap blok yang menyala, sebuah penunjuk ke sumber cahaya yang menentukan tingkat cahayanya (atau ke salah satu dari mereka, jika beberapa diikat). Dengan begitu, Anda dapat menghindari pembaruan pencahayaan global hampir seluruhnya: jika sumber cahaya baru ditambahkan (atau yang sudah terang), Anda hanya perlu melakukan satu pas pencahayaan rekursif tunggal untuk blok di sekitarnya, sementara jika ada yang dihapus (atau redup), Anda hanya perlu memperbarui blok yang mengarah ke sana.
Anda bahkan dapat menangani perubahan dinding dengan cara ini: ketika sebuah dinding dilepas, cukup mulai lintasan penerangan rekursif baru di blok itu; ketika ditambahkan, lakukan recalc pencahayaan untuk semua blok yang mengarah ke sumber cahaya yang sama dengan blok yang baru saja di dinding.
(Jika beberapa perubahan pencahayaan terjadi sekaligus - mis. Jika lampu dipindahkan, yang dianggap sebagai penghapusan dan tambahan - Anda harus menggabungkan pembaruan menjadi satu, menggunakan algoritma di atas. Pada dasarnya, Anda nol tingkat cahaya semua blok yang menunjuk ke sumber cahaya yang dihapus, tambahkan blok yang menyala di sekelilingnya serta sumber cahaya baru (atau sumber cahaya yang ada di area yang dihilangkan) ke daftar yang sesuai, dan jalankan pembaruan seperti di atas.)
sumber