Penerapan efek cat air dalam game

gambar

pengantar


Ketika pada Januari 2019 kami mulai mendiskusikan permainan warna baru kami . , kami segera memutuskan bahwa efek cat air akan menjadi elemen paling penting. Terinspirasi oleh iklan Bulgari ini , kami menyadari bahwa penerapan lukisan cat air harus konsisten dengan kualitas tinggi dari sumber daya yang tersisa yang kami rencanakan untuk dibuat. Kami menemukan artikel yang menarik oleh para peneliti dari Adobe (1) . Teknik cat air yang dijelaskan di dalamnya tampak luar biasa, dan karena sifat vektornya (bukan piksel), ia dapat bekerja bahkan pada perangkat seluler yang lemah. Implementasi kami didasarkan pada studi ini, kami mengubah dan / atau menyederhanakannya karena persyaratan kinerja kami berbeda. warna .- ini adalah permainan, oleh karena itu, selain menggambar itu sendiri, kami perlu membuat seluruh lingkungan 3D dan menjalankan logika permainan dalam satu bingkai. Kami juga berusaha memastikan bahwa simulasi dilakukan secara real time dan pemain segera melihat apa yang digambar.


Simulasi warna air.

Dalam artikel ini, kami akan membagikan detail masing-masing penerapan teknik ini di mesin game Unity dan berbicara tentang bagaimana kami mengadaptasinya agar berfungsi secara mulus di perangkat seluler kelas bawah. Kami akan berbicara lebih banyak tentang tahapan utama dari algoritma ini, tetapi tanpa menunjukkan kodenya. Implementasi ini dibuat di Unity 2018.4.2 dan kemudian diperbarui ke versi 2018.4.7.

Apa itu pewarnaan?


Tint . - Ini adalah permainan puzzle yang memungkinkan pemain untuk menyelesaikan level, mencampur warna-warna cat air sehingga cocok dengan warna-warna origami. Permainan ini dirilis pada musim gugur 2019 di Apple Arcade untuk iOS, macOS, dan tvOS.


Tangkapan layar screenshot.

Persyaratan


Teknik yang dijelaskan dalam artikel saya dapat dibagi menjadi tiga tahap utama yang dilakukan di setiap frame:

  1. Hasilkan tempat baru berdasarkan input pemain dan tambahkan ke daftar tempat
  2. Simulasi cat untuk semua tempat di daftar
  3. Render tempat

Di bawah ini kita akan berbicara secara rinci tentang bagaimana kita menerapkan setiap tahapan.

Kami bertujuan untuk mencapai 60 FPS, yaitu, tahapan ini dan semua logika yang dijelaskan di bawah ini dilakukan 60 kali per detik.

Mendapatkan input


Di setiap bingkai, kami mengubah input pemain (tergantung pada platform, itu bisa berupa sentuhan, posisi mouse atau kursor virtual) menjadi struktur splatData yang berisi posisi, vektor gerakan, warna dan tekanan (2). Pertama, kami memeriksa panjang gesek pemain di layar dan membandingkannya dengan nilai ambang yang diberikan. Dengan gesekan pendek, kami menghasilkan satu titik per bingkai pada posisi input. Dalam kasus yang berlawanan, kami mengisi jarak antara titik awal dan titik akhir gesekan pemain dengan titik-titik baru yang dibuat dengan kepadatan yang telah ditentukan (ini memastikan kepadatan cat konstan terlepas dari kecepatan gesek). Warnanya menunjukkan cat saat ini digunakan, dan kemiringan gerakan menunjukkan arah gesek. Bintik-bintik baru yang dibuat ditambahkan ke koleksi yang disebut splatList, yang juga berisi semua tempat yang dibuat sebelumnya. Ini digunakan untuk mensimulasikan dan membuat cat pada langkah-langkah berikut. Setiap titik individu menunjukkan "setetes" cat yang perlu dirender - blok bangunan utama lukisan cat air. Gambar cat air jadi akan menjadi hasil rendering puluhan / ratusan tempat berpotongan. Selain itu, nilai masa pakai (dalam bingkai) ditetapkan ke tempat yang baru dibuat, yang menentukan berapa lama tempat tersebut dapat disimulasikan.


