Artikel ini terlalu banyak air.

โ€œKami mulai mengembangkan game baru, dan kami membutuhkan air dingin. Bisakah kamu melakukan ini? โ€


- tanya saya. "Ya, tidak ada pertanyaan! Tentu saja saya bisa, โ€jawab saya, tetapi suara saya gemetar. โ€œDan juga tentang Persatuan?โ€ - dan menjadi jelas bagi saya bahwa ada banyak pekerjaan di depan.

Jadi, air. Sampai saat itu saya belum melihat Unity, persis seperti C #, jadi saya memutuskan untuk membuat prototipe pada alat yang saya tahu: C ++ dan DX9. Apa yang saya tahu dan sanggup praktikkan pada saat itu adalah tekstur bergulir normal untuk membentuk permukaan, dan pemetaan perpindahan primitif berdasarkan pada mereka. Segera perlu mengubah segalanya. Bentuk animasi realistis dari permukaan air. Naungan rumit (sangat). Generasi busa. Sistem LOD terpasang ke kamera. Saya mulai mencari informasi di Internet bagaimana melakukan semua ini.

Poin pertama, tentu saja, adalah pemahaman tentang Simulasi Air Laut Jerry Tessendorf .

Pager akademis dengan banyak formula yang tidak masuk akal tidak pernah diberikan kepada saya banyak, jadi setelah beberapa bacaan saya sedikit mengerti. Prinsip-prinsip umum jelas: setiap frame dihasilkan oleh peta ketinggian menggunakan Fast Fourier Transform, yang, sebagai fungsi waktu, dengan lancar mengubah bentuknya untuk membentuk permukaan air yang realistis. Tapi bagaimana dan apa yang harus dihitung, saya tidak tahu. Saya perlahan-lahan mempelajari kebijaksanaan menghitung FFT pada shader di D3D9, dan kode sumber dengan artikel di suatu tempat di belantara Internet, yang saya coba temukan selama satu jam, tetapi tidak berhasil (sayangnya), benar-benar membantu saya. Hasil pertama diperoleh (menakutkan sebagai perang nuklir):


Memulai kesuksesan dengan senang hati, dan transfer air ke Unity dimulai dengan penyelesaiannya.

Beberapa persyaratan diajukan untuk pertandingan dalam pertempuran laut:

  • Tampilan realistis. Cantik sebagai foreshortenings dekat dan jauh, busa dinamis, pencar, dll.
  • Dukungan untuk berbagai kondisi cuaca: kondisi tenang, badai, dan menengah. Ubah waktu hari.
  • Fisika daya apung kapal pada permukaan yang disimulasikan, benda mengambang.
  • Karena gim ini multipemain, airnya harus sama untuk semua peserta dalam pertempuran.
  • Permukaan gambar: bidang yang ditarik dari penerbangan inti voli, busa dari masuknya inti ke dalam air.

Geometri


Diputuskan untuk membangun struktur seperti quadtree, dengan pusat di sekitar kamera, yang dibangun kembali secara terpisah ketika pengamat bergerak. Mengapa diskrit? Jika Anda memindahkan mesh dengan lancar dengan kamera atau menggunakan proyeksi ruang layar seperti pada artikel Real-time water rendering - memperkenalkan konsep grid yang diproyeksikan , maka dalam rencana jangka panjang, karena resolusi geometri mesh yang tidak mencukupi, poligon akan โ€œmelompatโ€ ke atas dan bawah. Ini sangat mengejutkan. Gambarnya beriak. Untuk mengatasinya, seseorang harus sangat meningkatkan resolusi poligon kasa air, atau "meratakan" geometri jarak jauh, atau membangun dan memindahkan poligon sehingga perubahan ini tidak terlihat. Air kami progresif (hehe) dan saya memilih jalan ketiga. Seperti dalam teknik serupa (terutama yang akrab bagi semua orang yang menciptakan medan dalam permainan), Anda harus menyingkirkan persimpangan-T di perbatasan transisi tingkat detail. Untuk mengatasi masalah ini, pada awalnya 3 jenis paha depan dengan parameter tessellation yang diberikan dihitung:



Jenis pertama adalah untuk paha depan yang tidak transisi ke detail yang lebih rendah. Tidak ada pihak yang memiliki jumlah simpul dikurangi 2 kali lipat. Tipe kedua adalah untuk batas, tetapi bukan paha depan sudut. Tipe ketiga adalah paha batas sudut. Water mesh terakhir dibangun dengan memutar dan menskalakan ketiga jenis jerat ini.

Ini adalah bagaimana penampilan dengan warna yang berbeda dari level air LOD terlihat.


Bingkai pertama menunjukkan hubungan dua tingkat detail yang berbeda.

Video sebagai bingkai diisi dengan paha air:


