Halo semuanya! Nama saya Grigory Dyadichenko, dan saya adalah pendiri dan CTO dari Foxsys Studios. Hari ini kita akan berbicara tentang vertex shader. Artikel ini akan memeriksa praktik dari sudut pandang Unity, contoh yang sangat sederhana, serta banyak tautan untuk mempelajari informasi tentang shader di Unity. Jika Anda pandai menulis shader, maka Anda tidak akan menemukan sesuatu yang baru untuk diri sendiri. Siapa pun yang ingin mulai menulis shader di Unity, selamat datang di kucing.

Sedikit teori
Untuk pemahaman yang lebih baik tentang proses shader, mari kita lihat sedikit teori. Vertex shader atau vertex shader adalah tahap yang dapat diprogram dari shader yang bekerja dengan masing-masing vertex. Vertex pada gilirannya menyimpan berbagai atribut yang diproses oleh bagian shader ini untuk mendapatkan atribut yang dikonversi pada output.
Contoh di mana vertex shader digunakan
Deformasi objek - gelombang realistis, efek riak dari hujan, deformasi ketika peluru mengenai, semua ini dapat dilakukan dengan vertex shaders, dan itu akan terlihat lebih realistis daripada hal yang sama yang dilakukan melalui Bump Mapping di bagian fragmen shader. Karena ini adalah perubahan geometri. Shaders Level 3.0 pada subjek ini memiliki teknik yang disebut Pemetaan Penempatan, karena mereka sekarang memiliki akses ke tekstur di bagian sudut shader.
Animasi objek. Permainan terlihat lebih hidup dan menarik ketika tanaman bereaksi terhadap karakter atau pohon bergoyang tertiup angin. Untuk ini, vertex shader juga digunakan.
Pencahayaan kartun atau bergaya. Dalam banyak permainan, dari sudut pandang gaya, bukan pencahayaan yang terlihat jauh lebih menarik, tetapi gaya. Pada saat yang sama, tidak masuk akal untuk menghitung apa pun di bagian fragmen.
Menguliti. Pada saat tertentu di mesin game masalah ini diselesaikan, tetapi bagaimanapun, berguna untuk memahami vertex shader untuk memahami cara kerjanya.
Contoh sederhana bekerja dengan vertex

Saya tidak ingin itu terjadi, seperti dalam pelajaran lama tentang cara menggambar burung hantu, jadi mari kita secara bertahap. Buat shader permukaan standar. Ini dapat dilakukan dengan tombol kanan mouse di Project View atau di panel atas pada tab Assets. Buat-> Shader-> Standard Surface Shader.
Dan kami mendapatkan standar yang kosong.
Permukaan shaderShader "Custom/SimpleVertexExtrusionShader"
{
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
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
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;
}
ENDCG
}
FallBack "Diffuse"
}
Cara kerjanya dan secara umum kami akan menganalisisnya secara terperinci dalam artikel setelah praktik dasar, ditambah lagi kami akan memahaminya secara parsial selama implementasi shader. Untuk saat ini, biarkan beberapa hal tetap seperti yang diberikan. Singkatnya, tidak ada keajaiban (dalam hal bagaimana parameter dihubungkan, dll.) Hanya untuk kata kunci tertentu, unit menghasilkan kode untuk Anda, sehingga tidak menuliskannya dari awal. Karena itu, proses ini tidak cukup jelas. Anda dapat membaca lebih lanjut tentang shader permukaan dan propertinya di Unity di sini.
docs.unity3d.com/Manual/SL-SurfaceShaders.htmlKami akan menghapus semua yang tidak perlu dari itu sehingga tidak mengganggu, karena pada saat tertentu tidak diperlukan. Dan dapatkan shader pendek.
Shader sederhanaShader "Custom/SimpleVertexExtrusionShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
struct Input
{
float4 color : COLOR;
};
fixed4 _Color;
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = _Color;
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}