Contoh interpolasi titik gesek panjang. Lingkaran kosong menunjukkan tempat yang dibuat secara berkala.

Kanvas


Seperti cat asli, kita membutuhkan kanvas. Untuk mengimplementasikannya, kami menciptakan area terbatas dalam ruang 3D yang terlihat seperti selembar kertas. Koordinat input pemain dan semua operasi lainnya, seperti rendering mesh, direkam di ruang kanvas. Demikian pula, ukuran dalam piksel buffer apa pun yang digunakan untuk mensimulasikan gambar tergantung pada ukuran kanvas. Istilah "kanvas" seperti yang digunakan dalam artikel ini sama sekali tidak terkait dengan kelas Canvas dari Unity UI.


Kotak hijau menunjukkan area kanvas dalam permainan

Titik


Secara visual, titik diwakili oleh jala bundar, ujungnya terdiri dari 25 simpul. Anda dapat melihatnya sebagai "jatuhkan" yang ditinggalkan oleh sikat basah pada selembar kertas jika Anda menyentuhnya sebentar. Kami menambahkan offset acak kecil ke posisi masing-masing titik, yang memastikan ketidakmerataan tepi bintik-bintik cat.


Contoh jerat jaring.

Untuk setiap titik, kami juga menyimpan vektor kecepatan luar, yang kemudian digunakan dalam fase simulasi. Kami menghasilkan beberapa jerat tersebut dengan variasi kecil antara mereka membentuk dan menyimpan data mereka dalam objek skriptuemy ( objek scriptable ). Setiap kali seorang pemain menarik tempat secara real time, kami memberinya jala yang dipilih secara acak dari set ini. Perlu disebutkan bahwa pada resolusi layar yang berbeda kanvas memiliki ukuran piksel yang berbeda. Sehingga pada semua perangkat koefisien ukuran tempat sama, ketika Anda memulai permainan, kami mengubah skala sesuai dengan ukuran kanvas.


Contoh vektor tempat yang disimpan dengan data tempat baru.

Saat spot mesh dibuat, kami juga menyimpan "area pembasahan" nya, yang mendefinisikan satu set piksel yang berada di dalam batas spot asli. Area pembasahan digunakan untuk mensimulasikan adveksi . Selama eksekusi aplikasi pada saat membuat setiap tempat baru, kami menandai kanvas di bawahnya sebagai basah. Saat mensimulasikan pergerakan cat, kami mengizinkannya untuk "menyebar" ke area-area kanvas yang sudah basah. Kami menyimpan konten kelembaban kanvas dalam buffer wetmap global , yang diperbarui setiap kali tempat baru ditambahkan. Selain berpartisipasi dalam pencampuran dua warna, advection juga memainkan peran penting dalam penampilan akhir dari stroke cat itu sendiri.


Pengisian wetmap , piksel di dalam bentuk spot (lingkaran hijau) menandai buffer wetmap (kisi) sebagai basah (hijau). Buffer wetmap sendiri memiliki resolusi yang jauh lebih tinggi.

Selain itu, setiap spot juga berisi nilai opacity , yang merupakan fungsi dari area tersebut; itu mewakili efek menyimpan pigmen (jumlah pigmen yang konstan di tempat). Ketika ukuran tempat meningkat selama simulasi, opacitynya berkurang, dan sebaliknya.


Contoh cat tanpa advection (kiri) dan dengan itu (kanan).


Contoh adveksi cat.

Siklus simulasi


Setelah input pemain dalam bingkai saat ini diterima dan dikonversi ke tempat baru, langkah selanjutnya adalah mensimulasikan tempat untuk mensimulasikan penyebaran cat air. Pada awal simulasi ini, kami memiliki daftar tempat yang perlu diperbarui, dan peta basah yang diperbarui .

