零基礎入門Unity Shader(八)

前言

上篇我們討論了渲染管線的大概流程,以及在Unity的ShaderLab中如何進行對應,其中主要是關於從應用程序階段傳遞數值到頂點著色器,然後又經頂點著色器將數據傳遞給片斷著色器並最終顯示在屏幕上。那麼這篇就來主要討論下這些數據具體有哪些,以及如何對它們進行定義。


應用階段傳入頂點著色器的數據

當應用階段的數據傳過來時,頂點著色器怎麼知道誰是模型的頂點數據誰又是模型的法線數據呢?所以我們需一種方式來告拆計算機我們定義的變數代表著什麼。

struct appdata
{
float4 vertex : POSITION;
};

這裡我們聲明了一個float4類型的變數vertex,並給予了它頂點數據的語義(在變數後加冒號並跟一個語義),也就是說vertex變數將代表著模型的頂點數據被我們使用與傳遞。

那麼都有哪些語義呢?如下:

struct appdata
{
float4 vertex : POSITION; //頂點
float4 tangent : TANGENT; //切線
float3 normal : NORMAL; //法線
float4 texcoord : TEXCOORD0; //UV1
float4 texcoord1 : TEXCOORD1; //UV2
float4 texcoord2 : TEXCOORD2; //UV3
float4 texcoord3 : TEXCOORD3; //UV4
fixed4 color : COLOR; //頂點色
};

仔細觀察,這些正好也是模型在Unity中的所有數據信息。

在UnityCG.cginc中內置定義了三種常用的結構,我們也可以直接引用並調用,有關cginc的具體用法我們後面再討論,這裡為了熟悉學習,我們盡量自己定義結構。


頂點著色器到片斷著色器的數據

頂點著色器在處理完應用階段傳過來的數據後,會需要輸出並傳入片斷著色器,這個時候我們同樣需要定義一個結構來承載其中的數據,同樣的,輸出給片斷著色器的值也需要語義來標識。

struct v2f
{
float4 pos:SV_POSITION;
};

這裡聲明了float4類型的變數pos,並指定為SV_POSITION語義,表示pos就是頂點著色器輸出的屏幕裁剪空間下的頂點位置。這條語義是必須要有的,否則GPU無法進行接下來的光柵化處理。

其實在現代GPU上對這裡的語義如何定義並不關心了(除了SV_POSITION以外),主要是部分OpenGL ES2.0上面才需要特別注意而已。

這裡的語義除了SV_POSITION以外,我們還有另外兩種選擇:

  • TEXCOORD0~N系列

例如TEXCOORD0、TEXCOORD1、TEXCOORD2...等等,主要用於高精度數據。

COLOR0~N系列

例如COLOR0、COLOR1、COLOR2...等等,主要用於低精度數據。

雖然這兩種語義我們可以根據需要自由定義,但是,它並不是可以無限定義的,不同的GPU硬體有不同的數量限制。

以下為手機平台的常見規則:

  • OpenGL ES2.0支持最多8個
  • OpenGL ES3.0支持最多16個

從性能優化角度來講,數量越少性能越好。

另外,每個語義是4維向量,利用好這一點,可以大大節省總數量。

VFACE

在片斷著色器中還有些特殊的語義的可以識別,比如VFACE,效果如下:

如果渲染表面朝向攝像機,則Face節點輸出正值1,如果遠離攝像機,則輸出負值-1。

完整代碼如下:

Shader "Unlit/MyFirstShader"
{
Properties
{
_FrontTex("FrontTex", 2d) = "white"{}
_BackTex("BackTex", 2d) = "white"{}
}

SubShader
{
cull off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0

sampler2D _FrontTex;
sampler2D _BackTex;

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

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

v2f vert (appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.uv=v.texcoord;
return o;
}

fixed4 frag (v2f i,float face:VFACE) : SV_Target
{
fixed4 col=1;
col = face > 0 ? tex2D(_FrontTex,i.uv) : tex2D(_BackTex,i.uv);
return col;
}
ENDCG
}
}
}

另外比較特殊的還有SV_VertexID和VPOS,有興趣的可以自行查閱下相關資料,由於這些不常用,在入門篇章內就暫且先不討論了。


片斷著色器輸出相關語義

通常情況下,片斷著色器最終只需返回一個顏色值即可,也是我們最常見到的編寫方式,如下:

fixed4 frag (v2f i ) : SV_TARGET

這裡的SV_TARGET就是指定輸出顏色到RenderTarget的語義,其實我們也可以採用Struct的方式,就是像應用階段到頂點與頂點到片斷一樣,只是由於平時我們只需返回一個顏色所以就無需再用一個Struct了(你非要用也是可以的)。

當我們利用Struct時,就可以通過下列語義來輸出多個內容:

  • SV_Target0?N

默認SV_TARGET0,也就是SV_TARGET,還有SV_TARGET1,SV_TARGET2...這個在需要輸出多個RenderTarget時很有用。

  • SV_Depth

一般情況下,模型的像素深度值在光柵化時會自動插值計算得出,並不需要我們做額外的處理,但這並不代表不可以修改它,通過在片斷著色器中輸出SV_DEPTH語義可以更改像素的深度值。

注意此功能相對會消耗性能,在沒有特別需求的情況下盡量不要用!


ShaderReference

v0.03版同步更新,需要請進QQ群自取.


最後

歡迎大家關注更多乾貨的

公眾號:Unity技術美術 ( ID:gh_8b69cca044dc )

Unity技術美術QQ交流分享群:19470667


推薦閱讀:

TAG:Unity(遊戲引擎) | shader | 技術美術 |