Contoh naungan untuk Pipa Render Built-in
Visualizing vertex data

Dasar warna kustom

Ini contoh naungan untuk built-in Render PipelineRangkaian operasi yang mengambil isi dari Adegan, dan menampilkannya di layar. Unity memungkinkan Anda memilih dari pipa render yang dibangun sebelumnya, atau menulis sendiri. More info
Lihat di Glossary
menunjukkan dasar-dasar menulis warna kustom, dan menutupi kasus penggunaan umum.

Untuk informasi tentang naungan menulis, lihat Menu Menu.

Menyiapkan adegan

Langkah pertama adalah membuat beberapa objek yang akan Anda gunakan untuk menguji naungan Anda. Pilih Game Object > 3D ObjectPermainan 3D Objek seperti kubus, terrain atau ragdoll. More info
Lihat di Glossary
> Capsule di menu utama. Kemudian posisi kamera sehingga menunjukkan kapsul. Klik dua kali Kapsul di Hierarchy untuk memfokuskan pemandangan di atasnya, lalu pilih objek Main Camera dan klik Game object > Align with View dari menu utama.

Buat Material baru dengan memilih Create > MaterialAset yang menentukan bagaimana permukaan harus diberikan. More info
Lihat di Glossary
dari menu di Tampilan Proyek. Bahan baru yang disebut Bahan Baru akan muncul di Tampilan Proyek.

Membuat naungan

Sekarang buat aset Shader baru dengan cara yang sama. Pilih Create > ShaderProgram yang berjalan di GPU. More info
Lihat di Glossary
> Unlit Shader dari menu di Tampilan Proyek. Ini menciptakan naungan dasar yang hanya menampilkan tekstur tanpa pencahayaan.

Entri lain dalam menu Create > Shader menciptakan warna tulang telanjang atau jenis lainnya, misalnya permukaan naunganCara merampingkan naungan menulis untuk Pipeline Render Built-in. More info
Lihat di Glossary
dasar.

Menyiapkan mesh, bahan dan naungan

Membuat bahan menggunakan naungan melalui bahan inspectorJendela Unity yang menampilkan informasi tentang Pengaturan GameObject yang dipilih saat ini, aset atau proyek, memungkinkan Anda untuk memeriksa dan mengedit nilai. More info
Lihat di Glossary
, atau hanya seret aset naungan atas aset material di Tampilan Proyek. Inspektur materi akan menampilkan bola putih ketika menggunakan naungan ini.

Sekarang seret materi ke objek meshGrafik utama primitif Unity. Mesh membuat sebagian besar dunia 3D Anda. Unity mendukung mesh poligon triangulat atau Quadrangulasi. Nurbs, Nurms, permukaan Subdiv harus dikonversi ke poligon. More info
Lihat di Glossary
Anda dalam pandangan SceneAdegan berisi lingkungan dan menu permainan Anda. Pikirkan setiap file Adegan unik sebagai tingkat yang unik. Di setiap Adegan, Anda menempatkan lingkungan, hambatan, dan dekorasi, pada dasarnya merancang dan membangun permainan Anda dalam potongan-potongan. More info
Lihat di Glossary
atau Hierarchy. Atau, pilih objek, dan dalam inspektur membuatnya menggunakan bahan dalam slot Bahan komponen Mesh RendererKomponen mesh yang mengambil geometri dari Filter Mesh dan render di posisi yang ditentukan oleh komponen Transform objek. More info
Lihat di Glossary
.

Dengan hal-hal ini, Anda sekarang dapat mulai melihat kode naungan, dan Anda akan melihat hasil dari perubahan Anda ke naungan pada kapsul di Scene ViewTampilan interaktif ke dunia yang Anda buat. Anda menggunakan Adegan Lihat untuk memilih dan posisi pemandangan, karakter, kamera, lampu, dan semua jenis lain dari Game Object. More info
Lihat di Glossary
.

Bagian utama dari naungan

Untuk memulai memeriksa kode naungan, klik dua kali aset naungan di Tampilan Proyek. Kode naungan akan terbuka di editor skrip Anda (MonoDevelop atau Visual Studio).

Naungan dimulai dengan kode ini:

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

Naungan awal ini tidak terlihat sangat sederhana! Tapi jangan khawatir, kita akan pergi ke setiap langkah demi langkah.

Mari kita lihat bagian utama dari naungan sederhana kami.

Shader

Perintah Shader mengandung string dengan nama naungan. Anda dapat menggunakan karakter bulu mata maju "/" untuk menempatkan naungan Anda dalam sub-menu ketika memilih naungan Anda dalam inspektur Material.

Properties

Blok Properties mengandung variabel naungan (teks, warna dll) yang akan disimpan sebagai bagian dari Bahan, dan ditampilkan dalam inspektur material. Dalam templat naungan kami, ada properti tekstur tunggal yang dinyatakan.

SubShader

