Di bawah tenda Graveyard Keeper: Bagaimana efek grafis diimplementasikan

Halo semuanya! Selama 4 tahun saya belum menulis di Habr. Seri posting terakhir saya adalah tentang berbagai alat dan trik yang kami gunakan pada game terakhir kami (mengembangkannya di Unity). Sejak itu, kami berhasil merilis game, dan juga merilis yang baru. Jadi sekarang Anda bisa bernafas sedikit dan menulis beberapa artikel baru yang mungkin berguna bagi seseorang.


Hari ini saya ingin berbicara tentang trik dan trik grafik yang kami gunakan untuk membuat gambar yang Anda lihat di GIF di atas.

Kami sangat peka terhadap visual permainan kami dan karenanya telah menginvestasikan banyak waktu dan upaya dalam berbagai efek dan barang lainnya yang akan membuat pixel art kami semenarik mungkin. Mungkin seseorang akan menemukan sesuatu yang bermanfaat untuk diri mereka sendiri.

Untuk memulainya, saya akan secara singkat mencantumkan gambar apa yang akan ada dalam permainan kami:

  1. Lampu ambient variabel - perubahan dangkal dalam pencahayaan tergantung pada waktu hari.
  2. Koreksi warna-LUT - bertanggung jawab untuk mengubah nada gambar tergantung pada waktu hari (atau jenis zona).
  3. Sumber cahaya dinamis - obor, kompor, lampu.
  4. Peta normal - bertanggung jawab untuk memberikan volume ke objek, terutama saat memindahkan sumber cahaya.
  5. Matematika distribusi cahaya 3D - bertanggung jawab untuk memastikan bahwa sumber cahaya di tengah layar dengan benar menerangi objek yang lebih tinggi, tetapi tidak menerangi objek yang lebih rendah (mis. Berbalik ke arah kamera dengan sisi yang tidak menyala).
  6. Shadows - dibuat oleh sprite, memutar dan merespons posisi sumber cahaya.
  7. Simulasi ketinggian objek - untuk tampilan kabut yang benar.
  8. Dekorator lain: hujan, angin, animasi (termasuk animasi shader dedaunan dan rumput), dll.

Sekarang lebih detail.

Lampu ambient variabel


Di sini, pada prinsipnya, tidak ada yang istimewa. Di malam hari - lebih gelap, siang hari - lebih terang. Warna cahaya diatur oleh gradien waktu. Pada malam hari, sumber cahaya tidak hanya menjadi lebih gelap, tetapi memperoleh warna biru.

Ini terlihat seperti ini:


Koreksi warna LUT


LUT (Look-up table) - tabel swap warna. Secara kasar, ini adalah array RGB tiga dimensi di mana pada setiap node ada nilai warna, yang harus diganti dengan yang sesuai. Artinya, jika titik merah terletak pada koordinat (1, 1, 1), ini berarti bahwa semua warna putih dalam gambar akan diganti dengan merah. Jika koordinat (1, 1, 1) berwarna putih (R = 1, G = 1, B = 1), maka tidak ada perubahan. Dengan demikian, LUT tanpa perubahan memiliki warna untuk setiap koordinat yang sesuai dengan koordinat yang sama ini. Yaitu pada titik (0,4, 0,5, 0,8) adalah warna (R = 0,4, G = 0,5, B = 0,8).

Yah, perlu dicatat bahwa untuk kenyamanan mereka menghadirkan tekstur 3D sebagai dua dimensi. Sebagai contoh, seperti inilah tampilan "default" LUT (tanpa mengubah rendering warna):



Ini diterapkan secara elemen, ia bekerja dengan cepat dan mudah.

Ini juga sangat mudah diatur - Anda memberi artis gambar apa saja dari permainan dan mengatakan "nada warna sehingga terasa seperti malam hari." Setelah itu, terapkan semua lapisan koreksi warna ke LUT default dan dapatkan LUT malam.

Dalam kasus kami, artis mengalami sedikit macet dan menciptakan sebanyak 10 LUT yang berbeda untuk waktu yang berbeda dalam sehari (malam, senja, malam, dll.). Beginilah pengaturannya terlihat:


Akibatnya, tergantung pada waktu, lokasi yang sama terlihat berbeda:



Di sini, transparansi sprite cahaya dari windows juga berubah tergantung pada waktu hari.

Sumber cahaya dinamis dan peta normal


Sumber cahaya digunakan benar-benar biasa, dari Unity. Selain itu, peta normal digambar untuk setiap sprite, yang memungkinkan Anda untuk merasakan volume.


Normal seperti itu digambarkan dengan sederhana. Sang seniman secara kasar melukiskan cahaya dengan kuas dari 4 sisi:


Dan kemudian skrip ini menuju ke peta normal:


Jika Anda mencari shader (dan perangkat lunak) yang melakukan ini, Anda dapat melihat ke arah Lampu Sprite.

Simulasi cahaya 3D


Ini sedikit lebih rumit. Anda tidak bisa mengambil dan menyorot sprite. Kita perlu mempertimbangkan apakah sprite "di belakang" sumber cahaya atau "di depan".

Perhatikan gambar ini:


Kedua pohon berada pada jarak yang sama dari sumber cahaya, namun, pohon yang jauh menyala, dan pohon terdekat tidak (karena bagian yang tidak menyala diputar ke arah kamera).

