Bagaimana saya bisa membuat air tampak lebih gelap dengan kedalaman, seperti di Minecraft?


24

Di Minecraft saat Anda melihat air, semakin dalam Anda melihat semakin gelap. Adakah yang tahu cara membuat kode seperti itu?

Minecraft dengan efek minecraft dengan efek

game serupa tanpa efek game serupa tanpa efek


18
Bukankah ini dilakukan secara otomatis karena bahan kubus air semi-transparan?
pek

Saya kira tidak. Saya menambahkan gambar tanpa efek perbandingan.
Xavier

2
Mungkin itu efek campuran aditif yang hanya diterapkan pada kubus air? Sekali lagi, ini harus mudah karena bahannya semi-transparan.
pek

1
Anda juga dapat mengubah warna kotak sesuai kedalaman.
Ali1S232

Jawaban:


51

Pada dasarnya ada dua pendekatan berbeda untuk penerangan air berdasarkan kedalaman:

Voxel-Lighting

Minecraft menggunakan pencahayaan berbasis voxel, yang bekerja dengan menyebarkan cahaya ke kubus yang berdekatan, menurunkan kecerahan tergantung pada jenis blok. Lautan gelap adalah efek samping dari sistem ini.

Air memblokir sinar matahari dan mengurangi cahaya sebanyak 3 level per blok (bukan level 1 standar), yang berarti kecerahan di lautan untuk setiap jarak dari permukaan adalah:

0 (surface): 15 (direct sunlight)
1:           12
2:            9
3:            6
4:            3
5 and below:  0 (darkness)

Sumber: Minecraft Wiki - Light

Shadowing Berbasis Jarak

Dalam permainan dengan model pencahayaan tradisional, efek ini dapat dibuat dengan mengukur jumlah air yang ada di antara sumber cahaya dan dasar laut. Cahaya kemudian memudar berdasarkan jarak ini. Ada beberapa metode untuk melakukan ini:

Perhitungan Langsung

Jika Anda memiliki permukaan yang rata, Anda dapat dengan mudah menghitung jarak yang ditempuh cahaya di dalam air jika Anda melewatkan permukaan normal dari badan air \ vec {n}dan titik produk normal ini dan posisi permukaan ske dalam shader geometri.

Jarak air efektif adalah

\ maks (\ kiri (s - \ vec {n} \ cdot \ vec {p} \ kanan), 0) \ cdot \ kiri (1 + \ tan (\ alpha) \ kanan)

dimana \ vec {p}letak verteks dan alfamerupakan sudut antara arah cahaya di bawah permukaan dan permukaan air normal menuju badan air.

Saat matahari terbenam, alfahanya mencapai sedikit kurang dari 50 ° karena cahaya dibiaskan saat memasuki air.
Berikut adalah posting blog dengan penjelasan yang baik: Kamera Digital: Refleksi Internal Total
Posting lain dengan rincian lebih lanjut: Kamera Digital: Hukum Pembiasan Snell

Jika Anda menggunakan peta ketinggian pada permukaan yang sejajar dengan air, \ kiri (s - \ vec {n} \ cdot \ vec {p} \ kanan)jadilah \ kiri (s - h \ kanan). Faktor yang tepat sama dengan 1 jika matahari langsung di atas permukaan air.
Dengan cahaya titik, Anda harus menghitung alfauntuk setiap titik berdasarkan posisi relatif ke sumber cahaya.

Dengan ketinggian air tetap atau arah cahaya tetap, bagian dari persamaan adalah konstan dan tidak boleh dihitung dalam shader karena alasan kinerja.

Pro:

  • Cepat dan akurat

Cons:

  • Hanya berfungsi untuk permukaan air datar atau hanya untuk cahaya dari langsung di atas, karena hanya satu permukaan yang biasanya diperhitungkan. (Kombinasi permukaan kasar dan cahaya miring dapat bekerja pada beberapa perluasan dengan pemetaan paralaks.)
  • Tanpa kaustik

Pemetaan Shadow

Jika Anda merender permukaan air ke peta kedalaman yang terpisah (seperti yang terlihat dari sumber cahaya), Anda dapat menggunakan tekstur kedalaman itu untuk menghitung jarak yang ditempuh cahaya di dalam air sebelum mengenai permukaan.
Untuk melakukan ini, Anda memproyeksikan setiap simpul ke proyeksi tampilan sumber cahaya di shader vertex dan melakukan pencarian tekstur dalam pixel shader.

Jika permukaannya relatif rata, Anda harus menggunakan sumber cahaya yang dibiaskan untuk hasil yang lebih baik.

Pro:

  • Bekerja dengan geometri air yang relatif kompleks, asalkan tidak menutup sendiri. *
  • Dapat digunakan kembali untuk hampir semua jenis volume yang sebagian transparan.

Cons:

  • Lebih lambat dari perhitungan langsung.
  • Membutuhkan VRAM tambahan untuk peta kedalaman.
  • Tidak 100% akurat.

