Cg頂點程序必須在結構中傳遞頂點數據。幾種經常使用的頂點結構定義在文件UnityCG.cginc中,有以下三種結構體:android
一、appdata_base: 包含頂點位置,法線和一個紋理座標。
二、appdata_tan:包含頂點位置,切線,法線和一個紋理座標。
三、appdata_full:包含位置、法線、切線、頂點色和兩個紋理座標。ios
struct appdata_base { float4 vertex : POSITION; //頂點座標 float3 normal : NORMAL;//法線 float4 texcoord : TEXCOORD0;//UV }; struct appdata_tan { float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct appdata_full { float4 vertex : POSITION;//頂點座標 float4 tangent : TANGENT;//正切 float3 normal : NORMAL;//法線 float4 texcoord : TEXCOORD0;//第一層UV float4 texcoord1 : TEXCOORD1; //第二層UV fixed4 color : COLOR; //顏色 };
注:頂點座標和正切線爲何是float4,這有點意思,由於這裏它表示是齊次座標,好比咱們這樣表示一個float4(x,y,z,w),當w = 1的時候它表示點(x,y,z),當w= 0的時候它表示一個向量(x,y,z)。區別就在這裏,當W爲1時表示點,當W爲0時表示向量。app
texcoord0和texcoord1分別表示兩層UV,有時候咱們模型上的貼圖須要多個圖片一塊兒貼在一處,那麼貼兩層就會有兩層UV。函數
以上三種類型爲Unity內置的頂點Shader傳入結構體,若是想自定義也是能夠的,可是自定義結構體裏面的屬性,必須是基於appdata_full的,這樣才能識別出來,也就是自定義的結構裏的屬性必須到appdata_full裏的屬性裏去選。性能
下面給一個簡單的頂點/片元 shader 事例:優化
Shader "Custom/Example" { Properties { _MainTex ("Texture", 2D) = "white" { } //引號裏面的"Texture"則是Unity檢視面板中對應顯示的屬性名稱 } SubShader { pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; //_MainTex_ST的ST應該是SamplerTexture的意思 ,就是聲明_MainTex是一張採樣圖,也就是會進行UV運算。 //若是沒有這句話,是不能進行TRANSFORM_TEX的運算的。_MainTex_ST.xy爲 圖中的Tiling,zw爲圖中的offset. float4 _MainTex_ST; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; } ; v2f vert (appdata_base v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); //MVP矩陣變換,將裁剪空間座標轉換爲相對屏幕位置的UV座標 o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); //TRANSFORM_TEX的做用是用頂點的UV v.texcoord和材質球的採樣圖片_MainTex作運算,確保頂點材質球裏的縮放和偏移是正確的。等價於o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; return o; } float4 frag (v2f i) : COLOR { float4 texCol = tex2D(_MainTex,i.uv); float4 outp = texCol; return outp; } ENDCG } } }
其中SV_POSITION,SV_前綴的變量表明system value,在DX10之後的語義綁定中被使用表明特殊的意義,和POSITION用法並沒有不一樣。惟一區別是 SV_POSTION一旦被做爲vertex shader的輸出語義,那麼這個最終的頂點位置就被固定了(不能tensellate,不能再被後續改變它的空間位置?),直接進入光柵化處理,若是做爲fragment shader的輸入語義那麼和POSITION是同樣的,表明着每一個像素點在屏幕上的位置(這個說法其實並不許確,事實是fragment 在 view space空間中的位置,但直觀的感覺是如括號以前所述通常)
最後這個回答者說了,在DX10版本以前沒有引入SV_的預約義語義,POSITION被用做vertex shader的輸入,輸出,fragment shader的輸入參數。但DX10以後就推薦使用SV_POSITION做爲vertex shader的輸出和fragment shader的輸入了,注意vertex shader的輸入仍是使用POSITION!切記。可是DX10之後的代碼依舊兼容POSITION做爲全程表達,估計編譯器會自動判斷並替換的吧。spa
另外,給一點編寫shader的建議:.net
一、只計算須要計算的東西;
二、一般,須要渲染的像素比頂點數多,而頂點數又比物體數多不少。因此若是能夠,儘可能將運算從PS移到VS,或直接經過script來設置某些固定值;
三、在使用Surface Shader時,能夠經過一些指令讓shader優化不少。
一般狀況下,Surface shader的不少默認選項都是開啓的,以適應大多數狀況,可是不少時候,你能夠關閉其中的一些選項,從而讓你的shader運行的更快:
(1) approxview 對於使用了view direction的shader,該選項會讓view dir的normalize操做per-vertex進行,而不是per-pixel。這個優化一般效果明顯。
(2) halfasview 可讓Specular shader變得快一些,使用一個介於光照方向和觀察方向之間的half vector來代替真正的觀察方向viewDir來計算光照函數。
(3) noforwardadd Forward Render時,徹底只支持一盞方向光的per-pixel渲染,其他的光照所有按照per-vertex或SH渲染。這樣能夠確保shader在一個pass裏渲染完成。
(4) noambient 禁掉ambient lighting和SH lighting,可讓shader快一點兒。
四、浮點數精度相關:
float:最高精度,一般32位
half:中等精度,一般16位,-60000到60000,
fixed:最低精度,一般11位,-2.0到2.0,1/256的精度。
儘可能使用低精度。對於color和unit length vectors,使用fixed,其餘狀況,根據取值範圍儘可能使用half,實在不夠則使用float。
在移動平臺,關鍵是在fragment shader中儘量多的使用低精度數據。另外,對於多數移動GPU,在低精度和高精度之間轉換是很是耗的,在fixed上作swizzle操做也是很費事的。
五、Alpha Test
Alpha test和clip()函數,在不一樣平臺有不一樣的性能開銷。
一般使用它來cull那些徹底透明的像素。
可是,在ios和一些android上使用的PowerVR GPUs上面,alpha test很是的昂貴。
六、Color Mask
在移動設備上,Color Mask也是很是昂貴的,因此儘可能別使用它,除非真的是須要。code
最後推薦兩篇不錯的博客,orm