A Shader dapat mengandung satu atau lebih SubShaders, yang terutama digunakan untuk menerapkan warna untuk kemampuan GPU yang berbeda. Dalam tutorial ini kita tidak peduli dengan itu, sehingga semua naungan kita akan mengandung hanya satu SubShader.

Pass

Setiap SubShader terdiri dari sejumlah passes, dan setiap Pass mewakili eksekusi simpul dan kode fragmen untuk objek yang sama yang diberikan dengan bahan naungan. Banyak naungan sederhana hanya menggunakan satu lulus, tetapi naungan yang berinteraksi dengan pencahayaan mungkin perlu lebih (lihat Pipa pencahayaan untuk rincian). Perintah di dalam Pass biasanya mengatur state fungsi tetap, misalnya mode campuran.

CGPROGRAM .. ENDCG

Kata kunci ini mengelilingi bagian kode HLSL dalam naungan simpul dan fragmen. Biasanya ini adalah di mana sebagian besar kode yang menarik. Lihat simpul dan naungan fragmen untuk rincian.

Sederhana teduh

Templat naungan yang tidak menyala melakukan beberapa hal lebih dari akan benar-benar diperlukan untuk menampilkan objek dengan tekstur. Sebagai contoh, mendukung kolom Fog, dan tekstur tiling/offset dalam materi. Mari kita menyederhanakan naungan untuk minimum, dan tambahkan lebih banyak komentar:

Shader "Unlit/SimpleUnlitTexturedShader"
{
    Properties
    {
        // we have removed support for texture tiling/offset,
        // so make them not be displayed in material inspector
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            // use "vert" function as the vertex shader
            #pragma vertex vert
            // use "frag" function as the pixel (fragment) shader
            #pragma fragment frag

            // vertex shader inputs
            struct appdata
            {
                float4 vertex : POSITION; // vertex position
                float2 uv : TEXCOORD0; // texture coordinate
            };

            // vertex shader outputs ("vertex to fragment")
            struct v2f
            {
                float2 uv : TEXCOORD0; // texture coordinate
                float4 vertex : SV_POSITION; // clip space position
            };

            // vertex shader
            v2f vert (appdata v)
            {
                v2f o;
                // transform position to clip space
                // (multiply with model*view*projection matrix)
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                // just pass the texture coordinate
                o.uv = v.uv;
                return o;
            }
            
            // texture we will sample
            sampler2D _MainTex;

            // pixel shader; returns low precision ("fixed4" type)
            // color ("SV_Target" semantic)
            fixed4 frag (v2f i) : SV_Target
            {
                // sample texture and return it
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

Vertex ShaderProgram yang berjalan pada setiap simpul model 3D ketika model sedang diberikan. More info
Lihat di Glossary
adalah program yang berjalan pada setiap simpul model 3D. Cukup sering tidak melakukan sesuatu yang sangat menarik. Di sini kita hanya mengubah posisi simpul dari ruang objek menjadi disebut "ruang klip", yang apa yang digunakan oleh GPU untuk menjebak objek di layar. Kami juga lulus koordinat tekstur input yang tidak dimodifikasi - kami akan perlu untuk sampel tekstur dalam naungan fragmen.

Fragment Shader adalah program yang berjalan pada setiap pixelUnit terkecil dalam gambar komputer. Ukuran piksel tergantung pada resolusi layar Anda. Pencahayaan pixel dihitung pada setiap piksel layar. More info
Lihat di Glossary
bahwa objek menempati layar, dan biasanya digunakan untuk menghitung dan output warna setiap pixel. Biasanya ada jutaan piksel di layar, dan naungan fragmen dieksekusi untuk semua dari mereka! Mengoptimalkan naungan fragmen cukup bagian penting dari pekerjaan kinerja permainan secara keseluruhan.

Beberapa definisi variabel atau fungsi diikuti oleh Semantic Signifier - misalnya : POSITION atau : SV_Target. Penandaan semantik ini mengkomunikasikan “meaning” dari variabel ini ke GPU. Lihat halaman shader semantics untuk rincian.

Ketika digunakan pada model yang bagus dengan tekstur yang bagus, naungan sederhana kami terlihat cukup bagus!

Lebih sederhana warna naungan tunggal

Mari kita menyederhanakan naungan lebih banyak – kita akan membuat naungan yang menarik seluruh objek dalam satu warna. Ini tidak sangat berguna, tapi hey kita belajar di sini.

Shader "Unlit/SingleColor"
{
    Properties
    {
        // Color property for material inspector, default to white
        _Color ("Main Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            // vertex shader
            // this time instead of using "appdata" struct, just spell inputs manually,
            // and instead of returning v2f struct, also just return a single output
            // float4 clip position
            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return mul(UNITY_MATRIX_MVP, vertex);
            }
            
            // color from the material
            fixed4 _Color;

            // pixel shader, no inputs needed
            fixed4 frag () : SV_Target
            {
                return _Color; // just return it
            }
            ENDCG
        }
    }
}

Kali ini bukannya menggunakan structs untuk input (appdata) dan output (v2f), fungsi naungan hanya mengeras input secara manual. Kedua cara kerja, dan mana Anda memilih untuk menggunakan tergantung pada gaya dan preferensi pengkodean Anda.

Menggunakan jala normal untuk menyenangkan dan keuntungan

Mari kita lanjutkan dengan naungan yang menampilkan jaring normal di ruang dunia. Tanpa ado lebih lanjut:

Shader "Unlit/WorldSpaceNormals"
{
    // no Properties block this time!
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // include file that contains UnityObjectToWorldNormal helper function
            #include "UnityCG.cginc"

            struct v2f {
                // we'll output world space normal as one of regular ("texcoord") interpolators
                half3 worldNormal : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            // vertex shader: takes object space normal as input too
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // UnityCG.cginc file contains function to transform
                // normal from object to world space, use that
                o.worldNormal = UnityObjectToWorldNormal(normal);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 c = 0;
                // normal is a 3D vector with xyz components; in -1..1
                // range. To display it as color, bring the range into 0..1
                // and put into red, green, blue components
                c.rgb = i.worldNormal*0.5+0.5;
                return c;
            }
            ENDCG
        }
    }
}