* Anda dapat menentukan jumlah air di depan permukaan padat terdekat dengan menghitung kedalaman dari POV cahaya sebagai berikut:

  1. Jadikan geometri solid dalam adegan Anda seperti biasa. Untuk setiap fragmen, Anda menambahkan nilai kedalaman ke tekstur hasil.
  2. Render permukaan muka air tanpa memperbarui buffer kedalaman dan kurangi kedalaman fragmen dari hasilnya.
  3. Jadikan wajah belakang dengan cara yang sama, tetapi tambahkan kedalaman fragmen ke hasilnya.

Tekstur hasil sekarang berisi jumlah air di depan cahaya dalam ruang tampilan cahaya, sehingga nilainya harus diubah kembali sebelum Anda menggunakannya. Metode ini berfungsi untuk menghitung cahaya arah (minus refraksi), tetapi akan menyebabkan cahaya ambient yang salah jika permukaannya sangat tidak teratur dan ada banyak udara di antara badan air yang mempengaruhi fragmen yang sama.
Pro dan kontra sama dengan pemetaan bayangan normal, kecuali bahwa Anda memerlukan satu buffer lagi saat menghitung kedalaman dan kinerjanya lebih buruk karena Anda harus menggambar lebih banyak geometri.

Ray-Tracing

Ray tracing sejauh ini adalah yang paling akurat tetapi juga solusi paling mahal untuk menghasilkan volume transparan. Ada dua cara untuk melakukan ini: 1. Menelusuri dari dasar samudera menuju permukaan dan 2. Menelusuri dari sumber cahaya menuju air. Diperlukan beberapa sinar untuk setiap titik di lantai untuk menghitung kecerahan.

Pro:

  • Bekerja dengan benar dengan setiap geometri.
  • Kaustik benar!

Cons:

  • Lambat!

Efek tambahan

Ada beberapa hal lagi yang perlu diperhatikan saat memberikan air:

Kabut

Cahaya dalam air tersebar lagi saat bepergian ke pengamat, jadi Anda harus membaurkannya ke warna solid.

Jika pengamat tenggelam , Anda bisa membuat kabut berdasarkan hasil akhir buffer kedalaman. Warna kabut, tetapi kepadatannya tidak akan berubah dengan jarak pengamat dari permukaan! (Minecraft hanya menggunakan bagian dari efek ini.)

Jika pengamat melihat air dari atas , Anda perlu menghitung kabut berdasarkan perbedaan kedalaman antara permukaan dan geometri di bawah air. Warna kabut akan menjadi sedikit lebih gelap dengan perbedaan kedalaman yang lebih besar, tetapi hanya akan berubah ke titik di mana kabut sepenuhnya buram.

Warna kabut juga harus bergantung pada arah tampilan untuk setiap piksel, sehingga sedikit lebih gelap saat melihat ke bawah dalam kedua kasus.

Caustics pemalsuan

Jika Anda menggunakan Tekstur 3D ubin mulus alih-alih decal untuk kaustik palsu, Anda dapat menghindari peregangan pada permukaan vertikal. Kekuatan cahaya yang tersebar di dekat permukaan bervariasi dalam tiga dimensi, jadi menggunakan 2D-Texture biasanya menghasilkan peregangan di suatu tempat dalam pemandangan. Anda dapat memodelkan perubahan sudut cahaya dengan memproyeksikan posisi titik lantai ke sistem koordinat yang berbeda.

Kemungkinan lain adalah untuk menghitung kerapatan cahaya berdasarkan posisi permukaan dalam sistem koordinat cahaya, meskipun itu kemungkinan besar akan membutuhkan beberapa kinerja.

Kaustik harus memudar lebih cepat dari cahaya difus dengan meningkatnya kedalaman.

Gradien warna

Warna tersebar secara berbeda, sehingga warna terang harus berubah dengan meningkatnya kedalaman. Ini juga mencegah tepi tiba-tiba di mana, misalnya, pantai memotong permukaan air.

Sudut Insidensi

Karena pembiasan, cahaya menghantam dasar lautan jauh lebih curam daripada biasanya. The Wikipedia artikel tentang hukum Snell memiliki formula untuk sudut dan vektor.


6

Saya percaya bahwa efek pencahayaan langit di Minecraft lurus ke bawah - segala sesuatunya diarsir oleh apa yang ada di atas mereka di mana pun matahari berada. Kemudian pencahayaan lokal dari obor, dll diterapkan dengan efek dropoff - semakin jauh dari sumber cahaya, semakin sedikit cahaya yang didapat dari kubus.

Jika dilakukan dengan cara ini, setiap lapisan air akan secara kumulatif membayangi lapisan di bawahnya, sehingga masing-masing menjadi semakin gelap. Dedaunan pohon memberikan keteduhan seperti ini, namun tidak kumulatif. Anda mendapatkan naungan yang sama di bawah pohon apakah itu 1 atau 100 kubus dedaunan.

