Efek ini terinspirasi oleh 
episode Powerpuff Girls . Saya ingin membuat efek penyebaran warna di dunia hitam dan putih, tetapi untuk 
menerapkannya dalam koordinat ruang dunia , untuk melihat bagaimana 
warna melukis objek , dan tidak hanya menyebar datar di layar, seperti dalam kartun.
Saya menciptakan efek dalam 
Pipa Render Ringan yang baru 
dari mesin Unity, contoh bawaan dari pipa Pipa Rendering Scriptable. Semua konsep berlaku untuk saluran pipa lain, tetapi beberapa fungsi atau matriks bawaan mungkin memiliki nama yang berbeda. Saya juga menggunakan tumpukan pemrosesan pasca baru, tetapi dalam tutorial saya akan menghilangkan deskripsi rinci pengaturannya, karena dijelaskan dengan sangat baik dalam manual lain, misalnya, dalam 
video ini .
Efek post-processing dalam skala abu-abu
Hanya untuk referensi, seperti inilah pemandangan tanpa efek pasca pemrosesan.
Untuk efek ini, saya menggunakan paket Pasca Pemrosesan Unity 2018 yang baru, yang dapat diunduh dari manajer paket. Jika Anda tidak tahu cara menggunakannya, maka saya merekomendasikan 
tutorial ini .
Saya menulis efek saya sendiri dengan memperluas kelas PostProcessingEffectSettings dan PostProcessEffectRenderer yang ditulis dalam C #, kode sumbernya dapat dilihat di 
sini . Sebenarnya, saya tidak melakukan sesuatu yang sangat menarik dengan efek ini pada sisi CPU (dalam kode C #) kecuali bahwa saya menambahkan sekelompok properti umum ke Inspektur, jadi saya tidak akan menjelaskan bagaimana melakukan ini dalam tutorial. Saya harap kode saya berbicara sendiri.
Mari kita beralih ke kode shader dan mulai dengan efek grayscale. Dalam tutorial, kami tidak akan memodifikasi file shaderlab, struktur input, dan vertex shader, sehingga Anda dapat melihat kode sumbernya di 
sini . Sebagai gantinya, kami akan mengurus shader fragmen.
Untuk mengonversi warna menjadi skala abu-abu, kami 
mengurangi nilai setiap piksel menjadi nilai luminance yang menjelaskan 
kecerahannya . Ini dapat dilakukan dengan mengambil produk skalar dari 
nilai warna tekstur kamera dan 
vektor tertimbang , yang menggambarkan kontribusi setiap saluran warna terhadap kecerahan warna secara keseluruhan.
Mengapa kami menggunakan produk skalar? Jangan lupa bahwa produk skalar dihitung sebagai berikut:
dot(a, b) = a x * b x + a y * b y + a z * b zDalam hal ini, kami mengalikan setiap saluran dari 
nilai warna berdasarkan 
berat . Lalu kami menambahkan produk-produk ini untuk mengurangi mereka ke nilai skalar tunggal. Ketika warna RGB memiliki nilai yang sama di saluran R, G, dan B, warnanya berubah menjadi abu-abu.
Seperti apa bentuk kode shader:
 float4 fullColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.screenPos); float3 weight = float3(0.299, 0.587, 0.114); float luminance = dot(fullColor.rgb, weight); float3 greyscale = luminance.xxx; return float4(greyscale, 1.0); 