Selain menghasilkan warna yang cukup, normal digunakan untuk semua jenis efek grafis - pencahayaan, refleksi, siluet dan sebagainya.

Dalam naungan di atas, kita mulai menggunakan salah satu warna termasuk file bawaan Unity. Di sini, UnityCG.cginc digunakan yang mengandung fungsi praktis UnityObjectToWorldNormal. Kami juga telah menggunakan fungsi utilitas UnityObjectToClipPos, yang mengubah simpul dari ruang objek ke layar. Ini hanya membuat kode lebih mudah dibaca dan lebih efisien dalam keadaan tertentu.

Kami telah melihat bahwa data dapat dilewatkan dari simpul ke dalam naungan fragmen yang disebut “interpolators” (atau kadang disebut “varyings”). Dalam bahasa HLSL membentuk mereka biasanya dilabeli dengan semantik TEXCOORDn, dan masing-masing dari mereka dapat hingga vektor 4-komponen (lihat halaman semantics untuk rincian).

Juga kami telah mempelajari teknik sederhana dalam cara memvisualisasikan vektor ternormalisasi (dalam -1.0 hingga + 1.0 kisaran) sebagai warna: cukup banyak mereka dengan setengah dan tambahkan setengah. Untuk contoh visualisasi data yang lebih simpul, lihat Visualizaing vertex data.

Refleksi lingkungan menggunakan normal ruang dunia

Ketika SkyboxJenis khusus Bahan yang digunakan untuk mewakili langit. Biasanya enam sisi. More info
Lihat di Glossary
digunakan dalam adegan sebagai sumber refleksi (lihat Jendela pencahayaan), maka pada dasarnya adalah "default" Refleksi ProbeKomponen rendering yang menangkap pandangan bulat dari sekitarnya di semua arah, seperti kamera. Gambar yang ditangkap kemudian disimpan sebagai kubus yang dapat digunakan oleh benda dengan bahan reflektif. More info
Lihat di Glossary
diciptakan, mengandung data skybox. Probe refleksi adalah tekstur CubemapKoleksi enam tekstur persegi yang dapat mewakili refleksi di lingkungan atau skybox yang digambar di balik geometri Anda. Enam kotak membentuk wajah kubus imajiner yang mengelilingi objek; setiap wajah mewakili pandangan di sepanjang arah sumbu dunia (hingga, kiri, kanan, ke depan dan belakang). More info
Lihat di Glossary
internal; kami akan memperpanjang naungan normal ruang dunia di atas untuk melihat ke dalamnya.

Kode mulai mendapatkan sedikit yang terlibat sekarang. Tentu saja, jika Anda ingin naungan yang bekerja secara otomatis dengan lampu, bayangan, refleksi dan sisa sistem pencahayaan, caranya lebih mudah digunakan permukaan naungan. Contoh ini dimaksudkan untuk menunjukkan cara menggunakan bagian sistem pencahayaan dengan cara "manual".

Shader "Unlit/SkyReflection"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                half3 worldRefl : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // compute world space position of the vertex
                float3 worldPos = mul(_Object2World, vertex).xyz;
                // compute world space view direction
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                // world space normal
                float3 worldNormal = UnityObjectToWorldNormal(normal);
                // world space reflection vector
                o.worldRefl = reflect(-worldViewDir, worldNormal);
                return o;
            }
        
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the default reflection cubemap, using the reflection vector
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);
                // decode cubemap data into actual color
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                // output it!
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}