Dalam setiap bingkai, kita berkeliling daftar tempat dan mengubah posisi semua simpul tempat menggunakan persamaan berikut:


di mana: m adalah vektor gerak baru, a adalah parameter koreksi konstan (0.33), b adalah vektor kemiringan gerak = arah dinormalisasi dari gesekan pemain dikalikan dengan 0,3, cr adalah nilai skalar dari kekasaran kanvas = Random.Range (1,1 + r), r adalah parameter kekasaran global, untuk cat standar kita atur ke 0.4, v adalah vektor kecepatan yang dibuat terlebih dahulu dengan spot mesh, vm adalah faktor kecepatan, nilai skalar yang kita gunakan secara lokal dalam beberapa situasi untuk mempercepat adveksi, x (t + 1) - posisi vertex baru potensial, x (t) - posisi vertex saat ini, brApakah vektor kekasaran bercabang = (Random.Range (-r, r), Random.Range (-r, r)), w (x) adalah nilai pembasahan dalam buffer wetmap.

Hasil dari persamaan tersebut disebut bias random walk , ini meniru perilaku partikel dalam cat cat air nyata. Kami mencoba untuk memindahkan setiap titik titik keluar dari pusatnya ( v ), menambahkan keacakan. Kemudian arah gerakan sedikit berubah dengan arah pukulan ( b ) dan diacak lagi oleh komponen kekasaran lainnya ( br ). Kemudian posisi vertex baru ini dibandingkan dengan wetmap . Jika kanvas di posisi baru sudah basah (nilai dalam buffer wetmaplebih besar dari 0), maka kita beri titik baru posisi x (t + 1) , jika tidak kita tidak mengubah posisinya. Akibatnya, cat hanya akan menyebar di area kanvas yang sudah basah. Pada tahap terakhir, kami menghitung ulang area spot, yang digunakan dalam siklus rendering untuk mengubah opacity-nya.


Contoh skala mikro dari simulasi adveksi antara dua titik aktif cat.

Siklus Rendering - Buffer Basah


Setelah menghitung tempat, Anda dapat mulai merendernya. Pada pintu keluar setelah tahap emulasi, lubang bintik sering berubah menjadi cacat (misalnya, persimpangan terjadi), oleh karena itu, untuk rendering yang benar tanpa biaya tambahan untuk triangulasi berulang, kami menggunakan solusi dengan buffer stensil dua lintasan. Antarmuka menggambar Grafik Unity digunakan untuk membuat bintik-bintik , dan siklus rendering dilakukan di dalam metode Unity OnPostRender . Spot mesh dirender untuk membuat tekstur ( wetBuffer ) menggunakan kamera terpisah. Di awal siklus, wetBuffer dihapus dan ditetapkan sebagai target render menggunakan Graphics.SetRenderTarget (wetBuffer) . Selanjutnya untuk setiap tempat aktif dari splatList kami mengeksekusi urutan yang ditunjukkan pada diagram berikut:


Diagram siklus rendering.

Kami mulai dengan membersihkan buffer stensil sebelum setiap tempat sehingga keadaan buffer stensil dari tempat sebelumnya tidak mempengaruhi tempat baru. Lalu kami memilih bahan yang digunakan untuk menggambar tempat. Bahan ini bertanggung jawab atas warna titik, dan kami memilihnya berdasarkan indeks warna yang disimpan dalam splatData ketika pemain menggambar titik. Kemudian kita mengubah opacity warna (saluran alfa) berdasarkan area dari titik jala yang dihitung pada langkah sebelumnya. Rendering itu sendiri dilakukan dengan menggunakan shader buffer stensil dua lulus. Di pass pertama (Material.SetPass (0)) kami melewati mesh tempat asli untuk merekam koordinat di mana mesh diisi. Dengan lulus ini ColorMaskmenetapkan nilai 0, sehingga mesh itu sendiri tidak dirender. Pada pass kedua (Material.SetPass (1)) kami menggunakan segiempat yang dijelaskan di sekitar mesh spot. Kami memeriksa nilai dalam buffer stensil untuk setiap piksel segi empat; jika nilainya satu, piksel dirender, jika tidak dilewati. Sebagai hasil dari operasi ini, kami membuat bentuk yang sama dengan spot mesh, tetapi tentu saja tidak akan berisi artefak yang tidak diinginkan, misalnya, persimpangan-sendiri.


