Refleksi Caustic Realistis


Sebagian besar seniman teknis pada titik tertentu dalam karier mereka mencoba membuat refleksi yang masuk akal tentang kaustik. Jika Anda seorang pengembang game, maka salah satu alasan utama untuk membaca Twitter adalah aliran inspirasi yang tak ada habisnya yang dapat Anda tarik darinya. Beberapa hari yang lalu, Florian Gelzenlichter ( kolyaTQ di twitter) memposting GIF dari efek kaustik yang dibuat di Unity menggunakan shader. Posting (disajikan di bawah) dengan cepat memperoleh 1,5 ribu suka, yang menunjukkan minat yang tulus pada jenis konten ini.


Meskipun saya biasanya lebih tertarik pada serangkaian artikel yang lebih panjang dan rumit secara teknis (misalnya, tentang hamburan cahaya atmosfer volumetrik [ terjemahan di Habré] dan kinematika terbalik [terjemahan bagian pertama dan kedua di Habré), saya tidak bisa menahan godaan untuk menulis tutorial singkat dan lucu) tentang efek Florian .

Di akhir artikel ini ada tautan untuk mengunduh paket Unity dan semua aset yang diperlukan.

Apa itu kaustik?


Anda mungkin tidak menyadari konsep kaustik , meskipun Anda mengalami efek ini setiap hari. Caustics adalah pantulan cahaya yang disebabkan oleh permukaan melengkung. Dalam kasus umum, permukaan melengkung dapat berperilaku seperti lensa, memfokuskan cahaya di beberapa titik dan menyebarkannya di tempat lain. Media yang paling umum memberikan efek seperti itu adalah kaca dan air, yang menghasilkan apa yang disebut gelombang kaustik (lihat di bawah).


Caustics dapat mengambil bentuk lain. Pelangi, misalnya, adalah fenomena optik yang terjadi ketika cahaya dibiaskan dalam tetesan hujan. Karena itu, sebenarnya, itu pedas.

Efek anatomi


Ciri khas dari gelombang kaustik adalah cara bergeraknya; kemungkinan besar Anda melihatnya jika Anda pernah melihat bagian bawah kolam. Membuat kembali kaustik asli sangat mahal karena membutuhkan simulasi banyak sinar.

Florian berhasil menciptakan efek yang masuk akal, dimulai dengan tekstur kaustik tunggal. Untuk membuat tutorial saya, saya menggunakan tekstur yang ditunjukkan di bawah ini, diambil dari OpenGameArt .


Sifat penting yang memungkinkan efek ini terwujud adalah bahwa pola kaustik yang diperlihatkan di atas mulus . Ini berarti bahwa Anda dapat menempatkan dua gambar di samping satu sama lain dan tidak akan ada jahitan yang terlihat di antara mereka. Karena kita ingin menggunakan efek ini pada permukaan besar, penting bagi kita untuk memiliki kesempatan untuk meregangkan tekstur ini tanpa air mata yang dapat menghancurkan ilusi.

Setelah menerima tekstur, Florian menyarankan untuk mengambil tiga langkah:

  • Terapkan pola kaustik dua kali ke permukaan model, setiap kali menggunakan ukuran dan kecepatan yang berbeda
  • Campurkan dua pola dengan operator min
  • Pisahkan saluran RGB saat pengambilan sampel.

Mari kita lihat bagaimana Anda dapat menerapkan setiap langkah di Unity.

Penciptaan Shader


Langkah pertama adalah membuat shader baru. Karena efek ini kemungkinan akan digunakan dalam game 3D yang juga memiliki pencahayaan nyata, yang terbaik adalah memulai dengan shader permukaan . Shader permukaan adalah salah satu dari banyak jenis shader yang didukung oleh Unity (seperti vertex dan shader fragmen untuk bahan yang tidak diterangi, shader layar untuk efek pasca-pemrosesan, dan shader komputasi untuk simulasi di luar layar).

Shader permukaan baru hanya memiliki beberapa fitur. Untuk membuat efek ini, kita perlu mentransfer informasi ke shader. Yang pertama adalah tekstur kaustik. Kedua, ini adalah parameter yang digunakan untuk mengukur dan mengimbanginya.

