[閱讀筆記] Unity shader 入門精要 #2

《Unity shader 入門精要》的讀書筆記,五到八章。 這幾章說明基本的 shader,並以簡單的光照、貼圖、透明混色等實作進行基礎應用。

第五章:基本語法

  • 一段 Pass 的結構
1
2
3
4
5
6
7
8
Pass {
[Name][Tags][RenderSetup]
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// vert and frag function code
ENDCG
}
  • #pragma 用於指定兩種 shader 的 function name
  • Vertex shader 可用的傳入資料:
  • Vertex shader 可傳遞給 Fragment Shader 的資料
  • Fragment Shader 的輸出需要有 SV_Target
  • 在 Unity shader 定義的 properties 要在 CGPROGRAM 區塊中使用,需要再次宣告對應的型別、相同名稱的變數
  • UnityCG.cginc 有內建結構跟方法可以調用
  • 其他
    • #if UNITY_UV_START+AT_TOP #endif 可以進行平台判斷
    • 部分功能要用 #pragma target x.0 來指定版本,避免運行時錯誤

第六章:基礎光照

  • 一般光照模型分常四部分:自發光、環境光、漫射光、高光反射光。
  • 逐頂點光照效率較高,逐像素光照細節較佳。
  • 計算 world normal:normalize(mul(v.normal, (float3x3)_World2Object))
    • 使用 _Object2World 的轉置反矩陣運算,相當於用 _World2Object 進行左乘法。
  • 與光照相關的一些內建變數:
    • UNITY_LIGHTMODEL_AMBIENT 環境光變數 。
    • _LightColor0、_WorldSpaceLightPos0 與當下 Pass 相關的光照參數,需要加上對應的 LightMode Tag 才能使用
    • _WorldSpaceCameraPos 世界座標下的攝影機位置
  • 與光照相關的一些內建方法:
    • WorldSpaceViewDir 輸入物件空間下的頂點座標,取得頂點往攝影機的方向向量 (世界空間座標)
    • ObjSpaceViewDir 輸入物件空間下的頂點座標,取得頂點往攝影機的方向向量 (物件空間座標)
    • UnityWorldSpaceViewDir 輸入世界座標,取得該點往攝影機的方向向量 (世界空間座標)
    • UnityObjectToWorldNormal、UnityObjectToWorldDir、UnityWorldToObjectDir 轉換向量的所在座標空間,回傳值不為單位向量
    • WorldSpaceLightDir、ObjSpaceLightDir、UnityWorldSpaceLightDir 取得往光照的方向,不需要考慮光照類型

第七章:基礎紋理貼圖 (texture)

  • Unity uv 座標以左下為原點,右上為 (1, 1)。
  • 一個 2D shader property 可以對應到兩個 CGPROGRAM 變數
    • _MainTex ("Name", 2D) = "white" {} 對應
    • sampler2D _MainTexfloat4 _MainTex_ST
    • _MainTex_ST 用於縮放及平移,如:o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
    • 或者使用內建方法:o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
  • 凹凸映射/凹凸貼圖 Bump mapping:實現表面位移 (displacement) 的技術
    • 高度貼圖 height map
    • 法線貼圖 normal map
  • 漸變紋理貼圖:用一張漸變貼圖替代漫射光照的明暗,呈現不同的繪圖風格,此時貼圖採用 Clamp 設置。
  • 遮罩紋理貼圖:利用一張貼圖特定通道的資訊,對物體表面的渲染進行局部調整,例如:調整物體局部的反光效果。
  • 與紋理貼圖相關的一些內建變數:
    • TANGENT_SPACE_ROTATION 由物件座標旋轉至切線座標
  • 與紋理貼圖相關的一些內建方法:
    • TRANSFORM_TEX(texcoord, sampler2D) 貼圖 uv 的縮放及平移
    • tex2D (sampler2D, float2) 由貼圖中依照 uv 進行採樣,回傳一個 float4
    • UnpackNormal (flaot4) 將貼圖從 rbg 對應成 normal map 向量,Asset 設定須為 Normal map

第八章:透明處理

  • 使用 Alpha test 時,只會有完全透明與不透明兩種結果,但不會關閉深度寫入 (ZWrite),所以不影響深度測試 (Depth test) 檢查渲染順序。
    • 通常會用上 clip(x),x 有任何分量為負則捨棄渲染
  • 使用 Alpha blending 時,該物件的深度寫入 (ZWrite) 會被關閉,所以要對渲染順序進行控制。
  • Unity 定義了五個渲染順序 Queue 的值,數字越小的越早渲染
    • Background = 1000
    • Geomtry = 2000,預設值
    • AlphaTest = 2450
    • Transparent = 3000,透明混和 (Alpha blending) 應該為於此處
    • Overlay = 4000
  • 所以透明處理的 Shader 應如此設定:
1
2
3
4
5
6
7
8
9
10
// Alpha Test Subshader 
{
Tags { "Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout" }
Pass { }
}
// Alpha Blending Subshader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass { ZWrite Off Blend SrcAlpha OneMinusSrcAlpha }
}
  • Unity 的 Blend 命令:Result = SrcFactor * SrcColor + DstFactor * DstColor

  • 關閉深度寫入的透明效果 Shader 在比較複雜的模型上會出現呈現錯誤,可以加上一個額外的 Pass 在透明 shader 之前,**先 **進行深度寫入

    • ZWrite On 開啟深度寫入
    • ColorMask 0 關閉所有顏色通道
    • 不過依照我的理解,另外開啟 ZWrite 只是讓單一模型渲染正常,半透明模型之間的渲染還是會有機會出錯
  • 如果要讓模型呈現更好的透明效果,可能會需要開啟雙面渲染