原文地址:http://bbs.9ria.com/thread-212557-1-1.html
此教程將指引你如何建立自己的Shaders,讓你的游戲場景看起來更好。Unity配備了強大的陰影和材料的語言工具 稱為ShaderLab,以程式語言來看,它類似於CgFX和Direct3D的語法,它不只紀錄基本的端點或者映像點(vertex/pixel)資 訊,也描述了材質所必要的一切資訊。
在unity材質檢視器中可以看到Shaders的性質及多重shader(SubShaders)的描述,針對不同圖 形硬件,每個描述也都完整的說明了圖形硬件的彩現狀態,fixed function pipeline如何設定、vertex/ fragment programs如何作用。 Vertex and fragment程序可以使用高階Cg程式語言或低階shader組合。
在這個教程中,我們將描述如何使用fixed function與programmable pipelines兩種方式於ShaderLab中撰寫shaders,我們假設讀者擁有基本的OpenGL或Direct3D彩現概念,並對cg有 fixed function與programmable pipelines的常識,HLSL或GLSL編程語言技術,一些Shader教程與參考文件可於NVIDIA以及AMD的開發站上取得。
建立一個新的shader有兩種方法,可以由菜單Assets->Create->Shader新增,或復制一個既有的shader再進行編輯,新的shader可以透過雙擊來啟動編輯畫面(UniSciTE)
下面開始介紹一個基礎的shader范例:
- Shader "Tutorial/Basic" {
- Properties {
- _Color ("Main Color", Color) = (1,0.5,0.5,1)
- }
- SubShader {
- Pass {
- Material {
- Diffuse [_Color]
- }
- Lighting On
- }
着色器:開始
- }
- }
- Shader "Tutorial/Basic" {
- Properties {
- _Color ("Main Color", Color) = (1,0.5,0.5,1)
- }
- SubShader {
- Pass {
- Material {
- Diffuse [_Color]
- }
- Lighting On
- }
- }
- }
這個shader范例只是眾多shader中最基本的一個,它定義了一個顏色性質,名稱為Main Color,並指定了玫瑰色的效果(red=100% green=50% blue=50% alpha=100%),在調用時會跳過Diffuse的材質設定(_Color)並開啟頂點光源。
要測試這個shader,你可以建立一個新的材質,並於Shader下拉菜單選擇(Tutorial->Basic),再把這個新材質指定到物件上,拖拉材質檢視器的顏色表並查看其變化。是時候研究更復雜的事情了!
假如你開啟一個既有的復合shader,剛開始看可能會覺得有點難,在開始以前,我們將詳細說明unity內建的VertexLit shader。這個shader使用fixed function pipeline產生標准的per-vertex照明。
- Shader "VertexLit" {
- Properties {
- 2
- _Color ("Main Color", Color) = (1,1,1,0.5)
- _SpecColor ("Spec Color", Color) = (1,1,1,1)
- _Emission ("Emmisive Color", Color) = (0,0,0,0)
- _Shininess ("Shininess", Range (0.01, 1)) = 0.7
- _MainTex ("Base (RGB)", 2D) = "white" { }
- }
- SubShader {
- Pass {
- Material {
- Diffuse [_Color]
- Ambient [_Color]
- Shininess [_Shininess]
- Specular [_SpecColor]
- Emission [_Emission]
- }
- Lighting On
- SeperateSpecular On
- SetTexture [_MainTex] {
- constantColor [_Color]
- Combine texture * primary DOUBLE, texture * constant
- }
- }
- }
- }
- Shader "VertexLit" {
- Properties {
- _Color ("Main Color", Color) = (1,1,1,0.5)
- _SpecColor ("Spec Color", Color) = (1,1,1,1)
- 3
- _Emission ("Emmisive Color", Color) = (0,0,0,0)
- _Shininess ("Shininess", Range (0.01, 1)) = 0.7
- _MainTex ("Base (RGB)", 2D) = "white" { }
- }
- SubShader {
- Pass {
- Material {
- Diffuse [_Color]
- Ambient [_Color]
- Shininess [_Shininess]
- Specular [_SpecColor]
- Emission [_Emission]
- }
- Lighting On
- SeperateSpecular On
- SetTexture [_MainTex] {
- constantColor [_Color]
- Combine texture * primary DOUBLE, texture * constant
- }
- }
- }
- }
所有的shaders都必須以Shader作為開始,接着是這個shader的名稱(例如:VertexLit),這個名稱將會顯示於檢視器(Inspector)。所有的語法都必須放在{ }之內。
如果要把shaders放在unity的submenus下面,請使用斜線,例如:MyShaders/Test,你將 會看到有個submenu名為MyShaders,下面有個shader名為Test,或是像這樣MyShaders->Test在 Properties block下面接着的是SubShader block,每個描述都在這個段落中
Properties
properties位於shader block一開始的位置,你可以定義任何性質,這些性質將可在材質檢視器中編輯,在VertexLit的個范例中,properties block看起來像這樣:
properties block內的語法都是單行的,每一個性質描述都由內名稱開始(例如:Color,MainTex),在后方的括弧號中所顯示的名字也會顯示於inspector檢視器上,在此之后,描述的是該性質的預設值:
可用的性質類型請參考Properties Reference。預設值與性質有關,以color為例,預設值應該由四個值組成。
現在我們已經定義了四個性質,可以開始撰寫實際的shader了在開始以前,先了解shader的結構是如何定義的。
不同的繪圖卡有不同的能力,例如:有的繪圖卡支援fragment programs但有些沒有,有些可以一次處理四個貼圖?(four textures)其他的可能只能處理兩個或一個,為了要符合所有用戶的硬體需求,一個shader可以包涵多個SubShaders,當unity在運 算shader時,它將詳細察看所有的subshaders而且使用硬體可支持的第一個。
- Shader "Structure Example" {
- Properties { /* ...shader properties... }
- SubShader {
- // ...subshader that uses vertex/fragment programs...
- }
- SubShader {
- // ...subshader that uses four textures per pass...
- }
- SubShader {
- // ...subshader that uses two textures per pass...
- }
- SubShader {
- // ...subshader that might look ugly but runs on anything : )
- }
- }
- Shader "Structure Example" {
- Properties { /* ...shader properties... }
- SubShader {
- // ...subshader that uses vertex/fragment programs...
- }
- SubShader {
- // ...subshader that uses four textures per pass...
- }
- SubShader {
- // ...subshader that uses two textures per pass...
- }
- SubShader {
- // ...subshader that might look ugly but runs on anything : )
- 6
- }
- }
此系統提供unity可以支援現有所有的硬體並取得最佳的品質。它作到了,然而,結果是必須撰寫很長的shaders語法在每一個SubShader block,你可以設定彩現途徑的狀態;並定義彩現途徑本身。完整的SubShader語法請參照SubShader Reference章節
每個subshader等於是一個途徑集。要對幾何物件進行彩現,至少一定要有一個途徑,內定的VertexLit shader里面僅有一個途徑:
- view plaincopy to clipboardprint?
- // ...snip...
- Pass {
- Material {
- Diffuse [_Color]
- Ambient [_Color]
- Shininess [_Shininess]
- Specular [_SpecColor]
- Emission [_Emission]
- }
- Lighting On
- SeperateSpecular On
- SetTexture [_MainTex] {
- constantColor [_Color]
- Combine texture * primary DOUBLE, texture * constant
- }
- }
- // ...snip...
- // ...snip...
- 7
- Pass {
- Material {
- Diffuse [_Color]
- Ambient [_Color]
- Shininess [_Shininess]
- Specular [_SpecColor]
- Emission [_Emission]
- }
- Lighting On
- SeperateSpecular On
- SetTexture [_MainTex] {
- constantColor [_Color]
- Combine texture * primary DOUBLE, texture * constant
- }
- }
- // ...snip...
通過指令可以定義一個特殊的方法,用來驅動繪圖硬體彩現指定的幾何物件
例如:上方語法中有一個Material block,定義了照明時所需要幾項固定參數。而指令LightingOn用來開啟該照明設備,SeperateSpecular On則是啟用Seperate作為特殊鏡射效果。到目前為止的所有命令,皆屬於支援OpenGL/Direct3D技術硬體本身可使用的固定功能,您可以 參考OpenGL紅皮書,可以找到更多相關資料。
下一個命令是SetTexture,這是個非常重要的命令,這個命令可以定義影像紋理如何混合、組合以及如何運用於我們 的彩現環境里,SetTexture通常跟隨於紋理的屬性名稱之后(我們在這里使用_MainTex ),接下來的combiner block也是定義紋理的應用方式,這個combiner block的命令會在螢幕顯示每一個被執行的動作。
讓我們開始了一個着色器的一般結構小回顧:
- Shader "MyShaderName" {
- Properties {
- // ... properties here ...
- }
- SubShader {
- // ... subshader for graphics hardware A ...
- Pass {
- // ... pass commands ...
- }
- // ... more passes if needed ...
- }
- SubShader {
- // ... subshader for graphics hardware B ...
- }
- // ... Optional fallback ...
- FallBack "VertexLit"
- }
最后,我們在這里引入一個新的命令:
FallBack "VertexLit"
回退命令可用於在着色結束,它告訴它應該使用着色如果從目前的着色沒有SubShaders可以運行在用戶的圖形硬件。其效果是一樣的,包括在年底從后備 着色所有SubShaders相同。例如,如果你寫了顛簸映射着色,然后不要寫成一個非常基本的非凹凸映射的老顯卡你可以回退到subshader內置 VertexLit着色。
該着色的基本構建塊,介紹了在第一渲染教程,而性能完整的文件,SubShaders並通過提供。阿建設 SubShaders快速方法是使用證在其他着色器定義。該命令UsePass正是如此,所以你可以重用一個整潔時尚着色器代碼。作為一個例子下面的命令 使用的名稱,通過“基地”從內置的鏡面着色:
- UsePass "Specular/BASE"
着色:頂點和片段程序
為了UsePass為了工作,這個名字必須考慮的一個傳遞希望使用。通過名稱內的命令給它一個名字:
- Name "MyPassName"
頂點和片段方案
我們描述了通過該只使用一個單一的紋理相結合的第一個教程指令。現在是時候證明我們如何能夠通過使用我們的頂點和片段方案。
當您使用頂點和片段程序(即所謂“可編程管線”),大部分的硬編碼功能(“固定功能管線”)在圖形硬件已關閉。例如,使用一個頂點程序關閉標准3D變換, 燈光和紋理坐標生成完全。同樣地,使用任何一個片段程序替換紋理相結合,將在SetTexture命令定義的模式,因此SetTexture命令是沒有必 要的。
寫作頂點/片斷程序需要一個三維轉換,照明和協調空間 - 透徹的認識,因為你必須重寫固定功能是一樣的OpenGL的API將自己的建造。另一方面,你可以做更多比內置的!
利用ShaderLab Cg語言
在ShaderLab着色器通常用Cg語言嵌入“Cg的片段着色器中的文本”的編程語言。 Cg的片段被編譯成低級的統一着色器組裝編輯,最后着色是在你的游戲的數據中包含的文件只包含這個低層次的組裝。
當您選擇一個項目視圖着色,檢查員Cg語言編譯后顯示,這可能有助於在調試援助着色文本。統一自動編譯的CG OpenGL和Direct3D的片段,所以你的着色器,使用Cg的工作都將只。請注意,因為企業管治守則是由編輯編譯,你不能創建Cg的從腳本在運行時着色器。
一般來說,Cg的片段放在里面通行證塊。他們看起來像這樣:
- Pass {
- // ... the usual pass state setup ...
- CGPROGRAM
- // compilation directives for this snippet, e.g.:
- #pragma vertex vert
- #pragma fragment frag
- // the Cg code itself
- 2
- ENDCG
- // ... the rest of pass setup ...
- }
下面的例子演示了一個Cg的方案,使得對象的顏色正常人完全着色:
- Shader "Tutorial/Display Normals" {
- SubShader {
- Pass {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #pragma fragmentoption ARB_fog_exp2
- #include "UnityCG.cginc"
- struct v2f {
- V2F_POS_FOG;
- float3 color : COLOR0;
- };
- v2f vert (appdata_base v)
- {
- v2f o;
- PositionFog( v.vertex, o.pos, o.fog );
- o.color = v.normal * 0.5 + 0.5;
- return o;
- }
- half4 frag (v2f i) : COLOR
- {
- return half4( i.color, 1 );
- }
- ENDCG
- }
- }
- Fallback "VertexLit"
- }
當應用於一個對象,將圖像中的結果如下(如果您的圖形卡支持頂點和片段的過程中方案):
我們的“平均值顯示”着色沒有任何屬性,包含一個是空的,除了單一的企業管治守則證單SubShader。最后,向在VertexLit着色器內置的定義后備。讓我們解剖了部分企業管治守則的一部分:
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #pragma fragmentoption ARB_fog_exp2
- // ... snip ...
- ENDCG
整個Cg的片段之間寫入CGPROGRAM和ENDCG關鍵字。在開始編譯指令#pragma指令給出:
#pragma vertex name告訴該代碼包含在給定的函數(垂直這里頂點方案)。
#pragma fragment name告訴該代碼包含在給定的函數有關水土這里(片斷程序)。
#pragma fragmentoption name加上一個選項來編譯OpenGL的片段方案。在這里,我們添加指數平方霧支持。
繼匯編指令只是普通企業管治守則。我們首先包括一個內置的Cg的文件:
#include UnityCg.cginc
該文件包含常用UnityCg.cginc宣言等的着色器可以保持較小的功能。該文件本身是統一內發現的應用:/應用程 序/統一/ Unity.app /目錄/ CGIncludes / UnityCG.cginc。在這里,我們將使用appdata_base結構,V2F_POS_FOG宏觀和PositionFog從該文件助手作用。 我們可以只定義它們直接在着色,不包括文件的過程。
接下來,我們要定義一個片段“頂點”結構(這里命名為v2f) - 什么樣的信息是從頂點傳遞到片斷程序。我們通過標准的地位和霧參數和float3顏色參數。顏色將計算出的頂點程序和公正的片段程序的輸出。
我們的出發定義的頂點程序 - 垂直功能。在這里,我們在計算標准方法使用UnityCG.cginc輔助功能(位置和霧)輸入和輸出作為一種顏色正常的:
- ! o.color = v.normal * 0.5 + 0.5;
正常的組成部分是-1 .. 1范圍內,而色彩在0 .. 1范圍內,所以我們在規模和偏見的代碼高於正常。接下來,我們定義一個片斷程序 - 有關水土功能,只是顏色和產出計算為alpha組件1:
- half4 frag (v2f i) : COLOR
- {
- return half4( i.color, 1 );
- }
就這樣,我們的着色完畢!即使這樣簡單的着色器是非常有用的可視化網格法線。
當然,這種着色不響應所有燈光,而這其中,事情變得有點復雜,對渲染管線和光衰減有關詳細信息,參考網頁閱讀。
企業管治守則中使用着色性能當你定義的着色性能,你給他們一個像_Color或_MainTex名稱。要使用Cg語言你他們只需要定義一
個匹配的名稱和類型的變量。統一將自動設置Cg的變量的名稱與着色性能匹配。
下面是一個完整的着色器顯示的顏色調制紋理。當然,你可以很容易地做一個紋理合成調用相同的,但問題是這里只是為了說明如何使用Cg的屬性:
- Shader "Tutorial/Textured Colored" {
- Properties {
- _Color ("Main Color", Color) = (1,1,1,0.5)
- _MainTex ("Texture", 2D) = "white" { }
- 5
- }
- SubShader {
- Pass {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #pragma fragmentoption ARB_fog_exp2
- #include "UnityCG.cginc"
- float4 _Color;
- sampler2D _MainTex;
- struct v2f {
- V2F_POS_FOG;
- float2 uv : TEXCOORD0;
- };
- v2f vert (appdata_base v)
- {
- v2f o;
- PositionFog( v.vertex, o.pos, o.fog );
- o.uv = TRANSFORM_UV(0);
- return o;
- }
- half4 frag (v2f i) : COLOR
- {
- half4 texcol = tex2D( _MainTex, i.uv );
- return texcol * _Color;
- }
- ENDCG
- }
- }
- Fallback "VertexLit"
- }
這種着色結構是在前面的例子一樣。在這里,我們定義兩個屬性,即_Color和_MainTex。企業管治守則內我們定義相應的變量:
- float4 _Color;
- sampler2D _MainTex;
訪問中看到着色性能更多信息,Cg語言。
頂點和片段程序在這里不做任何幻想;頂點程序使用從UnityCG.cginc的TRANSFORM_UV宏,以確保質 地規模與偏移是正確應用,並片斷程序公正樣品的質地和顏色屬性的繁殖。請注意,因為我們正在編寫我們自己的節目片段在這里,我們不需要任何 SetTexture命令。如何紋理着色器適用於完全控制的片斷程序。
摘要
我們已經展示了如何定制着色器程序可以在幾個簡單的步驟產生。雖然這里的例子所示很簡單,沒有什么阻止你寫任意復雜的着色器程序!這可以幫助您采取統一充分利用,實現最佳的渲染效果。
完整的ShaderLab參考手冊在這里。我們也有一個陰影在forum.unity3d.com論壇,從而去那里得到你的着色器的幫助!編程快樂,享受團結和Shaderlab權力。
在這個block內我們設定了一個顏色值,並命名為_Color,我們會在后面使用這個顏色
在下個命令,我們指定如何混合紋理以及顏色值。我們用Combine命令來混合其他紋理或顏色,看起來像下面這樣:
Combine ColorPart, AlphaPart在這里ColorPart與AlphaPart定義了混合的顏色(RGB)以及alpha值(A)個別的資料,假如AlphaPart 被省略了,那它將與ColorPart的資料作混合在我們的VertexLit范例中:
- Combine texture * primary DOUBLE, texture * constant
這里的texture來源是當前的紋理(_MainTex),它將與主要的顏色互相搭配(*),主色為照明設備的顏色,它是由Material計算出來的 結果。最終是這兩個倍增后的結果會增加照明強度.aplha值(在逗號以后)是由constantColor倍增而得的結果。另一個常用的混合模式稱為 previous(在這個shader未使用),這是所有previous SetTexture的結果,並且可以用來混合多種紋理和顏色。