Contoh di atas menggunakan beberapa hal dari warna termasuk file bawaan:

  • unity_SpecCube0, unity_SpecCube0_HDR, Object2World, UNITY_MATRIX_MVP dari variabel naungan built-in. unity_SpecCube0 mengandung data untuk probe refleksi aktif.
  • UNITY_SAMPLE_TEXCUBE adalah makro built-in untuk sampel cubemap. Kebanyakan kubus biasa dinyatakan dan digunakan menggunakan sintaks HLSL standar (samplerCUBE dan texCUBE), namun kubus probe refleksi di Unity dinyatakan dengan cara khusus untuk menghemat slot sampler. Jika Anda tidak tahu apa yang, jangan khawatir, hanya tahu bahwa untuk menggunakan kubus unity_SpecCube0 Anda harus menggunakan makro UNITY_SAMPLE_TEXCUBE.
  • UnityWorldSpaceViewDir fungsi dari UnityCG.cginc, dan fungsi DecodeHDR dari file yang sama. Yang terakhir digunakan untuk mendapatkan warna sebenarnya dari data probe refleksi - karena toko Unity mencerminkan probe cubemap secara khusus dikodekan cara.
  • reflect hanya fungsi HLSL built-in untuk menghitung refleksi vektor sekitar yang diberikan normal.

Refleksi lingkungan dengan peta normal

Sering Normal MapsJenis tekstur Peta Bump yang memungkinkan Anda untuk menambahkan detail permukaan seperti benjolan, alur, dan goresan ke model yang menangkap cahaya seolah-olah mereka diwakili oleh geometri nyata.
Lihat di Glossary
digunakan untuk membuat detail tambahan pada objek, tanpa membuat geometri tambahan. Mari kita lihat bagaimana membuat naungan yang mencerminkan lingkungan, dengan tekstur peta normal.

Sekarang matematika mulai mendapatkan benar-benar terlibat, jadi kita akan melakukannya dalam beberapa langkah. Dalam naungan di atas, arah refleksi bersaing per-vertex (dalam naungan simpul), dan naungan fragmen hanya melakukan refleksi probe cubemap lookup. Namun setelah kita mulai menggunakan peta normal, permukaan normal itu sendiri perlu dihitung berdasarkan per-piksel, yang berarti kita juga harus memperhitungkan bagaimana lingkungan tercermin per-piksel!

Jadi pertama-tama, mari kita tuliskan naungan di atas untuk melakukan hal yang sama, kecuali kita akan memindahkan beberapa perhitungan ke naungan fragmen, sehingga mereka sebanding per-piksel:

Shader "Unlit/SkyReflection Per Pixel"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                float3 worldPos : TEXCOORD0;
                half3 worldNormal : TEXCOORD1;
                float4 pos : SV_POSITION;
            };

            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.worldPos = mul(_Object2World, vertex).xyz;
                o.worldNormal = UnityObjectToWorldNormal(normal);
                return o;
            }
        
            fixed4 frag (v2f i) : SV_Target
            {
                // compute view direction and reflection vector
                // per-pixel here
                half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                half3 worldRefl = reflect(-worldViewDir, i.worldNormal);

                // same as in previous shader
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl);
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}

Itu sendiri tidak memberi kita banyak - naungan terlihat persis sama, kecuali sekarang berjalan lebih lambat karena itu tidak lebih perhitungan untuk setiap piksel di layar, bukan hanya untuk setiap simpul model. Namun, kita akan membutuhkan perhitungan ini benar-benar segera. kesetiaan grafis yang lebih tinggi sering membutuhkan lebih banyak warna yang kompleks.

Kami harus belajar hal baru sekarang juga; yang disebut "sebelum bertanggung". Tekstur peta normal paling sering diekspresikan dalam ruang koordinat yang dapat dianggap sebagai “mengikuti permukaan” dari model. Dalam naungan kita, kita perlu tahu vektor dasar ruang tangen, membaca vektor normal dari tekstur, mengubahnya menjadi ruang dunia, dan kemudian melakukan semua matematika dari naungan di atas. Mari kita sampai ke sana!