Jika shader dasar dikonfigurasikan dengan benar, maka efek post-processing akan mewarnai seluruh layar dalam skala abu-abu.
Rendering efek warna di ruang dunia
Karena ini adalah efek pasca pemrosesan, 
kami tidak memiliki informasi tentang geometri adegan di vertex shader. Pada tahap pasca pemrosesan, satu-satunya informasi yang kami miliki adalah 
gambar yang diberikan oleh kamera dan 
ruang koordinat terpotong untuk pengambilan sampelnya. Namun, kami ingin efek pewarnaan menyebar ke seluruh objek, seolah-olah itu terjadi di dunia, dan tidak hanya di layar datar.
Untuk menggambar efek ini dalam geometri pemandangan, kita membutuhkan 
koordinat ruang dunia dari setiap piksel. Untuk berpindah dari 
koordinat ruang koordinat terpotong ke 
koordinat ruang dunia , kita perlu melakukan 
transformasi ruang koordinat .
Biasanya, untuk berpindah dari satu ruang koordinat ke yang lain, diperlukan sebuah matriks yang mendefinisikan transformasi dari ruang koordinat A ke ruang B. Untuk beralih dari A ke B, kita mengalikan vektor dalam ruang koordinat A dengan matriks transformasi ini. Dalam kasus kami, kami akan melakukan transisi berikut: 
ruang koordinat terpotong (ruang klip) -> 
ruang tampilan (ruang tampilan) -> 
ruang dunia (ruang dunia) . Artinya, kita membutuhkan matriks clip-to-view-space dan matriks view-to-world-space yang disediakan Unity.
Namun, 
Koordinat kesatuan dari ruang koordinat terpotong tidak memiliki nilai z yang menentukan kedalaman piksel, atau jarak ke kamera. Kita membutuhkan nilai ini untuk bergerak dari ruang koordinat terpotong ke ruang spesies. Mari kita mulai dengan ini!
Mendapatkan nilai buffer kedalaman
Jika pipa rendering diaktifkan, maka ia menggambar tekstur di 
viewport yang menyimpan 
nilai z dalam struktur yang disebut 
buffer kedalaman . Kami dapat mencicipi buffer ini untuk mendapatkan nilai 
z yang hilang 
dari ruang koordinat kami dari koordinat terpotong!
Pertama, pastikan bahwa 
buffer kedalaman benar-benar diberikan dengan mengklik bagian "Tambahkan Data Tambahan" dari kamera di Inspektur dan memeriksa bahwa kotak "Membutuhkan Tekstur Kedalaman" dicentang. Pastikan juga bahwa opsi Allow MSAA diaktifkan untuk kamera. Saya tidak tahu mengapa efek ini perlu diperiksa, tetapi itu benar. Jika buffer kedalaman digambar, maka di 
frame debugger Anda akan melihat tahap 
"Depth Prepass" .
Buat sampler _CameraDepthTexture dalam 
file hlsl TEXTURE2D_SAMPLER2D(_CameraDepthTexture, sampler_CameraDepthTexture); 
Sekarang mari kita menulis fungsi GetWorldFromViewPosition dan untuk saat ini kita akan menggunakannya untuk memeriksa 
buffer kedalaman . (Nanti kita akan mengembangkannya untuk mendapatkan posisi di dunia.)
 float3 GetWorldFromViewPosition (VertexOutput i) { float z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, i.screenPos).r; return z.xxx; } 
Di shader fragmen, gambarkan nilai sampel tekstur kedalaman.
 float3 depth = GetWorldFromViewPosition(i); return float4(depth, 1.0); 
Ini adalah hasil saya terlihat ketika hanya ada satu dataran berbukit di tempat kejadian (saya mematikan semua pohon untuk lebih menyederhanakan pengujian nilai-nilai ruang dunia). Hasil Anda harus terlihat serupa. Nilai hitam dan putih menggambarkan jarak dari geometri ke kamera.
Berikut adalah beberapa langkah yang dapat Anda ambil jika Anda mengalami masalah:
- Pastikan kamera telah mengaktifkan rendering dalam.
- Pastikan kamera telah mengaktifkan MSAA.
- Coba ganti bidang dekat dan jauh dari kamera.
- Pastikan bahwa objek yang Anda harapkan untuk melihat di buffer kedalaman menggunakan shader dengan pass kedalaman. Ini memastikan bahwa objek menarik ke buffer kedalaman. Semua shader standar di LWRP melakukan ini.
Mendapatkan nilai dalam ruang dunia
Sekarang kita memiliki semua informasi yang diperlukan untuk 
ruang koordinat terpotong , mari kita beralih ke 
ruang spesies , dan kemudian ke 
ruang dunia .
Perhatikan bahwa matriks transformasi yang diperlukan untuk operasi ini sudah ada di perpustakaan SRP. Namun, mereka terkandung dalam pustaka C # engine Unity, jadi saya memasukkannya ke shader di fungsi Render dari skrip 
ColorSpreadRenderer :
 sheet.properties.SetMatrix("unity_ViewToWorldMatrix", context.camera.cameraToWorldMatrix); sheet.properties.SetMatrix("unity_InverseProjectionMatrix", projectionMatrix.inverse); 