Satu petunjuk bahwa ini adalah metode yang digunakan adalah bahwa air tidak menjadi lebih gelap ketika semakin jauh dari penonton - hanya saat Anda turun. Ya, efek kabut memang menendang di kejauhan, tetapi tidak efek air gelap.

Jadi rumus dasar untuk menghitung pencahayaan akan menjadi seperti ini dalam pseudo-code ...

light_on_cube = 1.0
for each cube above target cube, from lowest to highest {
   if cube being examined is tree foliage
      light_on_cube = 0.5
   else if cube being examined is water
      light_on_cube = light_on_cube - 0.1
   else if cube being examined is solid 
      light_on_cube = 0
}

Ini tidak sempurna untuk menghitung pencahayaan di bawah gantung atau di gua, karena akan gelap gulita di bawah gantung menggunakan metode ini. Tetapi orang dapat menambahkan kedua sumber cahaya lokal (obor, api, dll.) Serta memperlakukan balok yang menyala sebagai sumber cahaya. Sesuatu seperti ini mungkin melakukannya ...

  1. Hitung cahaya dari matahari langsung dari atas (melalui pseudo-code di atas) untuk setiap kubus.
  2. Jika sebuah kubus memiliki sumber cahaya di sebelahnya, anggaplah sepenuhnya menyala (1.0)
  3. Jika sebuah kubus tidak menerima matahari dari langsung di atasnya, berikan cahaya berdasarkan seberapa jauh jaraknya dari kubus yang menyala penuh. Lebih dekat berarti lebih banyak cahaya, lebih jauh berarti lebih sedikit (sampai nol).

Idenya di sini adalah bahwa jika sebuah kubus dinyalakan oleh matahari atau obor, kubus di sebelahnya juga akan dinyalakan dengan cara tertentu. Dan semakin jauh Anda dari kubus yang menyala itu, semakin sedikit cahaya yang ada. Ini semacam cara kludge untuk memperkirakan pencahayaan difus tapi saya pikir (?) Itu akan berhasil.


1
Ya, saya cukup yakin itulah tiketnya. Saya telah melakukan hal serupa di permainan saya.
MichaelHouse

Omong-omong saya baru saja menambahkan blog Anda ke daftar pembaca google saya Byte56 - blog pengembang FTW!
Tim Holt

Oh, mengapa terima kasih. Masih di luar topik pertanyaan ini, tetapi saya baru saja membaca blog Anda tentang kelas Profesor Bailey. Saya berada di kelas itu tahun lalu! Saya cukup yakin Anda memberikan presentasi itu tahun lalu juga. Saya pikir nama Anda familier. Dunia kecil :)
MichaelHouse

3

Mungkin saya salah paham pertanyaannya, tetapi mengapa Anda tidak bisa mengubah warna balok tergantung kedalamannya?

Jika Anda memiliki kedalaman d (dalam blok, mulai dari 0) maka persamaan yang masuk akal untuk kecerahan adalah:

L = (1- m ) e - kd + m

Kode: L = (1.0 - m) * exp(-k * d) + m;

k mengontrol seberapa cepat ia menjadi lebih gelap (lebih tinggi = lebih cepat). Nilai yang masuk akal adalah 0,5.
m adalah kecerahan minimum yang Anda inginkan.
L bervariasi dari 0 hingga 1.

Jika Anda tidak tahu cara mengubah warna blok di API grafik apa pun yang Anda gunakan, silakan tanyakan itu sebagai pertanyaan terpisah (nyatakan API apa yang Anda gunakan, dan apakah Anda menggunakan shader atau tidak).


Saya sama sekali tidak berpikir untuk melakukan itu. Karena penasaran, dari mana Anda mendapatkan persamaan itu?
Xavier

1
@ Xavier: Saya baru saja mengada-ada. The e^-kdbit hanya sebuah peluruhan eksponensial, yang merupakan fungsi standar untuk hal-hal yang secara bertahap cenderung menuju nol atas beberapa nilai (kedalaman). Penggandaan dengan (1-m)dan penambahan mhanya untuk skala dan mengimbangi pembusukan sehingga berakhir minimal mtetapi masih dimulai pada 1. en.wikipedia.org/wiki/Exponential_decay
Peter Alexander

Masalahnya, blok warna yang lebih dalam hanya akan terlihat jika blok memiliki warna alfa; dalam hal ini tidak perlu mengubah warna blok, alfa akan membuat efek secara otomatis.
Jonathan Connell

@ Jonathan: Anda tidak membuat blok air, Anda membuat blok di dasar laut dengan warna gelap dan kemudian hanya memiliki lapisan alfa tunggal di permukaan air.
Peter Alexander

@ Peter Alexander Ok, saya berasumsi bahwa dalam game tipe blok ini, bahkan airnya terbuat dari blok.
Jonathan Connell
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.