Prosedur untuk melakukan teknik buffer stensil ganda (dari kiri ke kanan). Perhatikan bahwa penyangga stensil ini memiliki resolusi yang jauh lebih tinggi daripada yang ditunjukkan, sehingga dapat mempertahankan bentuk aslinya dengan akurasi tinggi.


Contoh tiga titik berpotongan yang diberikan dengan cara tradisional, yang mengarah pada penampilan artefak (kiri), dan menggunakan teknik buffer stensil dua arah dengan menghilangkan semua artefak (kanan).

Setelah merender semua tempat di wetBuffer, ditampilkan di adegan permainan. Kanvas kami menggunakan shader darurat yang menggabungkan wetBuffer , peta kertas difus, dan peta normal kertas.


Canvas shader: hanya wetBuffer (kiri), menambahkan tekstur kertas (tengah), peta normal ditambahkan (kanan).

Permainan ini mendukung mode untuk orang-orang dengan kebutaan warna, di mana pola-pola terpisah ditumpangkan di atas cat. Untuk mencapai ini, kami mengubah bahan noda dengan menambahkan tekstur pola dengan ubin. Pola mengikuti aturan pencampuran warna permainan, misalnya, biru (bar) + kuning (lingkaran) memberi hijau (lingkaran di bar) di persimpangan. Untuk memadukan pola dengan mulus, mereka harus dirender dalam ruang UV yang sama. Kami menyesuaikan koordinat UV segi empat yang digunakan pada lintasan kedua buffer stensil, membagi posisi x dan y (yang ditentukan dalam ruang kanvas) dengan lebar dan tinggi kanvas. Akibatnya, kami mendapatkan nilai yang benar dari u, v di ruang dari 0 hingga 1.


Contoh pola buta warna.

Optimasi - buffer bintik kering


Seperti disebutkan di atas, salah satu tugas kami adalah mendukung perangkat seluler berdaya rendah. Render tempat ternyata menjadi penghambat permainan kami. Setiap tempat membutuhkan tiga panggilan draw (panggilan dua lintasan + hapus buffer stensil), dan karena garis cat berisi puluhan atau ratusan spot, jumlah panggilan draw meningkat dengan cepat dan menyebabkan penurunan frame rate. Untuk mengatasinya, kami menerapkan dua teknik optimisasi: pertama, gambar simultan dari semua titik "kering" di dryBuffer , dan kedua, percepatan lokal pengeringan titik setelah mencapai sejumlah titik aktif.

dryBufferAdalah tekstur render tambahan yang ditambahkan ke siklus rendering. Seperti disebutkan sebelumnya, setiap tempat memiliki masa pakai (dalam bingkai), yang berkurang dengan masing-masing bingkai. Setelah umur mencapai 0, noda dianggap "mengering". Bintik-bintik kering tidak lagi disimulasikan, bentuknya tidak berubah, dan karenanya tidak perlu dirender lagi di setiap bingkai.


DryBuffer beraksi; bintik-bintik abu-abu menunjukkan bintik-bintik yang disalin ke dryBuffer.

Setiap tempat yang masa pakainya mencapai 0 dihapus dari splatList dan "disalin" ke dryBuffer . Selama proses penyalinan, siklus rendering digunakan kembali, dan kali ini dryBuffer ditetapkan sebagai target membuat tekstur .