Shader "Unlit/SkyReflection Per Pixel"
{
    Properties {
        // normal map texture on the material,
        // default to dummy "flat surface" normalmap
        _BumpMap("Normal Map", 2D) = "bump" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                float3 worldPos : TEXCOORD0;
                // these three vectors will hold a 3x3 rotation matrix
                // that transforms from tangent to world space
                half3 tspace0 : TEXCOORD1; // tangent.x, bitangent.x, normal.x
                half3 tspace1 : TEXCOORD2; // tangent.y, bitangent.y, normal.y
                half3 tspace2 : TEXCOORD3; // tangent.z, bitangent.z, normal.z
                // texture coordinate for the normal map
                float2 uv : TEXCOORD4;
                float4 pos : SV_POSITION;
            };

            // vertex shader now also needs a per-vertex tangent vector.
            // in Unity tangents are 4D vectors, with the .w component used to
            // indicate direction of the bitangent vector.
            // we also need the texture coordinate.
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL, float4 tangent : TANGENT, float2 uv : TEXCOORD0)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.worldPos = mul(_Object2World, vertex).xyz;
                half3 wNormal = UnityObjectToWorldNormal(normal);
                half3 wTangent = UnityObjectToWorldDir(tangent.xyz);
                // compute bitangent from cross product of normal and tangent
                half tangentSign = tangent.w * unity_WorldTransformParams.w;
                half3 wBitangent = cross(wNormal, wTangent) * tangentSign;
                // output the tangent space matrix
                o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x);
                o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y);
                o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z);
                o.uv = uv;
                return o;
            }

            // normal map texture from shader properties
            sampler2D _BumpMap;
        
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the normal map, and decode from the Unity encoding
                half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv));
                // transform normal from tangent to world space
                half3 worldNormal;
                worldNormal.x = dot(i.tspace0, tnormal);
                worldNormal.y = dot(i.tspace1, tnormal);
                worldNormal.z = dot(i.tspace2, tnormal);

                // rest the same as in previous shader
                half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                half3 worldRefl = reflect(-worldViewDir, worldNormal);
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl);
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}

Phew, yang cukup terlibat. Tapi lihat, refleksi mapped normal!

Menambahkan tekstur lebih

Mari kita tambahkan lebih banyak tekstur ke naungan yang normal, langit-reflektor di atas. Kami akan menambahkan tekstur warna dasar, terlihat dalam contoh unlit pertama, dan peta occlusion untuk mempergelar rongga.

Shader "Unlit/More Textures"
{
    Properties {
        // three textures we'll use in the material
        _MainTex("Base texture", 2D) = "white" {}
        _OcclusionMap("Occlusion", 2D) = "white" {}
        _BumpMap("Normal Map", 2D) = "bump" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            // exactly the same as in previous shader
            struct v2f {
                float3 worldPos : TEXCOORD0;
                half3 tspace0 : TEXCOORD1;
                half3 tspace1 : TEXCOORD2;
                half3 tspace2 : TEXCOORD3;
                float2 uv : TEXCOORD4;
                float4 pos : SV_POSITION;
            };
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL, float4 tangent : TANGENT, float2 uv : TEXCOORD0)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.worldPos = mul(_Object2World, vertex).xyz;
                half3 wNormal = UnityObjectToWorldNormal(normal);
                half3 wTangent = UnityObjectToWorldDir(tangent.xyz);
                half tangentSign = tangent.w * unity_WorldTransformParams.w;
                half3 wBitangent = cross(wNormal, wTangent) * tangentSign;
                o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x);
                o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y);
                o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z);
                o.uv = uv;
                return o;
            }

            // textures from shader properties
            sampler2D _MainTex;
            sampler2D _OcclusionMap;
            sampler2D _BumpMap;
        
            fixed4 frag (v2f i) : SV_Target
            {
                // same as from previous shader...
                half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv));
                half3 worldNormal;
                worldNormal.x = dot(i.tspace0, tnormal);
                worldNormal.y = dot(i.tspace1, tnormal);
                worldNormal.z = dot(i.tspace2, tnormal);
                half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                half3 worldRefl = reflect(-worldViewDir, worldNormal);
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl);
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);                
                fixed4 c = 0;
                c.rgb = skyColor;

                // modulate sky color with the base texture, and the occlusion map
                fixed3 baseColor = tex2D(_MainTex, i.uv).rgb;
                fixed occlusion = tex2D(_OcclusionMap, i.uv).r;
                c.rgb *= baseColor;
                c.rgb *= occlusion;

                return c;
            }
            ENDCG
        }
    }
}

Balon kucing terlihat bagus!

Texturing shader examples

Pola checkerboard prosedural

Ini adalah naungan yang menghasilkan pola checkerboard berdasarkan koordinat tekstur mesh:

Shader "Unlit/Checkerboard"
{
    Properties
    {
        _Density ("Density", Range(2,50)) = 30
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            float _Density;

            v2f vert (float4 pos : POSITION, float2 uv : TEXCOORD0)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(pos);
                o.uv = uv * _Density;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float2 c = i.uv;
                c = floor(c) / 2;
                float checker = frac(c.x + c.y) * 2;
                return checker;
            }
            ENDCG
        }
    }
}

Slider kepadatan dalam kontrol blok Properties bagaimana padat papan centang. Dalam naungan simpul, UV mesh dikalikan oleh nilai kepadatan untuk mengambil mereka dari kisaran 0 hingga 1 hingga kisaran 0 hingga kepadatan. Katakanlah kerapatan diatur ke 30 - ini akan membuat input i.uv ke dalam naungan fragmen mengandung nilai titik mengambang dari nol ke 30 untuk berbagai tempat jala yang diberikan.