Mari kita membuat dua properti shader :

 Properties { ... [Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {} // Tiling X, Tiling Y, Offset X, Offset Y _Caustics_ST("Caustics ST", Vector) = (1,1,0,0) } 

dan variabel Cg yang sesuai:

 sampler2D _CausticsTex; float4 _Caustics_ST; 

Properti shader sesuai dengan bidang yang ditampilkan di Inspektur Material Persatuan. Variabel Cg yang sesuai adalah nilai-nilai itu sendiri, yang dapat digunakan dalam kode shader.

Seperti yang Anda lihat dari kode di atas, _Caustics_ST adalah float4 , artinya berisi empat nilai. Kami akan menggunakannya untuk mengontrol pengambilan sampel tekstur kaustik. Yaitu:

  • _Caustics_ST.x : skala tekstur kaustik sepanjang sumbu X;
  • _Caustics_ST.y : skala tekstur kaustik sepanjang sumbu Y;
  • _Caustics_ST.z : perpindahan tekstur kaustik di sepanjang sumbu X;
  • _Caustics_ST.w : perpindahan tekstur kaustik di sepanjang sumbu Y;

Mengapa variabel bernama _Caustics_ST?
Jika Anda sudah memiliki sedikit pengalaman bekerja dengan shader, maka Anda sudah melihat properti lain yang diakhiri dengan akhiran _ST . Di Unity, _ST dapat digunakan untuk menambahkan informasi tambahan tentang bagaimana tekstur sampel.

Misalnya, jika Anda membuat variabel Cg _MainTex_ST , maka Anda dapat menggunakannya untuk mengatur ukuran dan mengimbangi saat menerapkan tekstur ke model.

Biasanya variabel _ST tidak memerlukan properti karena mereka ditampilkan secara otomatis di inspektur. Namun, dalam kasus khusus ini, kami tidak dapat mengandalkan ini karena kami perlu sampel tekstur dua kali, setiap kali dengan skala dan offset yang berbeda. Di masa depan, kita perlu menduplikasi variabel ini menjadi dua variabel yang berbeda.

Tekstur pengambilan sampel


Setiap permukaan shader berisi fungsi, biasa disebut surf , yang digunakan untuk menentukan warna setiap piksel yang diberikan. Fungsi surf "standar" terlihat seperti ini:

 void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 

Warna akhir ditentukan oleh jumlah bidang yang shader harus menginisialisasi dan kembali dalam struktur yang disebut SurfaceOutputStandard . Kita perlu mengubah Albedo , yang kira-kira cocok dengan warna objek yang diterangi oleh cahaya putih.

Di shader permukaan yang baru dibuat, Albedo diambil dari tekstur yang disebut _MainTex . Karena efek kaustik ditumpangkan di atas tekstur yang ada, kita harus melakukan pengambilan sampel tambahan tekstur di _CausticsTex .

Teknik yang disebut hamparan UV memungkinkan Anda memahami bagian mana dari tekstur yang perlu disampel, tergantung pada bagian geometri mana yang perlu dirender. Ini dilakukan dengan menggunakan uv_MainTex - variabel float2 , disimpan di setiap vertex dari model 3D dan menunjukkan koordinat tekstur.

Gagasan kami adalah menggunakan _Caustics_ST untuk menskala dan mengimbangi uv_MainTex untuk meregangkan dan memindahkan tekstur kaustik ke seluruh model.

 void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Caustics sampling fixed2 uv = IN.uv_MainTex * _Caustics_ST.xy + _Caustics_ST.zw; fixed3 caustics = tex2D(_CausticsTex, uv).rgb; // Add o.Albedo.rgb += caustics; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 

Apa yang terjadi jika Albedo melebihi 1?
Dalam kode di atas, kami menambahkan dua tekstur. Warna biasanya antara 0sebelumnya 1Namun, tidak ada jaminan bahwa sebagai hasilnya, beberapa nilai tidak akan melebihi interval ini.

Pada shader lama, ini bisa menyebabkan masalah. Ini sebenarnya fitur . Jika nilai warna piksel melebihi satu, maka ini artinya pengaruhnya harus "menyebar" di luar batas negara dan memengaruhi piksel yang berdekatan.

Inilah yang terjadi ketika refleksi specular yang sangat cerah diperoleh. Namun, efek ini seharusnya tidak dibuat hanya oleh shader permukaan. Agar efeknya berfungsi, kamera harus mengaktifkan HDR . Properti ini adalah singkatan dari High Dynamic Range ; itu memungkinkan nilai warna melebihi 1. Juga, untuk mengaburkan jumlah warna yang berlebihan pada piksel tetangga, efek pasca pemrosesan diperlukan.

Unity memiliki tumpukan post-processing sendiri, yang memiliki filter bloom yang melakukan hal itu. Anda dapat membaca lebih lanjut tentang ini di Blog Persatuan: PostFX v2 - Visual luar biasa, ditingkatkan .

Hasil awal ditunjukkan di bawah ini:


Kaustik animasi


Salah satu fitur terpenting dari kaustik adalah bagaimana ia bergerak. Saat ini, mereka hanya diproyeksikan secara statis ke permukaan model sebagai tekstur kedua.

Animasi materi dalam shader dapat diimplementasikan menggunakan properti Unity _Time . Ini dapat digunakan untuk mengakses waktu gim saat ini, yaitu, menambah waktu ke persamaan.

Cara termudah adalah dengan hanya mengimbangi tekstur berdasarkan waktu saat ini.

 // Caustics UV fixed2 uv = IN.uv_MainTex * _Caustics_ST.xy + _Caustics_ST.zw; uv += _CausticsSpeed * _Time.y; // Sampling fixed3 caustics = tex2D(_CausticsTex, uv).rgb; // Add o.Albedo.rgb += caustics; 

Bidang _Time.y berisi waktu pemutaran saat ini dalam detik . Jika refleksi bergerak terlalu cepat, Anda dapat melipatgandakannya dengan faktor. Untuk ini, variabel _CausticsSpeed dari tipe float2 digunakan dalam kode yang disajikan di atas.

Anda mungkin perlu menggetarkan tekstur kaustik dalam sinusoid untuk keperluan Anda. Penting untuk dipahami di sini bahwa tidak ada cara standar untuk menyadari efeknya. Tergantung pada kebutuhan Anda, Anda dapat membuat pantulan kaustik bergerak sangat berbeda.

Hasil yang ditunjukkan di bawah ini masih cukup biasa-biasa saja. Ini normal: kita masih harus melakukan banyak hal untuk membuat pantulan terlihat indah.


Pengambilan sampel berganda


Efeknya menjadi hidup jika Anda mencicipi tekstur kaustik tidak hanya sekali, tetapi dua kali. Jika Anda meletakkannya di atas satu sama lain dan memindahkannya dengan kecepatan yang berbeda, hasilnya akan sangat berbeda.

Pertama, kami menduplikasi properti _Caustics_ST dan _CausticsSpeed sehingga sampel dari dua tekstur memiliki skala, perpindahan, dan kecepatan yang berbeda:

 [Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {} // Tiling X, Tiling Y, Offset X, Offset Y _Caustics1_ST("Caustics 1 ST", Vector) = (1,1,0,0) _Caustics2_ST("Caustics 1 ST", Vector) = (1,1,0,0) // Speed X, Speed Y _Caustics1_Speed("Caustics 1 Speed", Vector) = (1, 1, 0 ,0) _Caustics2_Speed("Caustics 2 Speed", Vector) = (1, 1, 0 ,0) 

Sekarang kami memiliki dua sampel kaustik, mereka dapat dicampur menggunakan operator min . Jika Anda hanya mengambil nilai rata-rata, hasilnya tidak akan terlalu bagus.

 // Caustics samplings fixed3 caustics1 = ... fixed3 caustics2 = ... // Blend o.Albedo.rgb += min(caustics1, caustics2); 

Perubahan kecil seperti itu membuat perbedaan besar:


Agar kode tetap cantik, Anda juga dapat membungkus kode sampling kaustik dalam fungsi Anda sendiri:

 // Caustics fixed3 c1 = causticsSample(_CausticsTex, IN.uv_MainTex, _Caustics1_ST, _Caustics1_Speed); fixed3 c2 = causticsSample(_CausticsTex, IN.uv_MainTex, _Caustics2_ST, _Caustics2_Speed); o.Albedo.rgb += min(c1, c2); 

Pemisahan RGB


Agar pantulan kaustik terlihat bagus, Anda perlu melakukan trik terakhir. Melewati irisan, cahaya dengan panjang gelombang berbeda dibiaskan secara berbeda. Ini berarti bahwa ketika bergerak melalui air, cahaya dapat "terpecah" menjadi warna yang berbeda.

Untuk mensimulasikan efek ini, kita dapat membagi masing-masing sampel kaustik menjadi tiga, satu untuk setiap saluran warna. Dengan mengambil sampel saluran merah, hijau, dan biru dengan sedikit bias, kami mendapatkan ketidakcocokan warna.

Mari kita mulai dengan menambahkan properti _SplitRGB , yang menunjukkan kekuatan efek _SplitRGB :

 // Caustics UV fixed2 uv = IN.uv_MainTex * _Caustics_ST.xy + _Caustics_ST.zw; uv += _CausticsSpeed * _Time.y; // RGB split fixed s = _SplitRGB; fixed r = tex2D(tex, uv + fixed2(+s, +s)).r; fixed g = tex2D(tex, uv + fixed2(+s, -s)).g; fixed b = tex2D(tex, uv + fixed2(-s, -s)).b; fixed3 caustics = fixed3(r, g, b); 

Jumlah offset saluran RGB dapat dipilih secara sewenang-wenang, tetapi bahkan dengan offset sederhana ini, gambar yang sangat meyakinkan diperoleh:


Kesimpulan dan unduhan


Jika Anda tertarik mempelajari cara membuat tekstur kaustik mulus, maka Anda harus membaca artikel menarik Tekstur Kaustik Berkala .

Sementara itu, Florian terus bekerja pada shader kaustik dan telah membuat beberapa perbaikan yang cukup menarik yang bisa dilihat.


Paket lengkap untuk tutorial ini tersedia di Patreon, termasuk semua aset yang diperlukan untuk membuat ulang teknik ini. Paket ini diekspor dari Unity 2019.2 dan membutuhkan Postprocessing Stack v2.

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


All Articles