Pencampuran yang tepat antara wetBuffer dan dryBuffer tidak dapat dicapai hanya dengan menumpuk buffer di kanvas shader, karena tekstur render buffer wetBufferberisi bintik-bintik yang sudah dirender dengan nilai alfa (yang setara dengan alfa prapultiplied). Kami mengelak dari masalah ini dengan menambahkan satu langkah ke awal siklus rendering sebelum secara berulang melintasi titik-titik tersebut. Pada titik ini, kami membuat segi empat ukuran piramida pemangkasan kamera yang menampilkan dryBuffer . Berkat ini, setiap noda yang dihasilkan dalam wetBuffer sudah akan dicampur dengan noda kering yang sebelumnya dicat.


Campuran bintik basah dan kering.

Buffer dryBuffer mengakumulasi semua "kering" tempat dan tidak dibersihkan antara frame. Oleh karena itu, semua memori yang terkait dengan noda kedaluwarsa dapat dihapus setelah "disalin" ke buffer.


Berkat optimalisasi dengan dryBuffer , kami tidak lagi memiliki batasan pada jumlah cat yang dapat diterapkan pemain ke kanvas.

Menggunakan teknik dryBuffer secara terpisah memungkinkan pemain untuk menggambar dengan jumlah cat yang hampir tak terbatas, tetapi tidak menjamin kinerja yang konsisten. Seperti disebutkan di atas, goresan cat memiliki ketebalan konstan , yang dicapai dengan menggambar menggunakan interpolasi dari banyak titik antara titik awal dan titik akhir gesekan. Dalam kasus banyak gesekan cepat dan panjang, pemain dapat menghasilkan sejumlah besar tempat aktif. Bintik-bintik ini akan disimulasikan dan ditampilkan pada jumlah bingkai yang ditentukan oleh masa pakainya, yang pada akhirnya mengarah pada laju bingkai yang lebih rendah.

Untuk memastikan frame rate yang stabil, kami mengubah algoritme sehingga jumlah tempat aktif dibatasi oleh nilai konstan maxActiveSplats . Semua tempat yang melebihi nilai ini langsung “mengering”. Ini diwujudkan dengan mengurangi rentang hidup dari titik aktif tertua ke 0, itulah sebabnya mereka disalin ke buffer bintik kering lebih awal. Karena ketika kita mempersingkat masa pakai, kita mendapatkan tempat dalam kondisi simulasi yang tidak lengkap (yang akan terlihat cukup menarik), pada saat yang sama kita meningkatkan kecepatan penyebaran cat. Karena peningkatan kecepatan, titik mencapai ukuran yang hampir sama dengan kecepatan normal dengan umur standar.


Peragaan maksimal 40 titik aktif (atas) dan 80 (bawah). Bintik-bintik kering yang disalin dalam dryBuffer ditampilkan dalam warna abu-abu. Nilai menunjukkan "jumlah" cat yang dapat disimulasikan pada saat yang sama.

Nilai maxActiveSplats adalah parameter kinerja yang paling penting, memungkinkan kita untuk secara tepat mengontrol jumlah panggilan undian yang dapat kita alokasikan untuk rendering cat air. Kami mengaturnya saat startup, berdasarkan pada platform dan kekuatan perangkat. Anda juga dapat mengubah nilai ini selama eksekusi aplikasi jika penurunan dalam frame rate terdeteksi.

Kesimpulan


Penerapan algoritma ini telah menjadi tugas yang menarik dan menantang. Kami berharap para pembaca menikmati artikel ini. Anda dapat mengajukan pertanyaan di komentar ke aslinya . Jika Anda ingin menghargai cat air kami dalam aksi, maka cobalah bermain warna. di Apple Arcade .


Cuplikan layar game yang berjalan di Apple TV

(1) S. DiVerdi, A. Krishnaswamy, R. MÄch dan D. Ito, "Lukisan dengan Poligon: Mesin Cat Air Prosedural," dalam Transaksi IEEE pada Visualisasi dan Grafik Komputer, vol. 19, tidak. 5, hlm. 723-735, Mei 2013. doi: 10.1109 / TVCG.2012.295

(2) Tekanan hanya diperhitungkan saat menggambar Pensil Apple pada iPad.

All Articles