Kemudian kode naungan fragmen hanya mengambil bagian bilangan bulat dari koordinat masukan menggunakan fungsi bawaan HLSL, dan membaginya dengan dua. Ingat bahwa koordinat masukan adalah angka dari 0 sampai 30; ini membuat mereka semua menjadi "kubis" untuk nilai 0, 0,5, 1, 1.5, 2, 2.5, dan sebagainya. Ini dilakukan pada komponen x dan y dari koordinat masukan.floor function, and divides it by two. Recall that the input coordinates were numbers from 0 to 30; this makes them all be “quantized” to values of 0, 0.5, 1, 1.5, 2, 2.5, and so on. This was done on both the x and y components of the input coordinate.

Selanjutnya, kami menambahkan koordinasi x dan y ini bersama-sama (masing-masing dari mereka hanya memiliki nilai yang mungkin 0, 0,5, 1, 1.5, ...) dan hanya mengambil bagian fraksional menggunakan fungsi HLSL bawaan lainnya, frac. Hasil dari ini hanya bisa 0,0 atau 0,5. Kami kemudian menggandakannya dengan dua untuk membuatnya baik 0,0 atau 1.0, dan output sebagai warna (hasil ini dalam warna hitam atau putih masing-masing).

Tri-planar texturing

Untuk mesh yang kompleks atau prosedural, bukan teks yang menggunakan koordinat UV biasa, kadang-kadang berguna untuk hanya tekstur “proyek” ke objek dari tiga arah primer. Ini disebut “tri-planar” texturing. Idenya adalah untuk menggunakan permukaan normal untuk berat tiga arah tekstur. Ini adalah naungan:

Shader "Unlit/Triplanar"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Tiling ("Tiling", Float) = 1.0
        _OcclusionMap("Occlusion", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                half3 objNormal : TEXCOORD0;
                float3 coords : TEXCOORD1;
                float2 uv : TEXCOORD2;
                float4 pos : SV_POSITION;
            };

            float _Tiling;

            v2f vert (float4 pos : POSITION, float3 normal : NORMAL, float2 uv : TEXCOORD0)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(pos);
                o.coords = pos.xyz * _Tiling;
                o.objNormal = normal;
                o.uv = uv;
                return o;
            }

            sampler2D _MainTex;
            sampler2D _OcclusionMap;
            
            fixed4 frag (v2f i) : SV_Target
            {
                // use absolute value of normal as texture weights
                half3 blend = abs(i.objNormal);
                // make sure the weights sum up to 1 (divide by sum of x+y+z)
                blend /= dot(blend,1.0);
                // read the three texture projections, for x,y,z axes
                fixed4 cx = tex2D(_MainTex, i.coords.yz);
                fixed4 cy = tex2D(_MainTex, i.coords.xz);
                fixed4 cz = tex2D(_MainTex, i.coords.xy);
                // blend the textures based on weights
                fixed4 c = cx * blend.x + cy * blend.y + cz * blend.z;
                // modulate by regular occlusion map
                c *= tex2D(_OcclusionMap, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

Perhitungan pencahayaan

Biasanya ketika Anda menginginkan naungan yang bekerja dengan pipa pencahayaan Unity, Anda akan menulis permukaan naungan. Ini tidak sebagian besar dari "penngkatan berat" untuk Anda, dan kode naungan Anda hanya perlu untuk menentukan sifat permukaan.

Namun dalam beberapa kasus Anda ingin memotong jalur naungan permukaan standar; baik karena Anda ingin hanya mendukung beberapa subset terbatas dari seluruh pipa pencahayaan untuk alasan kinerja, atau Anda ingin melakukan hal-hal kustom yang tidak cukup “standar pencahayaan”. Contoh berikut akan menunjukkan bagaimana untuk mendapatkan data pencahayaan dari simpul dan naungan fragmen manual. Melihat kode yang dihasilkan oleh naungan permukaan (melalui inspektur warna) juga merupakan sumber pembelajaran yang baik.

Pencahayaan diffuse sederhana

Hal pertama yang perlu kita lakukan adalah untuk menunjukkan bahwa naungan kita sebenarnya membutuhkan informasi pencahayaan yang dilewati. Unity's rendering pipa mendukung berbagai cara rendering; di sini kita akan menggunakan default forward renderingSebuah jalur rendering yang membuat setiap objek dalam satu atau lebih melewati, tergantung pada lampu yang mempengaruhi objek. Lampu sendiri juga diperlakukan berbeda dengan Rendering Maju, tergantung pada pengaturan dan intensitas mereka. More info
Lihat di Glossary
satu.

Kita akan mulai hanya mendukung satu arah cahaya. Meneruskan rendering dalam karya Unity dengan membuat lampu directional utama, ambient, lightmapsTekstur pra-render yang mengandung efek sumber cahaya pada objek statis di tempat kejadian. Lightmaps dilalui atas geometri adegan untuk menciptakan efek pencahayaan. More info
Lihat di Glossary
dan refleksi dalam satu lulus yang disebut ForwardBase. Dalam naungan, ini ditunjukkan dengan menambahkan pass tag: Tags {“LightMode”=“ForwardBase”}. Ini akan membuat data lampu arah dilewatkan ke naungan melalui beberapa variabel built-in.

Ini adalah naungan yang menyulitkan pencahayaan diffuse sederhana per simpul, dan menggunakan tekstur utama tunggal:

Shader "Lit/Simple Diffuse"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            // indicate that our pass is the "base" pass in forward
            // rendering pipeline. It gets ambient and main directional
            // light data set up; light direction in _WorldSpaceLightPos0
            // and color in _LightColor0
            Tags {"LightMode"="ForwardBase"}
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc" // for UnityObjectToWorldNormal
            #include "UnityLightingCommon.cginc" // for _LightColor0

            struct v2f
            {
                float2 uv : TEXCOORD0;
                fixed4 diff : COLOR0; // diffuse lighting color
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                // get vertex normal in world space
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                // dot product between normal and light direction for
                // standard diffuse (Lambert) lighting
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                // factor in the light color
                o.diff = nl * _LightColor0;
                return o;
            }
            
            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                // sample texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // multiply by lighting
                col *= i.diff;
                return col;
            }
            ENDCG
        }
    }
}