Biarkan saya mengingatkan Anda bahwa itu sudah lama sekali (dan tidak benar). Sekarang lebih optimal dan fleksibel dapat dilakukan langsung pada GPU (GPU Pro 5. Quadtrees pada GPU). Dan itu akan menarik dalam satu panggilan undian, dan tessellation dapat meningkatkan detail.

Kemudian, proyek pindah ke D3D11, tetapi tangan tidak mencapai peningkatan bagian ini membuat laut.

Generasi bentuk gelombang


Untuk ini kita perlu Fast Fourier Transform. Untuk resolusi tekstur gelombang yang dipilih (anggap saja untuk saat ini, saya akan menjelaskan data apa yang disimpan di sana), kami menyiapkan data awal menggunakan parameter yang ditetapkan oleh para seniman (kekuatan, arah angin, ketergantungan gelombang pada arah angin dan lainnya). Semua ini harus dimasukkan ke dalam formula yang disebut. Spektrum Phillips Kami memodifikasi data awal yang diperoleh untuk setiap frame dengan mempertimbangkan waktu dan melakukan FFT pada mereka. Pada output, kami mendapatkan tekstur ubin di semua arah yang berisi offset simpul datar. Mengapa bukan sekadar peta ketinggian? Jika Anda hanya menyimpan tinggi offset, maka hasilnya akan menjadi massa "gelembung" yang tidak realistis, yang hanya menyerupai laut:


Jika kita mempertimbangkan perpindahan untuk ketiga koordinat, maka gelombang realistis "tajam" yang indah akan dihasilkan:


Satu tekstur animasi tidak cukup. Ubin terlihat, tidak cukup detail dalam waktu dekat. Kami mengambil algoritma yang dijelaskan dan membuat bukan hanya satu, melainkan 3 tekstur yang dihasilkan fft. Yang pertama adalah ombak besar. Ini mengatur bentuk gelombang dasar dan digunakan untuk fisika. Yang kedua adalah gelombang sedang. Dan akhirnya, yang terkecil. 3 generator FFT (opsi ke-4 adalah campuran terakhir):


Parameter lapisan diatur secara independen satu sama lain, dan tekstur yang dihasilkan dicampur dalam shader air ke dalam bentuk gelombang akhir. Sejalan dengan offset, peta normal setiap lapisan juga dihasilkan.

"Keseragaman" air di semua peserta pertempuran dipastikan dengan sinkronisasi parameter laut di awal pertempuran. Informasi ini dikirimkan oleh server ke setiap klien.

Model daya apung fisik


Karena itu perlu untuk membuat tidak hanya gambar yang indah, tetapi juga perilaku kapal yang realistis. Dan dengan mempertimbangkan fakta bahwa lautan badai (ombak besar) harus ada dalam permainan, tugas lain yang perlu diselesaikan adalah memastikan daya apung benda-benda di permukaan laut yang dihasilkan. Pertama saya mencoba membuat GPU membaca kembali tekstur gelombang. Tetapi, karena dengan cepat menjadi jelas bahwa semua fisika pertempuran laut harus dilakukan di server, laut, atau lebih tepatnya lapisan pertama, yang menetapkan bentuk gelombang, juga harus dibaca di server (dan, kemungkinan besar, tidak ada yang cepat dan / atau kompatibel dengan GPU), diputuskan untuk menulis salinan fungsional lengkap generator FFT GPU pada CPU dalam bentuk plug-in C ++ asli untuk Unity. Saya tidak mengimplementasikan algoritma FFT sendiri dan menggunakannya di perpustakaan Intel Performance Primitives (IPP). Tetapi semua pengikatan dan postprocessing dari hasil dilakukan oleh saya, diikuti oleh optimasi pada SSE dan paralelisasi oleh utas. Ini termasuk persiapan array data untuk FFT setiap frame, dan konversi akhir dari nilai yang dihitung ke peta offset gelombang.

Ada fitur lain yang menarik dari algoritma, yang didasarkan pada persyaratan untuk fisika air. Yang dibutuhkan adalah fungsi untuk mendapatkan ketinggian gelombang dengan cepat pada titik tertentu di dunia. Ini logis, karena ini adalah dasar untuk membangun daya apung dari objek apa pun. Tetapi, karena pada output prosesor FFT kita mendapatkan offsetmap, bukan heightmap, pilihan biasa dari tekstur tidak memberi kita ketinggian gelombang jika diperlukan. Untuk kesederhanaan, pertimbangkan opsi 2D:



