Unity Shader 00 - 梳理 Unity Shader 的基本結構

drnkcff發表於2020-10-07

0x00 寫在前面

之前一直在閱讀 The Book of Shaders 一書,為什麼會開始寫 Unity Shader 呢?一方面,因為該書目前尚未完結,寫下此文時已閱讀到該書的最新章節;另一方面,也需要通過一些實踐來檢驗以及鞏固所學的知識。Unity 引擎提供的環境正好是一個不錯的媒介。

本文沒有完整可執行的 Shader 程式碼,只是簡單梳理一下 Unity Shader 的基本結構,為之後學習做鋪墊。

0x01 基本框架

一段 Unity Shader 的基本結構如下:

// 路徑及名字
Shader "Path/ShaderName"
{
    // 材質屬性
    Properties
    {

    }

    // 子著色器
    SubShader
    {
        [Tags]
        [RenderSetup]
        Pass
        {
            Name "PassName"
            [Tags]
            [RenderSetup]
            // ...
            // 頂點/片元著色器
        }
        // 更多 Pass 可選
    }
    // 更多 SubShader 可選
    Fallback "OtherShaderName" // 可選
}

0x02 名字

Path/ShaderName 決定該 Shader 在選擇皮膚上所處的位置,很像檔案的全路徑。例如,Shader "Path0/Path1/CustomShader" 將位於 Path0 → Path1 → CustomShader:

0x03 屬性

Properties 括號中定義的屬性將在材質皮膚上展示,可以方便地進行調節。

Properties
{
    Name("Display Name", Type) = DefaultValue
    // Other Properties
}

定義一個屬性,就像在 C 語言中定義一個變數一樣,只是格式稍有不同。在上面的程式碼中,Name 是屬性的名字,Display Name 是用於展示在材質皮膚上的名字,Type 是屬性的型別,DefaultValue 是屬性的預設值。ps:其中 Name 常常以下劃線開頭,例如 _PropertiesName。

下面列出一些常用的屬性:

數字

_Number0("Number 0", Range(-2.0, 2.0)) = 0.0
_Number1("Number 1", Float) = 0.618
_Number2("Number 2", Int) = 3

其中,Range 將在皮膚上展示一個滑動條,它的兩個引數依次表示可以滑動的最小值和最大值。Float 是浮點數,Int 是整數。

顏色和向量

_Color("Color", Color) = (1, 1, 1, 1)
_Vector("Vector", Vector) = (1, 1, 1, 1)

顏色 Color 的四個值分別表示 RGBA,在皮膚上將會展示修改按鈕以及吸取按鈕。向量 Vector 為四維向量,在皮膚上展示為 XYZW 四個格子。

紋理

_2DTexture("2D Texture", 2D) = "defaulttexture" {}
_Cube("Cube", Cube) = "defaulttexture" {}
_3DTexture("3D Texture", 3D) = "defaulttexture" {}

2D 表示 2D 紋理貼圖,Cube 表示立方體貼圖,3D 表示 3D 紋理貼圖。它們都有 Tiling 和 Offset。

在 Properties 中定義的屬性可以在 SubShader 的程式碼塊中使用,只需要在相應的地方定義與這些屬性名字相同、型別匹配的變數即可。SubShader 中的型別與 Properties 中的稍有不同,下面列出兩者對應的匹配關係:

  • Color 和 Vector 可對應 float4、half4、fixed4
  • Range 和 Float 可對應 float、half、fixed
  • 2D 對應 sampler2D
  • Cube 對應 samplerCUBE
  • 3D 對應 sampler3D

SubShader 程式碼中,有的屬性也可以不在 Properties 中定義,兩者都可以在執行時通過 C# 指令碼動態地進行修改(如,使用方法 Material.SetFloat 修改浮點屬性)。兩者的區別是,定義在 Properties 中的屬性在修改後將會被儲存,而不在 Properties 中的屬性則不會。

0x04 SubShader

每個 Unity Shader 中都可以包含多個 SubShader,當 Unity 展示一個 Mesh 時,將會從這些 SubShader 中找到第一個可以執行的來執行。因為不同的硬體對 Shader 的支援不同,這裡可以理解為

0x05 Tags

Tags 是 kv 結構,用來指定 Shader 怎樣以及何時渲染物件。

Tags { "TagName1" = "Value1" "TagName2" = "Value2" }

關於 Tags 的詳細說明可以檢視:ShaderLab: SubShader Tags

0X06 RenderSetup

RenderSetup 用來設定顯示卡的渲染狀態。比如,設定剔除模式。

Cull Back | Front | Off

關於渲染狀態的詳細設定可以檢視:ShaderLab: Pass

0x07 Pass

在 SubShader 中指定的 RenderSetup 將會應用到所有 Pass 中,我們也可以在 Pass 中單獨指定。Pass 中的 Tags 與 SubShader 中的有所不同,主要用於控制該 Pass 中的環境光、頂點照明等,詳見:ShaderLab: Pass Tags

Pass
{
    Name "PassName" // 可選
    [Tags]
    [RenderSetup]
    // ...
    // 頂點/片元著色器
}

當用 Name 為 Pass 指定名字後,可以在其它地方複用這個 Pass。其中,Pass 名需要大寫。用法如下:

UsePass "ShaderName/PASSNAME"

一個 SubShader 中可以有多個 Pass,這些 Pass 將會被依次執行。

0x08 Fallback

如果所有 SubShader 都無法使用時,將會嘗試使用 Fallback 指定的著色器。如:

Fallback "Diffuse"

0X09 總結

本文簡單梳理了一下 Unity Shader 的基本結構,因為了解不深,部分內容只能粗略帶過。這些內容在以後有需要時會再深入研究。

最後,再回顧一下 Unity Shader 的基本結構吧:

  • 每個 Shader 都需要定義名字,格式為:Shader "Path/ShaderName" {}。
  • 在 Shader 的括號中,如果有需要,我們可以在 Properties 語義塊中定義材質的屬性。
  • 每個 Shader 都包含多個子著色器 SubShader。使用多個 SubShader 是因為不同的硬體對 Shader 的支援有所不同。當例項執行時,引擎會幫我們找到第一個可以執行的 SubShader 來執行。當所有的 SubShader 都不能執行時,則使用 Fallback 指定的 Shader。
  • 每個 SubShader 包含多個 Pass,每個 Pass 表示一個渲染流程。因為有的效果需要幾個渲染才能表現出來,這時候就需要使用多個 Pass 了。
  • SubShader 和 Pass 都可以通過一些語義 (Tags 和 RenderType) 來設定渲染狀態,Pass 還可以通過:Name "PassName" 來指定 Pass 的名字。

參考資料:

相關文章