Shades Pembubaran dan Eksplorasi Dunia

Bagian 1: Pembubaran Shader


Shader pembubaran mengembalikan efek yang indah, apalagi, mudah dibuat dan dipahami; Hari ini kita akan membuatnya dalam Unity Shader Graph , dan juga menulis di HLSL .

Berikut ini contoh yang akan kami buat:



Bagaimana cara kerjanya


Untuk membuat shader terlarut , kita harus bekerja dengan nilai AlphaClipThreshold di shader "Shader Graph" atau menggunakan fungsi HLSL yang disebut clip .

Pada dasarnya, kami memberi tahu shader untuk tidak merender piksel berdasarkan tekstur dan nilai yang diteruskan. Kita perlu mengetahui hal berikut: bagian putih larut lebih cepat .

Kami akan menggunakan tekstur berikut:


Anda dapat membuat sendiri - garis lurus, segitiga, tetapi apa saja! Ingatlah bahwa bagian putih larut lebih cepat .

Saya membuat tekstur ini di Photoshop menggunakan filter Clouds.

Bahkan jika Anda hanya tertarik pada Grafik Shader dan Anda tidak tahu apa-apa tentang HLSL, saya masih merekomendasikan membaca bagian ini, karena berguna untuk memahami bagaimana Unity Shader Graph bekerja di dalam.



Hlsl


Dalam HLSL, kami menggunakan fungsi klip (x) . Fungsi klip (x) membuang semua piksel dengan nilai kurang dari nol . Karena itu, jika kita memanggil klip (-1) , kita akan yakin bahwa shader tidak akan pernah merender piksel ini. Anda dapat membaca lebih lanjut tentang klip di Microsoft Documents .

Sifat-sifat


Shader memerlukan dua properti, Dissolve Texture and Amount (yang akan menunjukkan proses eksekusi keseluruhan). Seperti halnya properti dan variabel lain, Anda dapat menyebutnya apa saja yang Anda suka.