Sekarang mari kita memperluas fungsi GetWorldFromViewPosition kami.
Pertama, kita perlu mendapatkan posisi di viewport dengan 
mengalikan posisi dalam ruang koordinat terpotong oleh InverseProjectionMatrix . Kita juga perlu melakukan beberapa sihir voodoo dengan posisi layar, yang terkait dengan bagaimana Unity menyimpan posisinya di ruang koordinat terpotong.
Akhirnya, kita dapat 
melipatgandakan posisi di viewport dengan ViewToWorldMatrix untuk mendapatkan posisi di 
ruang dunia .
 float3 GetWorldFromViewPosition (VertexOutput i) {  
Mari kita lakukan pemeriksaan untuk memastikan bahwa posisi di ruang global sudah benar. Untuk melakukan ini, saya menulis 
shader yang hanya mengembalikan posisi objek di 
ruang dunia ; ini adalah perhitungan yang cukup sederhana berdasarkan shader biasa, kebenarannya dapat dipercaya. Matikan efek pasca-pemrosesan dan ambil tangkapan layar dari shader uji ini untuk 
ruang dunia . Setelah saya menerapkan shader ke permukaan bumi dalam adegan terlihat seperti ini:
(Perhatikan bahwa nilai-nilai di ruang dunia jauh lebih besar dari 1,0, jadi jangan khawatir bahwa warna-warna ini masuk akal; sebagai gantinya, pastikan saja hasilnya sama untuk jawaban "benar" dan "dihitung"). Selanjutnya, mari kita kembali ke tes objek adalah bahan biasa (dan bukan bahan uji ruang dunia), dan kemudian nyalakan efek pasca-pemrosesan lagi. Hasil saya terlihat seperti ini:
Ini benar-benar mirip dengan uji shader yang saya tulis, yaitu, perhitungan ruang dunia kemungkinan besar benar!
Menggambar lingkaran di ruang dunia
Sekarang kita memiliki 
posisi di ruang dunia , kita dapat menggambar lingkaran warna di TKP! Kita perlu mengatur 
radius di mana efeknya akan menggambar warna. Di luar, efeknya akan membuat gambar dalam skala abu-abu. Untuk mengaturnya, Anda perlu menyesuaikan nilai untuk 
radius efek ( 
_MaxSize ) dan pusat lingkaran (_Center). Saya mengatur nilai-nilai ini di kelas C # 
ColorSpread sehingga mereka terlihat di inspektur. Mari perluas shader fragmen kami dengan memaksanya 
untuk memeriksa apakah piksel saat ini ada di dalam radius lingkaran :
 float4 Frag(VertexOutput i) : SV_Target { float3 worldPos = GetWorldFromViewPosition(i);  
Akhirnya, kita bisa menggambar warna berdasarkan apakah itu berada dalam 
radius di 
ruang dunia . Seperti inilah efek dasarnya!
Menambahkan Efek Khusus
Saya akan melihat beberapa teknik lagi yang digunakan untuk membuat warna menyebar di tanah. Ada banyak lagi untuk efek penuh, tetapi tutorial sudah menjadi terlalu besar, jadi kita akan membatasi diri kita pada yang paling penting.
Animasi Lingkaran Pembesaran
Kami ingin efeknya menyebar ke seluruh dunia, yaitu, seolah tumbuh. Untuk melakukan ini, Anda perlu mengubah 
jari - 
jari tergantung pada waktu.
_StartTime menunjukkan waktu di mana lingkaran harus mulai tumbuh. Dalam proyek saya, saya menggunakan skrip tambahan yang memungkinkan Anda mengklik di mana saja di layar untuk memulai pertumbuhan lingkaran; dalam hal ini, waktu mulai sama dengan waktu mouse diklik.
_GrowthSpeed ββmengatur kecepatan meningkatkan lingkaran.
 
Kita juga perlu memperbarui pemeriksaan jarak untuk membandingkan jarak saat ini dengan meningkatnya 
radius efek , dan bukan dengan _MaxSize.
 
Seperti apa hasilnya nanti:
Menambah radius noise
Saya ingin efeknya lebih seperti buram cat, bukan hanya lingkaran yang tumbuh. Untuk melakukan ini, mari kita 
tambahkan noise ke radius efek sehingga distribusinya tidak merata.
Pertama, kita perlu sampel tekstur di 
ruang dunia . Koordinat UV dari i.screenPos terletak di 
ruang layar , dan jika kami sampel berdasarkan pada mereka, bentuk efeknya akan bergerak dengan kamera; jadi mari kita gunakan koordinat di 
ruang dunia . Saya menambahkan parameter 
_NoiseTexScale untuk mengontrol 
skala sampel tekstur noise , karena koordinat di ruang dunia cukup besar.
 
Sekarang mari kita sampel tekstur noise dan tambahkan nilai ini ke radius efek. Saya menggunakan skala _NoiseSize untuk kontrol lebih besar atas ukuran noise.
 
Beginilah hasilnya setelah beberapa penyesuaian:
Kesimpulannya
Anda dapat mengikuti pembaruan tutorial di 
Twitter saya, dan di 
Twitch saya menghabiskan stream coding! (Juga, saya mengalirkan game dari waktu ke waktu, jadi jangan heran jika Anda melihat saya duduk di piyama dan bermain Kingdom Hearts 3.)
Ucapan Terima Kasih: