Dalam tutorial ini, saya akan berbicara tentang cara membuat ulang efek doodle sprite populer di Unity menggunakan shader. Jika gaya Anda memerlukan gaya ini, maka dari artikel ini Anda akan belajar cara mencapainya tanpa membuat banyak gambar tambahan.
Selama beberapa tahun terakhir, gaya ini telah menjadi semakin populer dan secara aktif digunakan dalam permainan seperti
GoNNER dan
Baba adalah Anda .
Tutorial ini mencakup semua yang Anda butuhkan, dari dasar-dasar pengkodean shader hingga matematika yang digunakan. Di akhir artikel ada tautan untuk mengunduh paket Unity lengkap.
Keberhasilan
Doodle Studio 95 menginspirasi saya untuk membuat tutorial ini
! .
Pendahuluan
Di blog saya, saya mengeksplorasi topik yang cukup kompleks, dari
matematika kinematika terbalik hingga
hamburan Rayleigh atmosfer . Saya benar-benar ingin membuat topik yang sulit dimengerti oleh khalayak luas. Tetapi jumlah orang yang tertarik pada mereka dan memiliki tingkat teknis yang memadai tidak begitu besar. Karena itu, Anda tidak perlu heran bahwa artikel paling populer adalah yang paling sederhana. Ini juga berlaku untuk
tweet Nick Caman baru-baru ini di mana ia menunjukkan cara membuat
efek doodle di Unity.
Setelah 1000 suka dan 4000 retweet, menjadi jelas bahwa ada permintaan kuat untuk tutorial sederhana yang dapat dipelajari bahkan oleh orang-orang yang hampir tidak memiliki pengetahuan membuat shader.
Jika Anda mencari cara profesional dan efektif untuk menghidupkan sprite 2D dengan kontrol artistik tingkat tinggi, maka saya sangat merekomendasikan
Doodle Studio 95! (lihat GIF di bawah).
Di sini Anda dapat melihat beberapa game yang menggunakan alat ini.
Anatomi Efek Doodle
Untuk membuat ulang efek doodle, pertama-tama kita perlu memahami cara kerjanya dan teknik apa yang digunakan di dalamnya.
Efek shader. Pertama, kami ingin efek ini sesederhana mungkin dan tidak memerlukan skrip tambahan. Ini dimungkinkan berkat penggunaan
shader yang memberi tahu Unity cara merender model 3D (termasuk yang datar!) Di layar. Jika Anda tidak terbiasa dengan dunia
pengkodean shader , maka Anda harus mempelajari artikel saya
Pengantar Lembut untuk Shader .
Sprite shader. Unity hadir dengan banyak jenis shader. Jika Anda menggunakan alat Unity 2D yang disediakan, maka kemungkinan besar Anda bekerja dengan
sprite . Jika demikian, maka Anda memerlukan
Sprite Shader - jenis khusus shader yang kompatibel dengan
SpriteRenderer
Unity. Atau Anda dapat mulai dengan
shader Unlit yang lebih tradisional.
Offset verteks. Saat menggambar sprite secara manual, tidak ada bingkai yang sepenuhnya bertepatan dengan yang lainnya. Kami ingin membuat sprite "goyah" dalam beberapa cara untuk mensimulasikan efek ini. Shader memiliki cara yang sangat efektif untuk melakukan ini, menggunakan
vertex offset . Ini adalah teknik yang memungkinkan Anda untuk mengubah posisi simpul dari objek 3D. Jika kita memindahkannya secara acak, kita mendapatkan efek yang diinginkan.
Waktu perjalanan Animasi tangan bebas biasanya memiliki frame rate yang rendah. Jika kita ingin mensimulasikan, katakanlah, lima frame per detik, maka kita perlu mengubah posisi simpul sprite lima kali per detik. Namun, Unity cenderung menjalankan game dengan kecepatan refresh yang jauh lebih tinggi; mungkin dengan 30 atau bahkan 60 frame per detik. Agar sprite tidak berubah 60 kali per detik, Anda perlu mengerjakan komponen pewaktu animasi.
Langkah 1: menyelesaikan sprite shader
Jika Anda ingin membuat shader baru di Unity, maka pilihannya akan sangat terbatas. Shader terdekat yang dapat kita mulai adalah
Unlit Shader , meskipun belum tentu paling cocok untuk tujuan kita.
Jika Anda ingin doodle shader sepenuhnya kompatibel dengan
SpriteRenderer
Unity, maka kita perlu melengkapi
Sprite Shader yang ada . Sayangnya, kami tidak dapat mengaksesnya langsung dari Unity itu sendiri.
Anda dapat melakukannya dengan membuka halaman
arsip unduhan Unity dan mengunduh paket
Build in shaders untuk versi Unity yang sedang Anda tangani. Ini adalah file zip yang berisi kode sumber dari semua shader yang datang bersama build Unity Anda.
Setelah mengunduh, unzip dan temukan file
Sprites-Diffuse.shader
di
builtin_shaders-2018.1.6f1\DefaultResourcesExtra
Sprites-Diffuse.shader
. Ini adalah file yang akan kita gunakan dalam tutorial.
Sprite-Diffuse bukan sprite shader standar!Saat membuat sprite baru, materi standarnya menggunakan shader yang disebut Sprites-Default.shader
, bukan Sprites-Diffuse.shader
.
Perbedaan antara keduanya adalah bahwa yang pertama tidak menggunakan pencahayaan, dan yang kedua merespons pencahayaan di tempat kejadian. Karena sifat implementasi Unity, versi difus jauh lebih mudah untuk diedit daripada versi tanpa pencahayaan.
Di akhir tutorial ini ada tautan untuk mengunduh doodle shader dengan dan tanpa penerangan.
Langkah 2: offset simpul
Di dalam
Sprites-Diffuse.shader
ada fungsi yang disebut
vert
, yang merupakan
fungsi simpul yang kita bicarakan di atas. Namanya tidak penting, hal utama adalah bahwa itu bertepatan dengan yang ditentukan di bagian
vertex:
dari
#pragma
:
#pragma surface surf Lambert vertex:vert nofog nolightmap nodynlightmap keepalpha noinstancing
Singkatnya, fungsi simpul dipanggil untuk setiap simpul model 3D dan memutuskan bagaimana menempatkannya pada ruang layar dua dimensi. Dalam tutorial ini, kami hanya tertarik pada cara memindahkan objek.
Parameter
appdata_full v
berisi bidang
vertex
, yang berisi posisi 3D dari setiap titik dalam
ruang objek . Saat nilainya berubah, verteks bergeser. Misalnya, kode yang ditunjukkan di bawah ini akan mentransfer objek dengan shadernya satu unit di sepanjang sumbu X.
void vert (inout appdata_full v, out Input o) { v.vertex = UnityFlipSprite(v.vertex, _Flip); v.vertex.x += 1; #if defined(PIXELSNAP_ON) v.vertex = UnityPixelSnap (v.vertex); #endif UNITY_INITIALIZE_OUTPUT(Input, o); o.color = v.color * _Color * _RendererColor; }
Secara default, game 2D yang dibuat di Unity hanya beroperasi dengan sumbu X dan Y, jadi kita perlu mengubah
v.vertex.xy
untuk memindahkan sprite pada bidang dua dimensi.
Apa itu ruang objek?Bidang vertex
dari struktur appdata_full
berisi posisi titik saat ini diproses oleh shader di ruang objek. Ini adalah posisi simpul dengan asumsi bahwa objek tersebut terletak di pusat dunia (0,0,0), tanpa mengubah skala dan tanpa rotasi.
Puncak yang diungkapkan dalam ruang dunia mencerminkan posisi mereka yang sebenarnya dalam kancah Persatuan.
Mengapa objek tidak bergerak dengan kecepatan satu meter per frame?Jika kita menambahkan +1 ke komponen x
dari nilai transform.position
dalam metode Update
skrip C #, kita akan melihat bagaimana objek terbang ke kanan dengan kecepatan 1 meter per bingkai, yaitu sekitar 216 kilometer per jam.
Ini karena perubahan yang dilakukan C # mengubah posisi itu sendiri. Dalam fungsi vertex, ini tidak terjadi. Shader hanya mengubah representasi visual dari model, tetapi tidak memperbarui atau memodifikasi simpul yang tersimpan dari model. Itulah mengapa menambahkan +1 ke v.vertex.x
menggeser objek hanya satu meter saja.
Ingatlah untuk mengimpor sprite sebagai Ketat!Efek ini menggeser bagian atas sprite. Secara tradisional, sprite diimpor ke Unity sebagai quadrangles (lihat gambar di sebelah kiri). Ini berarti mereka hanya memiliki empat puncak. Jika demikian, maka hanya titik-titik ini yang dapat dipindahkan, yang mengurangi kekuatan efek orat-oret.
Untuk distorsi yang lebih kuat dan lebih realistis, Anda perlu mengimpor sprite dengan memilih
Tight untuk parameter
Mesh Type , yang mengubahnya menjadi bentuk cembung (lihat gambar di sebelah kanan).
Ini meningkatkan jumlah simpul. Ini tidak selalu diinginkan, tetapi itulah yang kita butuhkan sekarang.
Offset acak
Efek doodle secara acak menggeser posisi setiap dhuwur. Mengambil sampel
angka acak dalam shader selalu menjadi tugas yang sulit. Ini terutama disebabkan oleh arsitektur GPU terdistribusi, yang merumitkan dan mengurangi efisiensi pembuatan algoritma yang digunakan di sebagian besar perpustakaan (termasuk
Mathf.Random
).
Posting Nick Caman menggunakan tekstur suara yang, ketika disampel, memberikan ilusi keacakan. Dalam konteks proyek Anda, ini mungkin bukan pendekatan yang paling efektif, karena dalam hal ini jumlah operasi pencarian tekstur yang dilakukan oleh shader berlipat ganda.
Oleh karena itu, di sebagian besar bayangan, fungsi yang agak membingungkan dan kacau digunakan, yang, terlepas dari determinisme mereka, bagi kita tampaknya tidak memiliki keteraturan. Dan karena mereka harus didistribusikan, setiap angka acak harus dihasilkan dengan benihnya sendiri. Ini bagus untuk kita, karena posisi setiap titik harus unik. Kita dapat menggunakan ini untuk mengikat nomor acak ke setiap titik. Kami akan membahas implementasi fungsi acak ini nanti; sebut saja
random3
.
Kita dapat menggunakan
random3
untuk menghasilkan offset acak dari setiap titik. Dalam contoh di bawah ini, angka acak diskalakan menggunakan properti
_NoiseScale
, yang memungkinkan Anda untuk mengontrol kekuatan perpindahan.
void vert (inout appdata_full v, out Input o) { ... float2 noise = random3(v.vertex.xyz).xy * _NoiseScale; v.vertex.xy += noise; ... }
Sekarang kita perlu menulis kode
random3
.
Keacakan dalam shader
Salah satu fungsi pseudorandom ikonik yang paling umum dan digunakan dalam shader diambil dari artikel 1998 oleh W. Ray dengan judul "
Pada menghasilkan angka acak, dengan bantuan y = [(a + x) sin (bx)] mod 1 ".
float rand(float2 co) { return fract(sin(dot(co.xy ,float2(12.9898,78.233))) * 43758.5453); }
Fungsi ini bersifat deterministik (artinya, itu tidak
benar -
benar acak), tetapi berperilaku begitu acak sehingga terlihat benar-benar acak. Fungsi seperti ini disebut
pseudo-random . Untuk tutorial saya, saya memilih fungsi yang lebih kompleks, yang ditemukan oleh
Nikita Miropolsky.Menghasilkan angka acak semu dalam shader adalah topik yang sangat kompleks. Jika Anda tertarik untuk mempelajari lebih lanjut tentang dia, maka
The Book of Shaders memiliki bab yang bagus tentangnya. Selain itu,
Patricio Gonzales Vivo telah membangun repositori besar fungsi pseudo-acak yang disebut
noise GLSL , yang dapat digunakan dalam shader.
Langkah 3: tambahkan waktu
Berkat kode yang kami tulis, setiap titik di setiap bingkai digeser dengan jumlah yang sama. Jadi kami mendapatkan sprite terdistorsi, bukan efek doodle. Untuk memperbaikinya, Anda perlu menemukan cara untuk mengubah efek dari waktu ke waktu. Salah satu cara termudah untuk melakukan ini adalah dengan menggunakan posisi titik dan waktu saat ini untuk menghasilkan angka acak.
Dalam kasus kami, saya baru saja menambahkan waktu saat ini dalam detik
_Time.y
ke posisi titik.
float time = float3(_Time.y, 0, 0); float2 noise = random3(v.vertex.xyz + time).xy * _NoiseScale; v.vertex.xy += noise;
Efek yang lebih kompleks mungkin memerlukan cara yang lebih canggih untuk menambah waktu pada persamaan. Tetapi karena kita hanya tertarik pada efek acak intermiten, maka dua nilai lebih dari cukup.
Pergantian waktu
Masalah utama dengan menambahkan
_Time.y
adalah itu menyebabkan sprite dianimasikan di setiap frame. Ini tidak diinginkan bagi kami, karena sebagian besar animasi yang digambar tangan memiliki frame rate yang rendah. Komponen waktu tidak boleh kontinu, tetapi diskrit. Ini berarti bahwa jika kita ingin menampilkan lima frame per detik, maka itu harus berubah hanya lima kali per detik. Artinya, waktu harus
dikaitkan dengan seperlima detik. Satu-satunya nilai yang valid seharusnya
,
,
,
,
,
dengan, dan seterusnya ...
Saya sudah membahas snap pada blog saya di
How To Snap To Grid . Pada artikel ini, saya mengusulkan solusi untuk masalah pengikatan posisi suatu objek pada grid spasial. Jika kita perlu mengikat waktu ke kotak waktu, maka matematika, dan karena itu kodenya, akan sama.
Fungsi yang ditunjukkan di bawah ini mengambil angka
x
dan mengikatnya ke nilai integer yang merupakan kelipatan dari
snap
.
inline float snap (float x, float snap) { return snap * round(x / snap); }
Artinya, kode kita menjadi seperti ini:
float time = snap(_Time.y, _NoiseSnap); float2 noise = random3(v.vertex.xyz + float3(time, 0.0, 0.0) ).xy * _NoiseScale; v.vertex.xy += noise;
Kesimpulan
Paket Unity untuk efek ini dapat diunduh secara gratis
di Patreon .
Sumber Daya Tambahan
Selama beberapa bulan terakhir, sejumlah besar permainan dalam gaya corat-coret telah muncul. Menurut saya, alasannya adalah kesuksesan
Doodle Studio 95! - Alat untuk Persatuan, yang dikembangkan oleh
Fernando Ramallo . Jika gaya ini cocok untuk gim Anda, maka saya sarankan untuk membeli alat yang luar biasa ini.