Hanya warna pada model dengan pencahayaan. Dalam hal ini, Unity bertanggung jawab untuk menghitung pencahayaan.
Pertama, tambahkan efek paling sederhana dari contoh Unity. Ekstrusi itu normal, dan pada contohnya kita akan menganalisis cara kerjanya.
Untuk melakukan ini, tambahkan
vertex: vert modifier ke
#pragma surface surf Standard fullforwardshadows line. Jika kita melewatkan
appdata_full v sebagai parameter untuk suatu fungsi, maka pada dasarnya fungsi ini adalah pengubah titik. Pada intinya, ini adalah bagian dari shader vertex, yang dibuat oleh unit penghasil kode, yang melakukan pemrosesan awal simpul. Juga di blok
Properties , tambahkan bidang
_Amount yang menerima nilai dari 0 hingga 1. Untuk menggunakan bidang
_Amount di shader, kita juga perlu mendefinisikannya di sana. Dalam fungsi ini, kita hanya akan beralih ke normal tergantung pada
_Amount , di mana 0 adalah posisi vertex standar (nol shift), dan 1 adalah pergeseran tepat ke normal.
SimpleVertexExtrusionShaderShader "Custom/SimpleVertexExtrusionShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_Amount ("Extrusion Amount", Range(0,1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows vertex:vert
#pragma target 3.0
struct Input
{
float4 color : COLOR;
};
fixed4 _Color;
float _Amount;
void vert (inout appdata_full v)
{
v.vertex.xyz += v.normal * _Amount;
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = _Color;
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
Anda dapat melihat fitur penting dari shader. Meskipun shader dieksekusi setiap frame, hasil yang diperoleh selama operasi shader tidak disimpan dalam mesh, tetapi digunakan hanya untuk rendering. Oleh karena itu, tidak mungkin untuk menghubungkan ke fungsi shader, serta
pembaruan dalam skrip. Mereka diterapkan setiap frame tanpa mengubah data mesh, tetapi hanya memodifikasi mesh untuk rendering lebih lanjut.
Misalnya, salah satu cara termudah untuk membuat animasi adalah menggunakan waktu untuk mengubah amplitudo. Di unit ada variabel
bawaan daftar lengkap yang dapat ditemukan di sini
docs.unity3d.com/Manual/SL-UnityShaderVariables.html Dalam hal ini, kami akan menulis shader baru berdasarkan shader masa lalu kami. Alih-alih
_Amount, mari kita buat nilai float
_Amplitude dan gunakan variabel Unity
_SinTime bawaan .
_SinTime adalah sinus waktu, dan oleh karena itu dibutuhkan nilai dari -1 hingga 1. Namun, jangan lupa bahwa semua variabel waktu
bawaan di unit shader adalah vektor
float4 . Misalnya,
_SinTime didefinisikan sebagai
(sin (t / 8), sin (t / 4), sin (t / 2), sin (t)) , di mana t adalah waktunya. Karenanya, kami mengambil komponen z sehingga animasinya lebih cepat. Dan kita mendapatkan:
SimpleVertexExtrusionWithTimeShader "Custom/SimpleVertexExtrusionWithTime"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_Amplitude ("Extrusion Amplitude", float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows vertex:vert
#pragma target 3.0
struct Input
{
float4 color : COLOR;
};
fixed4 _Color;
float _Amplitude;
void vert (inout appdata_full v)
{
v.vertex.xyz += v.normal * _Amplitude * (1 - _SinTime.z);
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = _Color;
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
Jadi ini adalah contoh sederhana. Saatnya menggambar burung hantu!
Deformasi benda
Saya sudah menulis seluruh artikel tentang satu efek deformasi dengan analisis terperinci dari matematika proses dan logika pemikiran ketika mengembangkan efek seperti
habr.com/en/post/435828 Ini akan menjadi burung hantu kita.
Semua shader dalam artikel ditulis dalam hlsl. Bahasa ini sebenarnya memiliki dokumentasi voluminous sendiri, yang banyak orang lupa dan bertanya-tanya dari mana setengah fungsi kabel berasal, meskipun mereka didefinisikan dalam HLSL
docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl- fungsi intrinsikNamun pada kenyataannya, permukaan shader dalam sebuah unit adalah topik yang besar dan produktif. Plus, Anda tidak selalu ingin dipusingkan dengan pencahayaan Unity. Terkadang Anda perlu menipu dan menulis shader tercepat yang hanya memiliki set efek yang telah ditentukan sebelumnya. Dalam kesatuan, Anda dapat menulis shader tingkat rendah.
Shader tingkat rendah

Menurut tradisi lama yang baik bekerja dengan shader, selanjutnya kita akan menyiksa kelinci Stanford.
Secara umum, yang disebut Unity ShaderLab pada dasarnya adalah visualisasi seorang inspektur dengan bidang-bidang dalam materi dan beberapa penyederhanaan penulisan shader.
Ambil struktur umum shaderlab shader:
Struktur shader umumShader "MyShaderName"
{
Properties
{
//
}
SubShader // ( )
{
Pass
{
//
}
//
}
//
FallBack "VertexLit" // ,
}
Arahan kompilasi seperti
#pragma vertex vertfragmen #pragma fragmententukan fungsi shader mana yang akan dikompilasi sebagai vertex dan fragmen shader, masing-masing.
Mari kita ambil salah satu contoh paling umum - shader untuk menampilkan warna normals:
SimpleNormalVisualizationShader "Custom/SimpleNormalVisualization"
{
Properties
{
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.normal * 0.5 + 0.5;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4 (i.color, 1);
}
ENDCG
}
}
FallBack "VertexLit"
}

Dalam kasus ini, di bagian simpul, kami menulis nilai normal yang dikonversi ke warna simpul, dan di bagian piksel kami menggunakan warna ini sebagai warna model.
Fungsi
UnityObjectToClipPos adalah fungsi bantu Unity (dari file
UnityCG.cginc ) yang menerjemahkan simpul objek ke posisi yang terkait dengan kamera. Tanpanya, suatu objek, ketika memasuki visibilitas kamera (frustrum), akan ditarik dalam koordinat layar, terlepas dari posisi transformasi. Sejak awalnya posisi simpul disajikan dalam koordinat objek. Nilai hanya relatif terhadap porosnya.
Blok ini.
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
Ini adalah definisi dari struktur yang akan diproses pada bagian verteks dan dipindahkan ke bagian fragmen. Dalam hal ini, ditentukan bahwa dua parameter diambil dari mesh - posisi titik dan warna titik. Anda dapat membaca lebih lanjut tentang data apa yang dapat dilemparkan ke dalam unit di tautan ini
docs.unity3d.com/Manual/SL-VertexProgramInputs.htmlKlarifikasi penting. Nama-nama atribut mesh tidak penting. Yaitu, katakanlah dalam atribut warna Anda dapat menulis vektor penyimpangan dari posisi semula (dengan cara ini mereka kadang-kadang membuat efek ketika karakter berjalan sehingga rumput "menolak" darinya). Bagaimana atribut ini akan diproses sepenuhnya tergantung pada shader Anda.
Kesimpulan
Terima kasih atas perhatian anda! Sangatlah sulit untuk menulis beberapa efek kompleks tanpa bagian yang terpisah-pisah, untuk alasan ini kita akan membahas hal serupa dalam artikel terpisah. Saya berharap selama artikel ini menjadi sedikit lebih jelas bagaimana kode untuk vertex shader ditulis secara umum, dan di mana Anda dapat menemukan informasi untuk dipelajari, karena shader adalah topik yang sangat mendalam.
Dalam artikel mendatang, kami akan menganalisis jenis shader lain, efek individu, dan saya akan mencoba menggambarkan logika pemikiran saya saat membuat efek baru atau kompleks.
Sebuah repositori juga telah dibuat di mana semua hasil dari seri artikel ini
github.com/Nox7atra/ShaderExamples akan ditambahkan. Saya harap informasi ini akan berguna bagi pemula yang baru memulai perjalanan mereka dalam mempelajari topik ini.
Beberapa tautan bermanfaat (termasuk sumber):
www.khronos.org/opengl/wiki/Vertex_Shaderdocs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-referencedocs.unity3d.com/en/current/Manual/SL-Reference.htmldocs.unity3d.com/Manual/GraphicsTutorials.htmlwww.malbred.com/3d-grafika-3d-redaktory/sovremennaya-terminologiya-3d-grafiki/vertex-shader-vershinnyy-sheyder.html3dpapa.ru/accurate-displacement-workflow