Properties { //Your other properties //[...] //Dissolve shader properties _DissolveTexture("Dissolve Texture", 2D) = "white" {} _Amount("Amount", Range(0,1)) = 0 } 

Pastikan untuk menambahkan yang berikut setelah CGPROGRAM SubShader (dengan kata lain, mendeklarasikan variabel):

 sampler2D _DissolveTexture; half _Amount; 

Juga, jangan lupa. bahwa nama mereka harus cocok dengan nama di bagian Properties.

Fungsi


Kami memulai fungsi Permukaan atau Fragmen dengan mengambil sampel tekstur pembubaran dan mendapatkan nilai merah . PS Tekstur kami disimpan dalam skala abu-abu , yaitu nilainya R , G dan B sama, dan Anda dapat memilih salah satunya . Misalnya, putih adalah (1,1,1) , hitam adalah (0,0,0) .

Dalam contoh saya, saya menggunakan shader permukaan:

 void surf (Input IN, inout SurfaceOutputStandard o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).r; //Get how much we have to dissolve based on our dissolve texture clip(dissolve_value - _Amount); //Dissolve! //Your shader body, you can set the Albedo etc. //[...] } 

Dan itu dia! Kita dapat menerapkan proses ini untuk shader yang ada dan mengubahnya menjadi shader pembubaran !

Ini adalah Surface Shader standar dari mesin Unity, yang diubah menjadi shader pembubaran dua sisi :

 Shader "Custom/DissolveSurface" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 //Dissolve properties _DissolveTexture("Dissolve Texutre", 2D) = "white" {} _Amount("Amount", Range(0,1)) = 0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Cull Off //Fast way to turn your material double-sided CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; //Dissolve properties sampler2D _DissolveTexture; half _Amount; void surf (Input IN, inout SurfaceOutputStandard o) { //Dissolve function half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).r; clip(dissolve_value - _Amount); //Basic shader function fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } ENDCG } FallBack "Diffuse" } 



Grafik shader


Jika kita perlu membuat efek ini menggunakan Unity Shader Graph , maka kita harus menggunakan nilai AlphaClipThreshold (yang bekerja secara berbeda dari klip (x) dari HLSL). Dalam contoh ini, saya membuat shader PBR.

Fungsi AlphaClipThreshold menginstruksikan shader untuk membuang semua piksel yang nilainya kurang dari nilai Alpha -nya. Misalnya, jika 0.3f , dan nilai alfa kami adalah 0.2f , maka shader tidak akan membuat piksel ini. Fungsi AlphaClipThreshold dapat ditemukan di dokumentasi Unity : PBR Master Node dan Unlit Master Node .

Ini shader yang sudah jadi:


Kami sampel tekstur pembubaran dan mendapatkan nilai merah , dan kemudian menambahkannya ke nilai Jumlah (yang merupakan properti yang saya tambahkan untuk menunjukkan proses eksekusi keseluruhan, nilai 1 berarti pembubaran lengkap) dan hubungkan ke AlphaClipThreshold . Selesai!

Jika Anda ingin menerapkannya pada shader yang ada, maka cukup salin koneksi node ke AlphaClipThreshold (jangan lewatkan properti yang diperlukan!). Anda juga dapat membuatnya dua sisi dan mendapatkan hasil yang lebih indah!



Pengurai kontur kontur


Dan jika Anda mencoba menambahkan kontur ke dalamnya? Ayo lakukan!


Kami tidak dapat bekerja dengan piksel yang sudah terlarut, karena setelah menjatuhkannya, piksel tersebut menghilang selamanya . Sebagai gantinya, kita dapat bekerja dengan nilai-nilai "hampir larut"!

Di HLSL, ini sangat sederhana, cukup tambahkan beberapa baris kode setelah menghitung klip :

 void surf (Input IN, inout SurfaceOutputStandard o) { //[...] //After our clip calculations if (dissolve_value - _Amount < .05f) //outline width = .05f o.Emission = fixed3(1, 1, 1); //emits white color //Your shader body, you can set the Albedo etc. //[...] } 

Selesai!

Saat bekerja dengan Grafik Shader, logikanya sedikit berbeda. Inilah shader yang sudah jadi:




Kita dapat membuat efek yang sangat keren dengan shader disolusi sederhana; Anda dapat bereksperimen dengan berbagai tekstur dan nilai , serta menghasilkan sesuatu yang lain!

Bagian 2: Dunia eksplorasi shader


Sebuah shader penjelajahan dunia (atau " shader pembubaran dunia , atau pembubaran global ") memungkinkan kita untuk sama-sama menyembunyikan semua objek dalam adegan berdasarkan jaraknya ke posisi; sekarang kita akan membuat shader seperti itu dalam Unity Shader Graph dan menuliskannya dalam HLSL .

Berikut ini contoh yang akan kami buat:




Jarak sebagai parameter


Misalkan kita perlu membubarkan sebuah objek dalam sebuah adegan jika terlalu jauh dari pemain . Kami telah mengumumkan parameter _Amount , yang mengontrol hilangnya / pembubaran objek, jadi kami harus menggantinya dengan jarak antara objek dan pemain.

Untuk melakukan ini, kita perlu mengambil posisi Player dan Object .

Posisi Pemain


Prosesnya akan serupa untuk Unity Shader Graph dan HLSL : kita perlu mentransfer posisi pemain dalam kode.

 private void Update() { //Updates the _PlayerPos variable in all the shaders //Be aware that the parameter name has to match the one in your shaders or it wont' work Shader.SetGlobalVector("_PlayerPos", transform.position); //"transform" is the transform of the Player } 



Grafik shader


Posisi objek dan jarak ke sana


Menggunakan Grafik Shader, kita dapat menggunakan node Posisi dan Jarak.



PS Agar sistem ini berfungsi dengan Sprite Renderers, Anda perlu menambahkan properti _MainTex, sampel dan sambungkan ke albedo. Anda dapat membaca tutorial Spader shader difus saya sebelumnya (yang menggunakan grafik shader).



HLSL (permukaan)


Posisi Obyek


Dalam HLSL, kita dapat menambahkan variabel worldPos ke struktur Input kami untuk mendapatkan posisi simpul objek.

 struct Input { float2 uv_MainTex; float3 worldPos; //add this and Unity will set it automatically }; 

Pada halaman dokumentasi Unity, Anda dapat mengetahui parameter bawaan apa yang diizinkan untuk ditambahkan ke struktur input.

Terapkan jarak


Kita perlu menggunakan jarak antara objek dan pemain sebagai jumlah pembubaran. Untuk melakukan ini, Anda dapat menggunakan fungsi jarak built-in ( dokumentasi Microsoft ).

 void surf (Input IN, inout SurfaceOutputStandard o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).x; float dist = distance(_PlayerPos, IN.worldPos); clip(dissolve_value - dist/ 6f); //"6" is the maximum distance where your object will start showing //Set albedo, alpha, smoothness etc[...] } 

Hasil (3D)



Hasil (2D)



Seperti yang Anda lihat, objek larut "lokal", kami tidak mendapatkan efek homogen, karena kami mendapatkan "nilai disolusi" dari tekstur sampel menggunakan UV dari masing-masing objek. (Dalam 2D, ini kurang terlihat).



3D LocalUV Melarutkan Shader di HLSL


 Shader "Custom/GlobalDissolveSurface" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 _DissolveTexture("Dissolve texture", 2D) = "white" {} _Radius("Distance", Float) = 1 //distance where we start to reveal the objects } SubShader{ Tags { "RenderType" = "Opaque" } LOD 200 Cull off //material is two sided CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; sampler2D _DissolveTexture; //texture where we get the dissolve value struct Input { float2 uv_MainTex; float3 worldPos; //Built-in world position }; half _Glossiness; half _Metallic; fixed4 _Color; float3 _PlayerPos; //"Global Shader Variable", contains the Player Position float _Radius; void surf (Input IN, inout SurfaceOutputStandard o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).x; float dist = distance(_PlayerPos, IN.worldPos); clip(dissolve_value - dist/ _Radius); fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } ENDCG } FallBack "Diffuse" } 

Sprite Diffuse - LocalUV Melarutkan Shader di HLSL


 Shader "Custom/GlobalDissolveSprites" { Properties { [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {} _Color("Tint", Color) = (1,1,1,1) [MaterialToggle] PixelSnap("Pixel snap", Float) = 0 [HideInInspector] _RendererColor("RendererColor", Color) = (1,1,1,1) [HideInInspector] _Flip("Flip", Vector) = (1,1,1,1) [PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {} [PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0 _DissolveTexture("Dissolve texture", 2D) = "white" {} _Radius("Distance", Float) = 1 //distance where we start to reveal the objects } SubShader { Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" "CanUseSpriteAtlas" = "True" } Cull Off Lighting Off ZWrite Off Blend One OneMinusSrcAlpha CGPROGRAM #pragma surface surf Lambert vertex:vert nofog nolightmap nodynlightmap keepalpha noinstancing #pragma multi_compile _ PIXELSNAP_ON #pragma multi_compile _ ETC1_EXTERNAL_ALPHA #include "UnitySprites.cginc" struct Input { float2 uv_MainTex; fixed4 color; float3 worldPos; //Built-in world position }; sampler2D _DissolveTexture; //texture where we get the dissolve value float3 _PlayerPos; //"Global Shader Variable", contains the Player Position float _Radius; void vert(inout appdata_full v, out Input o) { v.vertex = UnityFlipSprite(v.vertex, _Flip); #if defined(PIXELSNAP_ON) v.vertex = UnityPixelSnap(v.vertex); #endif UNITY_INITIALIZE_OUTPUT(Input, o); o.color = v.color * _Color * _RendererColor; } void surf(Input IN, inout SurfaceOutput o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).x; float dist = distance(_PlayerPos, IN.worldPos); clip(dissolve_value - dist / _Radius); fixed4 c = SampleSpriteTexture(IN.uv_MainTex) * IN.color; o.Albedo = c.rgb * ca; o.Alpha = ca; } ENDCG } Fallback "Transparent/VertexLit" } 

PS Untuk membuat shader terakhir, saya menyalin standar Unity Sprite-Diffuse shader dan menambahkan bagian "larut" yang dijelaskan sebelumnya di bagian artikel ini. Semua shader standar dapat ditemukan di sini .



Membuat efeknya homogen


Untuk membuat efek homogen, kita dapat menggunakan koordinat global (posisi di dunia) sebagai koordinat UV dari tekstur disolusi. Penting juga untuk mengatur Wrap = Ulangi dalam parameter tekstur pembubaran sehingga kita dapat mengulangi tekstur tanpa menyadarinya (pastikan teksturnya mulus dan diulang dengan baik!)


HLSL (permukaan)


 half dissolve_value = tex2D(_DissolveTexture, IN.worldPos / 4).x; //I modified the worldPos to reduce the texture size 

Grafik shader



Hasil (2D)



Inilah hasilnya: kita dapat melihat bahwa tekstur pembubaran sekarang seragam untuk seluruh dunia.

Shader ini sudah ideal untuk gim 2D , tetapi untuk objek 3D perlu ditingkatkan .

Masalah dengan objek 3D



Seperti yang Anda lihat, shader tidak berfungsi untuk wajah "non-vertikal", dan sangat merusak tekstur. Inilah sebabnya mengapa itu terjadi. bahwa koordinat UV memerlukan nilai float2, dan jika kita melewati worldPos, maka hanya menerima X dan Y.

Jika kami memperbaiki masalah ini dengan menerapkan perhitungan untuk menampilkan tekstur pada semua wajah, kami akan menemukan masalah baru: ketika gelap, objek akan saling bersilangan, dan tidak akan tetap homogen.

Akan sulit bagi pemula untuk memahami solusinya: perlu untuk menghilangkan tekstur, menghasilkan suara tiga dimensi di dunia dan mendapatkan "nilai pembubaran" dari itu. Dalam posting ini saya tidak akan menjelaskan generasi noise 3D, tetapi Anda dapat menemukan banyak fungsi yang siap digunakan!

Berikut ini contoh noise shader: https://github.com/keijiro/NoiseShader . Anda juga dapat mempelajari cara menghasilkan noise di sini: https://thebookofshaders.com/11/ dan di sini: https://catlikecoding.com/unity/tutorials/noise/

Saya akan mengatur fungsi permukaan saya dengan cara ini (dengan asumsi Anda sudah menulis bagian noise):

 void surf (Input IN, inout SurfaceOutputStandard o) { float dist = distance(_PlayerPos, IN.worldPos); //"abs" because you have to make sure that the noise is between the range [0,1] //you can remove "abs" if your noise function returns a value between [0,1] //also, replace "NOISE_FUNCTION_HERE" with your 3D noise function. half dissolve_value = abs(NOISE_FUNCTION_HERE(IN.worldPos)); if (dist > _Radius) { float clip_value = dissolve_value - ((dist - _Radius) / _Radius); clip(clip_value); if (clip_value < 0.05f) o.Emission = float3(1, 1, 1); } fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 

Pengingat singkat HLSL: sebelum menggunakan / memanggil fungsi, itu harus ditulis / dideklarasikan.

PS Jika Anda ingin membuat shader menggunakan Unity Shader Graph, Anda perlu menggunakan Custom Nodes (dan menghasilkan noise dengan menuliskan kode HLSL di dalamnya). Saya akan berbicara tentang Custom Nodes dalam tutorial mendatang.

Hasil (3D)





Menambahkan Kontur


Untuk menambahkan kontur, Anda perlu mengulangi proses dari bagian sebelumnya dari tutorial.




Efek terbalik


Dan jika kita ingin membalikkan efek ini? (Benda akan hilang jika pemain ada di dekatnya)

Cukup bagi kita untuk mengubah satu baris:

 float dist = _Radius - distance(_PlayerPos, IN.worldPos); 

(Proses yang sama berlaku untuk Grafik Shader).

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


All Articles