Ini membuat objek bereaksi terhadap arah cahaya - bagian-bagian yang menghadap cahaya diterangi, dan bagian-bagian yang menghadap tidak diterangi sama sekali.

Diffuse pencahayaan dengan ambient

Contoh di atas tidak mengambil pencahayaan ambient atau probe cahaya ke akun. Mari kita perbaiki ini! Ini ternyata kita bisa melakukan ini dengan menambahkan hanya satu baris kode. Kedua data ambient dan probe cahayaprobe cahaya menyimpan informasi tentang bagaimana cahaya melewati ruang di tempat kejadian Anda. Koleksi probe cahaya yang diatur dalam ruang tertentu dapat meningkatkan pencahayaan pada objek bergerak dan pemandangan LOD statis dalam ruang itu. More info
Lihat di Glossary
dilewatkan untuk naungan dalam bentuk Harmonik bulat, dan fungsi ShadeSH9 dari UnityCG.cginc% ruanginclude file melakukan semua pekerjaan mengevaluasinya, mengingat ruang dunia normal.

Shader "Lit/Diffuse With Ambient"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                fixed4 diff : COLOR0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diff = nl * _LightColor0;

                // the only difference from previous shader:
                // in addition to the diffuse lighting from the main light,
                // add illumination from ambient or light probes
                // ShadeSH9 function from UnityCG.cginc evaluates it,
                // using world space normal
                o.diff.rgb += ShadeSH9(half4(worldNormal,1));
                return o;
            }
            
            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                col *= i.diff;
                return col;
            }
            ENDCG
        }
    }
}

Naungan ini sebenarnya mulai terlihat sangat mirip dengan naungan Legacy Diffuse built-in!

Menerapkan pengecoran bayangan

Perayaan kami saat ini tidak dapat menerima atau melemparkan bayangan. Mari mengimplementasikan pengecoran bayangan terlebih dahulu.

Untuk melemparkan bayangan, naungan harus memiliki ShadowCaster Jenis lulus dalam setiap subshaders atau setiap fallback. The ShadowCaster pass digunakan untuk membuat objek ke bayangan peta, dan biasanya cukup sederhana - naungan simpul hanya perlu mengevaluasi posisi simpul, dan naungan fragmen cukup banyak tidak melakukan apa pun. bayangan hanyalah depth bufferSebuah toko memori yang memegang kedalaman nilai z setiap pixel dalam gambar, di mana nilai z adalah kedalaman untuk setiap piksel yang diberikan dari pesawat proyeksi. More info
Lihat di Glossary
, jadi bahkan output warna oleh warna fragmen tidak benar-benar penting.

Ini berarti bahwa untuk banyak naungan, umpan kas bayangan akan hampir persis yang sama (keberatan tak terbatas memiliki deformasi berbasis simpul kustom, atau memiliki cutout alfa / bagian semitransparent). Cara termudah untuk menariknya melalui perintah naungan UsePass:

Pass
{
    // regular lighting pass
}
// pull in shadow caster from VertexLit built-in shader
UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"

Namun kita belajar di sini, jadi mari kita lakukan hal yang sama “dengan tangan” agar bisa berbicara. Untuk kode yang lebih pendek, kami telah mengganti pass pencahayaan (“ForwardBase”) dengan kode yang hanya tidak bertekstur ambient. Di bawah ini, ada "ShadowCaster" lulus yang membuat objek mendukung pengecoran bayangan.

Shader "Lit/Shadow Casting"
{
    SubShader
    {
        // very simple lighting pass, that only does non-textured ambient
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                fixed4 diff : COLOR0;
                float4 vertex : SV_POSITION;
            };
            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                // only evaluate ambient
                o.diff.rgb = ShadeSH9(half4(worldNormal,1));
                o.diff.a = 1;
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                return i.diff;
            }
            ENDCG
        }

        // shadow caster rendering pass, implemented manually
        // using macros from UnityCG.cginc
        Pass
        {
            Tags {"LightMode"="ShadowCaster"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"

            struct v2f { 
                V2F_SHADOW_CASTER;
            };

            v2f vert(appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }

            float4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
    }
}