Untuk membentuk gelombang, texels (elemen tekstur yang ditunjukkan oleh garis-garis vertikal) berisi vektor (panah) yang mengatur offset titik puncak flat mesh (titik biru) ke arah posisi akhir (ujung panah). Misalkan kita mengambil data ini dan mencoba mengekstraksi ketinggian air pada titik yang menarik bagi kita. Sebagai contoh, kita perlu mengetahui ketinggian di hB. Jika kita mengambil vektor texel tB, maka kita mendapatkan offset ke titik dekat hC, yang bisa sangat berbeda dari yang kita butuhkan. Ada dua opsi untuk menyelesaikan masalah ini: pada setiap permintaan tinggi, periksa set texels yang berdekatan sampai kami menemukan satu yang memiliki offset ke posisi yang menarik bagi kami. Dalam contoh kami, kami menemukan texel tA berisi offset terdekat. Tetapi pendekatan ini tidak bisa disebut cepat. Memindai radius texel tidak jelas ukuran apa (dan apakah laut badai atau tenang, pemindahan bisa sangat bervariasi) dapat memakan waktu lama.

Opsi kedua - setelah menghitung peta ofset, mengonversinya menjadi peta ketinggian menggunakan pendekatan hamburan. Ini berarti bahwa untuk setiap vektor offset, kami menulis ketinggian gelombang yang ditetapkannya ke titik di mana ia digeser. Ini akan menjadi array data yang terpisah, yang akan digunakan untuk mendapatkan ketinggian di tempat tujuan. Menggunakan ilustrasi kami, sel tB akan berisi tinggi hB yang diperoleh dari vektor tA โ†’ hB. Ada satu fitur lagi. Sel tA tidak akan berisi nilai yang valid, karena tidak ada vektor yang bergerak ke dalamnya. Untuk mengisi "lubang" seperti itu, sebuah bagian dibuat untuk mengisinya dengan nilai-nilai tetangga.

Ini adalah tampilannya jika Anda membuat visualisasi perpindahan menggunakan vektor (offset merah-besar, hijau-kecil):


Sisanya sederhana. Pesawat dari garis air bersyarat diatur untuk kapal. Di atasnya, grid persegi titik sampel ditentukan, yang mendefinisikan tempat penerapan kekuatan mendorong keluar dari air untuk kapal. Kemudian untuk setiap titik kami memeriksa apakah itu di bawah air atau tidak menggunakan peta ketinggian air yang dijelaskan di atas. Jika titik berada di bawah air, maka berikan gaya vertikal ke lambung fisika benda pada titik ini, diskalakan dengan jarak dari titik ke permukaan air. Jika di atas air, maka kita tidak melakukan apa-apa, gravitasi akan melakukan segalanya untuk kita. Faktanya, formula di sana sedikit lebih rumit (semua untuk penyempurnaan perilaku kapal), tetapi prinsip dasarnya adalah ini. Dalam video visualisasi daya apung di bawah ini, kubus biru adalah lokasi sampel, dan garis-garis di bawahnya adalah besarnya gaya yang mendorong keluar dari air.


Dalam implementasi server ada titik optimasi lain yang menarik. Tidak perlu mensimulasikan air yang berbeda untuk instance pertempuran yang berbeda jika mereka lulus dalam kondisi cuaca yang sama (parameter simulator FFT yang sama). Jadi keputusan logisnya adalah membuat kumpulan simulator, yang unit tempur memenuhi permintaan untuk mendapatkan air simulasi dengan parameter yang diberikan. Jika parameternya sama dari beberapa kejadian, maka air yang sama akan kembali ke mereka. Ini diimplementasikan menggunakan Memor Mapped File API. Ketika simulator FFT dibuat, itu memberikan akses ke datanya dengan mengekspor deskriptor dari blok yang diperlukan. Contoh server, bukannya meluncurkan simulator nyata, meluncurkan "boneka" yang hanya memberikan data yang dibuka oleh deskriptor ini. Ada beberapa bug lucu yang terkait dengan fungsi ini. Karena kesalahan penghitungan referensi, simulator dihancurkan, tetapi file yang dipetakan memori masih hidup sementara setidaknya satu pegangan untuk itu terbuka. Data berhenti diperbarui (tidak ada simulator) dan air "berhenti".

Di sisi klien, kami membutuhkan informasi tentang bentuk gelombang untuk menghitung penetrasi inti ke dalam gelombang dan memainkan sistem partikel dan busa. Kerusakan dihitung pada server dan di sana juga perlu untuk menentukan dengan benar apakah inti telah masuk ke air (gelombang dapat menutup kapal, terutama dalam badai). Di sini sudah diperlukan untuk melakukan pelacakan peta ketinggian dengan analogi seperti yang dilakukan dalam pemetaan paralaks atau efek SSAO.

Shading


