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;
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() {
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;
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);
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;
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).