Saya memecahkan masalah ini dengan cukup sederhana. Shader menghitung jarak sepanjang sumbu y vertikal antara sumber cahaya dan sprite. Dan jika itu positif (sumber cahaya sebelum sprite), maka kita menerangi sprite seperti biasa, tetapi jika itu negatif (sprite memblokir sumber cahaya), tetapi intensitas cahaya meluruh sangat jauh dari jarak dengan koefisien yang sangat besar. Justru koefisien yang dibuat, dan bukan hanya "tidak menyala", sehingga ketika sumber cahaya bergerak dan tiba-tiba muncul di belakang sprite, sprite tidak langsung menjadi hitam, tetapi secara bertahap. Tapi masih cukup cepat.


Bayangan


Bayangan dibuat oleh sprite yang berputar di sekitar titik. Saya mencoba untuk menambahkan lebih banyak kompresi (condong) kepada mereka, tetapi ternyata tidak perlu.

Secara total, setiap objek dapat memiliki maksimal 4 bayangan. Salah satunya dari Matahari, dan tiga dari sumber cahaya dinamis. Gambar di bawah ini menunjukkan prinsip:



Tugas "menemukan 3 sumber cahaya berikutnya dan menghitung jarak / sudut bayangan kepada mereka" diselesaikan dengan skrip yang berputar di Pembaruan. Ya, itu tidak bekerja dengan sangat cepat, karena kamu harus melakukan banyak matematika. Jika saya menulis sekarang, saya akan menggunakan sistem pekerjaan paralel model baru di Unity. Tapi ini belum, jadi saya hanya mengoptimalkan skrip biasa sebanyak mungkin.

Satu-satunya hal yang penting adalah bahwa saya membuat rotasi sprite tidak berubah, tetapi di dalam shader vertex. Yaitu rotasi tidak bergerak. Hanya saja parameternya diatur dalam sprite (saya menggunakan warna untuk ini, karena semua sama, semua bayangan hitam), dan shader sudah bertanggung jawab untuk rotasi sprite. Ini lebih cepat karena tidak perlu mencabut geometri dalam Unity.

Kelemahan lain dari pendekatan ini adalah bahwa bayangan harus diatur (dan kadang-kadang dicat) secara individual untuk setiap objek. Benar, kami berhasil, mungkin, dengan selusin sprite universal yang sedikit banyak berbeda (tipis, tebal, oval, dll.).

Kerugian kedua adalah terkadang sulit membuat bayangan untuk objek yang titik kontaknya dengan bumi sangat memanjang. Misalnya, lihat bayangan dari pagar:


Tidak sempurna. Ini terlihat seperti jika Anda membuat sprite pagar itu sendiri tembus:


Di sini, bagaimanapun, perlu dicatat bahwa sprite masih sangat cacat secara vertikal (sprite bayangan asli hampir terlihat seperti lingkaran). Itulah sebabnya gilirannya bukan hanya berupa belokan, tetapi sebagai distorsi.

Simulasi kabut dan ketinggian


Ada kabut di dalam game. Sepertinya ini (di atas adalah versi normal, di bawah ini adalah kabut 100% ekstrim untuk menunjukkan efeknya).



Seperti yang Anda lihat, puncak-puncak rumah dan pepohonan menonjol dari kabut. Sebenarnya, mencapai efek ini cukup sederhana. Kabut terdiri dari banyak awan horisontal yang didistribusikan ke seluruh kedalaman panggung. Dan hasilnya, ternyata bagian atas dari semua sprite diblokir oleh sprite kabut yang lebih sedikit:



Angin


Pixel art wind adalah cerita lain. Tidak banyak pilihan. Baik bernyawa dengan tangan Anda (yang hampir tidak mungkin dengan jumlah karya seni kami), atau menulis shader yang cacat, tetapi kemudian Anda terkadang harus menanggung distorsi yang buruk. Anda bisa, tentu saja, tidak bernyawa sama sekali, tetapi kemudian gambarnya terlihat mati.

Kami memilih opsi distorsi menggunakan shader. Ini terlihat seperti ini:


Jika Anda menerapkan shader ini ke tekstur kotak-kotak, menjadi jelas apa yang terjadi:


Perlu juga dicatat bahwa kita tidak menjiwai seluruh mahkota, tetapi hanya individu yang pergi:


Kami juga mengocok gandum dalam angin, tetapi semuanya sederhana - vertex shader merusak koordinat x, dengan mempertimbangkan komponen-y. Semakin tinggi titik, semakin kuat sempoyongan. Hal ini dilakukan agar hanya yang terhuyung, tetapi root tidak. Plus - fase goyangan berubah dari koordinat x / y sehingga berbagai sprite di layar berayun secara acak.


Shader yang sama juga digunakan untuk menciptakan efek goyang gandum dan rumput ketika pemain melewatinya.


Mungkin itu saja untuk saat ini. Saya sengaja tidak membahas masalah membangun adegan dan geometri, karena ini adalah bahan untuk artikel terpisah. Selebihnya, ia berbicara tentang solusi utama yang digunakan dalam pengembangan.

PS: Kalau ada yang tertarik dengan aspek teknis, tulis di komentar. Mungkin saya akan menceritakan dalam artikel terpisah. Kecuali, tentu saja, perlu.

PPS: Saya mengambil kesempatan ini untuk mengatakan bahwa sekarang kami ingin menemukan beberapa orang yang kompeten dalam tim (programmer, PM, KM, artis). Detail ada di situs web studio. Saya harap kalimat ini tidak melanggar aturan.

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


All Articles