Pada prinsipnya, seperti di tempat lain. Refleksi, refraksi, hamburan bawah permukaan secara licik diremas, dengan mempertimbangkan kedalaman bagian bawah, kami memperhitungkan efek fresnel, kami mempertimbangkan specular. Kami mempertimbangkan hamburan untuk punggungan tergantung pada posisi matahari. Busa dihasilkan sebagai berikut: buat "titik busa" pada puncak gelombang (gunakan ketinggian sebagai metrik), lalu terapkan bintik yang baru dibuat ke bintik-bintik dari bingkai sebelumnya sambil mengurangi intensitasnya. Dengan demikian, kami memperoleh noda bintik busa dalam bentuk ekor dari lambang gelombang berjalan.


Kami menggunakan tekstur "bintik-bintik" yang diperoleh sebagai topeng yang kami gunakan untuk mencampur tekstur gelembung, noda, dll. Kami mendapatkan pola busa dinamis yang cukup realistis di permukaan gelombang. Topeng ini dibuat untuk setiap lapisan FFT (saya ingatkan Anda, kami memiliki 3 dari mereka), dan dalam campuran terakhir mereka semua bercampur.

Video di atas memvisualisasikan topeng busa. Lapisan pertama dan kedua. Saya memodifikasi parameter generator dan hasilnya terlihat pada tekstur.

Dan sebuah video dari laut badai yang agak kikuk. Di sini Anda dapat melihat dengan jelas bentuk gelombang, kemampuan generator, dan busa:


Gambar air


Gambar penggunaan:



Digunakan untuk:

  • Penanda, visualisasi zona ekspansi inti.
  • Gambar busa pada titik di mana inti menabrak air.
  • Bangun kapal berbusa
  • Memeras air di bawah kapal untuk menghilangkan efek ombak yang membanjiri geladak dan palka yang banjir.

Kasus dasar yang jelas adalah texturing proyektif. Itu diimplementasikan. Tetapi ada persyaratan tambahan. Spesies terdekat - sabun karena resolusi yang tidak mencukupi (Anda dapat meningkatkan, tetapi tidak tanpa batas), dan saya ingin gambar proyeksi di atas air ini menjadi jauh terlihat. Di mana masalah yang sama diselesaikan? Itu benar, dalam bayangan (bayangan peta). Bagaimana dia dipecahkan di sana? Kanan, Cascaded (Parallel Split) Shadow Map. Kami juga akan menggunakan teknologi ini dan menerapkannya pada tugas kami. Kami memecah frustum kamera menjadi subfrust N (3-4 biasanya). Untuk masing-masing, kami membuat persegi panjang yang menggambarkan di bidang horizontal. Untuk setiap persegi panjang seperti itu, kami membuat matriks proyeksi ortografis dan menggambar semua objek yang menarik untuk masing-masing kamera ortho tersebut. Setiap kamera tersebut menarik ke dalam tekstur yang terpisah, dan kemudian, di shader lautan, kami menggabungkan mereka menjadi satu gambar proyeksi yang solid.


Jadi saya meletakkan pesawat besar dengan tekstur bendera di laut:



Inilah yang mengandung splits:



Selain gambar-gambar yang biasa, perlu untuk menggambar topeng busa tambahan (untuk jejak kapal dan tempat-tempat di mana inti menabrak) dengan cara yang persis sama, serta topeng untuk memeras air di bawah kapal. Ini banyak kamera dan banyak gang. Pada awalnya itu bekerja begitu mengerem, tetapi kemudian, setelah beralih ke D3D11, menggunakan "propagasi" geometri dalam shader geometris dan menggambar setiap salinan menjadi target render terpisah melalui SV_RenderTergetArrayIndex, sangat mungkin untuk mempercepat efek ini.

Perbaikan dan peningkatan


D3D11 adalah tangan yang sangat bebas dalam banyak momen. Setelah beralih ke sana dan Unity 5, saya membuat generator FFT pada compute shaders. Secara visual, tidak ada yang berubah, tetapi menjadi sedikit lebih cepat. Terjemahan kesalahan perhitungan tekstur refleksi dari render kamera lengkap yang terpisah untuk teknologi Screen Space Planar Reflections memberikan dorongan yang baik dalam kinerja. Saya menulis tentang optimalisasi objek permukaan air di atas, tetapi tangan saya tidak mencapai transfer jala ke Quadtree GPU.

Banyak yang bisa dilakukan dengan lebih optimal dan sederhana. Misalnya, jangan pagar kebun dengan simulator CPU, tetapi cukup jalankan opsi GPU pada server dengan perangkat d3d WARP (perangkat lunak). Array data di sana tidak terlalu besar.

Nah, secara umum, entah bagaimana. Pada saat pembangunan dimulai, semuanya modern dan keren. Sekarang sudah menjadi tua di beberapa tempat. Ada lebih banyak bahan yang tersedia, bahkan ada analog yang mirip dengan github: Crest . Sebagian besar game yang memiliki lautan menggunakan pendekatan yang serupa.

Source: https://habr.com/ru/post/id451604/


All Articles