Sekarang ada selandang pesawat, menggunakan naungan Diffuse built-in biasa, sehingga kita dapat melihat bayangan kami bekerja (anggota, naungan kami saat ini tidak mendukung bayangan receiving namun!).

Kami telah menggunakan arahan #pragma multi_compile_shadowcaster. Hal ini menyebabkan naungan dikompilasi menjadi beberapa varian dengan makro preprocessor yang berbeda didefinisikan untuk setiap (lihat halaman beberapa varian warna untuk rincian). Ketika rendering ke bayangan, kasus lampu titik vs jenis cahaya lainnya membutuhkan sedikit kode naungan yang berbeda, itulah sebabnya arahan ini diperlukan.

Menerima bayangan

Menerapkan dukungan untuk menerima bayangan akan membutuhkan membandingkan pencahayaan dasar yang melewati beberapa varian, untuk menangani kasus “arah cahaya tanpa bayangan” dan “arah cahaya dengan bayangan” dengan benar. #pragma multi_compile_fwdbase directive (lihat beberapa varian warna untuk rincian). Bahkan itu lebih banyak: itu juga menyusun varian untuk jenis lightmap yang berbeda, EnlightenSistem pencahayaan oleh Geomerics yang digunakan dalam Unity untuk lightmapping dan untuk Mencerahkan Penerangan Global Realtime. More info
Lihat di Glossary
Realtime Global IlluminationKelompok teknik yang model pencahayaan langsung dan tidak langsung untuk memberikan hasil pencahayaan yang realistis.
Lihat di Glossary
pada atau off dll. Saat ini kita tidak perlu semua itu, jadi kita akan secara eksplisit melewatkan varian ini.

Kemudian untuk mendapatkan perhitungan bayangan yang sebenarnya, kami akan #include “AutoLight.cginc” shader include file dan menggunakan SHADOW_ COORDS, TRANSFER_SHADOW, SHADOW_ATTENUASI makro dari itu.

Ini adalah naungan:

Shader "Lit/Diffuse With Shadows"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            // compile shader into multiple variants, with and without shadows
            // (we don't care about any lightmaps yet, so skip these variants)
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
            // shadow helper functions and macros
            #include "AutoLight.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                SHADOW_COORDS(1) // put shadows data into TEXCOORD1
                fixed3 diff : COLOR0;
                fixed3 ambient : COLOR1;
                float4 pos : SV_POSITION;
            };
            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diff = nl * _LightColor0.rgb;
                o.ambient = ShadeSH9(half4(worldNormal,1));
                // compute shadows data
                TRANSFER_SHADOW(o)
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
                fixed shadow = SHADOW_ATTENUATION(i);
                // darken light's illumination with shadow, keep ambient intact
                fixed3 lighting = i.diff * shadow + i.ambient;
                col.rgb *= lighting;
                return col;
            }
            ENDCG
        }

        // shadow casting support
        UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    }
}

Lihat, kami memiliki bayangan sekarang!

Contoh warna lain

Fog

Shader "Custom/TextureCoordinates/Fog" {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            //Needed for fog variation to be compiled.
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct vertexInput {
                float4 vertex : POSITION;
                float4 texcoord0 : TEXCOORD0;
            };

            struct fragmentInput{
                float4 position : SV_POSITION;
                float4 texcoord0 : TEXCOORD0;
                
                //Used to pass fog amount around number should be a free texcoord.
                UNITY_FOG_COORDS(1)
            };

            fragmentInput vert(vertexInput i){
                fragmentInput o;
                o.position = UnityObjectToClipPos(i.vertex);
                o.texcoord0 = i.texcoord0;
                
                //Compute fog amount from clip space position.
                UNITY_TRANSFER_FOG(o,o.position);
                return o;
            }

            fixed4 frag(fragmentInput i) : SV_Target {
                fixed4 color = fixed4(i.texcoord0.xy,0,0);
                
                //Apply fog (additive pass are automatically handled)
                UNITY_APPLY_FOG(i.fogCoord, color); 
                
                //to handle custom fog color another option would have been 
                //#ifdef UNITY_PASS_FORWARDADD
                //  UNITY_APPLY_FOG_COLOR(i.fogCoord, color, float4(0,0,0,0));
                //#else
                //  fixed4 myCustomColor = fixed4(0,0,1,0);
                //  UNITY_APPLY_FOG_COLOR(i.fogCoord, color, myCustomColor);
                //#endif
                
                return color;
            }
            ENDCG
        }
    }
}
Contoh naungan untuk Pipa Render Built-in
Visualizing vertex data