不管你會不會寫Unity3D的shader,估計你會知道,Unity3D編寫shader有三種方式,這篇東西主要就是說一下這三種東西有什么區別,和大概是怎樣用的。
先來列一下這三種方式:
fixed function shader
vertex and fragment shader
surface shader
為什么Unity3D要提供三種shader的編寫方式呢?那是因為三種方式的編寫的難易度有區別,對應着不同的使用人群。其實我覺得這是Uniy3D想得有點多了,着色器不單止是為了實現效果,還要效果實現的效率高,才適合真正使用在項目中,單純實現一個效果,那只是demo級別的,只適合unity3D自身的產品推廣,對實際使用者的意義不會很大。基於這種想法,我下面只會大概的介紹三種方式的使用方法,以后會通過其他文章有針對性的對某種方式進行詳細的說明。
先來看看三種方式的說明:
fixed function shader (固定功能着色器)
最簡單的着色器類型,只能使用Unity3D自帶的固定語法和提供的方法,適用於任何硬件,使用難度最小
vertex and fragment shader (頂點片段程序着色器)
頂點片段着色器,效果最為豐富的着色器類型,使用Cg/HLSL語言規范,着色器由頂點程序和片段程序組成。所有效果都需要自己編寫,使用難度相對較大。
surface shader (表面着色器)
同樣使用Cg/HLSL語言規范的着色器類型,不過把光照模型提取出來,可以使用Unity3D自帶的一些光照模型,也可以自己編寫光照模型,着色器同樣由頂點程序和片段程序組成,不過本身有默認的程序方法,使用者可以只針對自己關系的效果部分進行編寫。由於選擇性比較大,所以可以編寫出較為豐富的效果,使用難度相對vertex and fragment shader小。
由上面的三種方式的說明可以看出來了,如果你是由豐富的的Cg開發經驗的,好的選擇就是直接使用vertex and fragment shader了。fixed function shader雖然簡單,但能實現的效果非常有限。而surface shader是Unity3D提供的一種較為折中的方式,同樣能實現較豐富的效果,但難度相對小很多。不過surface shader有一個問題,它不支持SubShader內部的多pass,所以某些需要多pass的效果要實現起來會比較困難。
接下來是主要內容,每一種方式的編寫規范和舉例:
1、Fixed Function shaders
1.標准范例:
1 Shader "VertexLit" { 2 Properties { 3 _Color ("Main Color", Color) = (1,1,1,0.5) 4 _SpecColor ("Spec Color", Color) = (1,1,1,1) 5 _Emission ("Emmisive Color", Color) = (0,0,0,0) 6 _Shininess ("Shininess", Range (0.01, 1)) = 0.7 7 _MainTex ("Base (RGB)", 2D) = "white" { } 8 } 9 10 SubShader { 11 Pass { 12 Material { 13 Diffuse [_Color] 14 Ambient [_Color] 15 Shininess [_Shininess] 16 Specular [_SpecColor] 17 Emission [_Emission] 18 } 19 Lighting On 20 SeparateSpecular On 21 SetTexture [_MainTex] { 22 constantColor [_Color] 23 Combine texture * primary DOUBLE, texture * constant 24 } 25 } 26 } 27 }
2.具體說明
具體變量、Material模塊參考《Unity3D的着色器介紹(二)——Unity3D的Shader基本結構說明》,主要區別是使用SetTexture 模塊來控制貼圖
SetTexture的具體寫法:
SetTexture [貼圖名稱] {
ConstantColor color
Combine ColorPart, AlphaPart
}
ConstantColor color
定義一個常量顏色在combine command里面使用。
combine command
combine src1 * src2
將src1和src2相乘,結果會比兩者更暗。
combine src1 + src2
將src1和src2相加,結果會比兩者更亮
combine src1 - src2
從src1里減去src2
combine src1 +- src2
把src1加到src2,然后減去0.5
combine src1 lerp (src2) src3
src3和src1之間的插值,使用src2的透明值。插值是相對的。
當透明值是1時顯示src1,當透明值是0時顯示src3
combine src1 * src2 + src3
將src1和src2的透明度相乘,然后加上src3
combine src1 * src2 +- src3
將src1和src2的透明度相乘,然后與src3相加減去0.5
Multiplies src1 with the alpha component of src2, then does a signed add with src3.
combine src1 * src2 - src3
將src1和src2的透明度相乘,然后減去src3
matrix command
matrix [MatrixPropertyName]
Transforms texture coordinates used in this command with the given matrix
其中Combine部分是可選的,作用是設定顏色部分和透明部分的疊加方式。
其中透明部分如果不寫,將會默認使用顏色部分相同的設置。
一般的寫法是Combine texture * primary,這樣的意思是把貼圖乘以頂點顏色作為最終顏色。
也可以寫成Combine texture * primary DOUBLE,加一個DOUBLE的作用是讓光照強度增幅成2倍。primary代表的是初始的頂點顏色,也就是在沒有加貼圖之前的頂點顏色。
如果需要疊加第二張或者更多貼圖,那么就不能再乘以primary了,要改為previous,也就是Combine texture * previous。previous代表的是之前的顏色,也就是你前一張貼圖乘以初始顏色之后的顏色。
從Fixed Function shaders的寫法可以看出,這種類型的shader非常簡單易用,基本上所有平台設備都能支持,不過能做的東西確實不多。你可以用來簡單的表現一些基本的貼圖和顏色疊加的效果,也可以使用TexGen的貼圖來實現一些貼圖特效,不過不能控制頂點,也不能較為復雜的控制表面着色。
2、Vertex and Fragment Shader
1.標准例子:
1 Shader "Custom/Exam1" { 2 Properties { 3 _MainTex ("Texture", 2D) = "white" { } 4 } 5 SubShader 6 { 7 pass 8 { 9 CGPROGRAM 10 #pragma vertex vert 11 #pragma fragment frag 12 #include "UnityCG.cginc" 13 sampler2D _MainTex; 14 float4 _MainTex_ST; 15 struct v2f { 16 float4 pos : SV_POSITION; 17 float2 uv : TEXCOORD0; 18 } ; 19 v2f vert (appdata_base v) 20 { 21 v2f o; 22 o.pos = mul(UNITY_MATRIX_MVP,v.vertex); 23 o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); 24 return o; 25 } 26 float4 frag (v2f i) : COLOR 27 { 28 float4 texCol = tex2D(_MainTex,i.uv); 29 float4 outp = texCol; 30 return outp; 31 } 32 ENDCG 33 } 34 } 35 }
2.細節解釋:
1、Cg小片段
Cg小片段以CGPROGRAM開頭,以ENDCG結尾。中間的內容使用Cg/HLSL語言。
Cg小片段的開始處,可以添加:
#pragma vertex name - 表明這是個以name為名字的函數的頂點程序。
#pragma fragment name- 表明這是個以name為名字的函數的片段程序。
#pragma fragmentoption option - 添加選項到編譯的OpenGL 片段程序。 通過 ARB 片段程序 可以查詢到所允許的規范的選項列表。 這個指令對頂點程序或者不是以OpenGL為編譯目標的程序無效。
#pragma target name - 着色器目標編譯。
#pragma target default 編譯默認目標:
在Direct3D 9環境中:頂點着色器1.1和像索着色器2.0
ARB頂點程序 有128位指令限制,ARB片段程序 有96位指令限制(32位紋理 + 64位算術運算),16個臨時寄存器和4個間接紋理。
#pragma target 3.0 編譯着色器模式3.0:
在Direct3D 9環境中:頂點着色器3.0和像索着色器 3.0
ARB頂點程序 沒有指令限制,ARB片段程序 有1024位指令限制(512位紋理 + 512位算術運算),32個臨時寄存器和4個間接紋理。可以通過使用 #pragma profileoption 命令擴展限制值。例如: #pragma profileoption MaxTexIndirections=256增加間接紋理上限到256個。需要注意的是某些着色器模式3.0中的特性不支持ARB頂點程序和ARB片段程序,比如派生指令這樣的。你可以使用 #pragma glsl命令轉換到GLSL中,這樣子限制較少。
#pragma only_renderers space separated names - 僅用給定的渲染器編譯着色器。默認情況下用所有的渲染器都編譯着色器。
?d3d9 - Direct3D 9.
?opengl - OpenGL.
?gles - OpenGL ES 2.0.
?xbox360 - Xbox 360.
?ps3 - PlayStation 3.
?flash - Flash.
#pragma exclude_renderers space separated names - 不用給定的渲染器編譯着色器。 默認情況下用所有的渲染器都編譯着色器。參考上一點。
#pragma glsl - 用桌面OpenGL平台編譯着色器時,轉換成GLSL里面的Cg/HLSL(而不是默認設置的ARB頂點/片段程序)。
2、參數對應聲明
如有以下着色器輸入參數
_MyColor ("Some Color", Color) = (1,1,1,1)
_MyVector ("Some Vector", Vector) = (0,0,0,0)
_MyFloat ("My float", Float) = 0.5
_MyTexture ("Texture", 2D) = "white" {}
_MyCubemap ("Cubemap", CUBE) = "" {}
那么在cg程序里面應該再次聲明這些參數,才能在cg程序里面使用
float4 _MyColor;
float4 _MyVector;
float _MyFloat;
sampler2D _MyTexture;
samplerCUBE _MyCubemap;
類型對應關系:
Color和Vector屬性對應 float4 類型。
Range和Float屬性對應 float 類型。
Texture屬性對應 普通2D紋理的sampler2D 類型。
CUBE和RECT紋理對應 samplerCUBE 和 samplerRECT類型。
3、頂點程序
需要獲取頂點的信息,可以先引用#include "UnityCG.cginc"
然后在這個包里面有2種類型包含了頂點的信息:
appdata_base: 包含頂點位置,法線 和一個紋理坐標。
appdata_tan:包含頂點位置,切線,法線 和一個紋理坐標。
其中包含以下的參數:
float4 vertex 是頂點位置
float3 normal 是頂點法線
float4 texcoord 是第一UV坐標
float4 texcoord1 是第二UV坐標
float4 tangent 是切線向量(用在法線貼圖中)
float4 color 是每個頂點(per-vertex)顏色
當然也可以不引用#include "UnityCG.cginc",直接自己定義appdata
struct appdata {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
其中常用類型:
POSITION:頂點位置
COLOR:頂點顏色
NORMAL:法線
TANGENT:切線
TEXCOORD0:UV1
TEXCOORD1:UV2
聲明頂點程序名稱
#pragma vertex vert
下面就可以寫頂點程序vert了
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.color.xyz = v.normal * 0.5 + 0.5;
return o;
}
其中v2f是一個自定義塊,里面定義了頂點程序返回時包含的信息,也可以傳遞給片段程序用,其中位置改成SV_POSITION。也可以自定義一些其他的屬性。
在頂點程序里面的頂點與矩陣相乘得到最終位置,相關矩陣解釋:
UNITY_MATRIX_MVP
當前模型*視*投影矩陣。(注:模型矩陣為 本地->世界)
UNITY_MATRIX_MV
當前模型*視圖矩陣
UNITY_MATRIX_V
當前視圖矩陣
UNITY_MATRIX_P
當前投影矩陣
UNITY_MATRIX_VP
當前視圖*投影矩陣
UNITY_MATRIX_T_MV
轉置模型*視圖矩陣
UNITY_MATRIX_IT_MV
-逆轉置模型*視矩陣
UNITY_MATRIX_TEXTURE0 to UNITY_MATRIX_TEXTURE3
紋理變換矩陣
當每一幀渲染的時候,每一個需要渲染的物體會自動的把頂點信息輸入到shader的指定頂點程序,在vert頂點程序里面處理完,把頂點需要的信息賦值之后,就可以把v2f返回,這時候gpu會自動接收到頂點信息,並作處理。這個是GPU編程的特點。
3、片段程序
#pragma fragment frag
然后寫
half4 frag (v2f i) : COLOR
{
half4 texcol = tex2D (_MainTex, i.uv);
return texcol * _Color;
}
片段程序不需要自定義塊作為返回值,因為片段程序的返回值就直接是顏色了。
不過在片段程序里面,可以通過對之前頂點程序做處理獲得的頂點位置、uv、法線等信息進行各種處理,讓最終該點的顏色發生各種各樣的改變。
3、Surface Shaders
1.標准例子:
Shader "Example/Diffuse Texture" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; }; sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
2.詳細說明
程序聲明寫法:
#pragma surface surfaceFunction lightModel [optionalparams]
surfaceFunction就是程序名稱,該程序應該寫成:
void surf (Input IN,inout SurfaceOutput o),
Input是你自己定義的結構。Input結構中應該包含所有紋理坐標(texture coordinates)和和表面函數(surfaceFunction)所需要的額外的必需變量。
lightModel 光照模型:
可選:
Lambert
BlinnPhong
或者自定義
Optional parameters:
可選參數:
alpha -透明( Alpha)混合模式。使用它可以寫出半透明的着色器。
alphatest:VariableName -透明( Alpha)測試模式。使用它可以寫出 鏤空效果的着色器。鏤空大小的變量(VariableName)是一個float型的變量。
finalcolor:ColorFunction - 自定義的最終顏色函數(final color function)。 請參考范例:表面着色器例子(Surface Shader Examples)。
exclude_path:prepass 或者 exclude_path:forward - 使用指定的渲染路徑,不需要生成通道。
addshadow - 添加陰影投射 & 收集通道(collector passes)。通常用自定義頂點修改,使陰影也能投射在任何程序的頂點動畫上。
dualforward - 在正向(forward)渲染路徑中使用 雙重光照貼圖(dual lightmaps)。
fullforwardshadows - 在正向(forward)渲染路徑中支持所有陰影類型。
decal:add - 添加貼花着色器(decal shader) (例如: terrain AddPass)。
decal:blend - 混合半透明的貼花着色器(Semitransparent decal shader)。
softvegetation - 使表面着色器(surface shader)僅能在Soft Vegetation打開時渲染。
noambient - 不適用於任何環境光照(ambient lighting)或者球面調和光照(spherical harmonics lights)。
novertexlights - 在正向渲染(Forward rendering)中不適用於球面調和光照(spherical harmonics lights)或者每個頂點光照(per-vertex lights)。
nolightmap - 在這個着色器上禁用光照貼圖(lightmap)。(適合寫一些小着色器)
nodirlightmap - 在這個着色器上禁用方向光照貼圖(directional lightmaps)。 (適合寫一些小着色器)。
noforwardadd - 禁用正向渲染添加通道(Forward rendering additive pass)。 這會使這個着色器支持一個完整的方向光和所有光照的per-vertex/SH計算。(也是適合寫一些小着色器).
approxview - 着色器需要計算標准視圖的每個頂點(per-vertex)方向而不是每個像索(per-pixel)方向。 這樣更快,但是視圖方向不完全是當前攝像機(camera) 所接近的表面。
halfasview - 在光照函數(lighting function)中傳遞進來的是half-direction向量,而不是視圖方向(view-direction)向量。 Half-direction會計算且會把每個頂點(per vertex)標准化。這樣做會很快,但不完全准確。
注意:surface由於使用了光照模型,所以是不能寫pass,直接寫在subshader里面的。
輸入端:
uv_貼圖變量名稱 - 貼圖的uv
float3 viewDir - 視圖方向( view direction)值。為了計算視差效果(Parallax effects),邊緣光照(rim lighting)等,需要包含視圖方向( view direction)值。
float4 with COLOR semantic -每個頂點(per-vertex)顏色的插值。
float4 screenPos - 屏幕空間中的位置。 為了反射效果,需要包含屏幕空間中的位置信息。比如在Dark Unity中所使用的 WetStreet着色器。
float3 worldPos - 世界空間中的位置。
float3 worldRefl - 世界空間中的反射向量。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。 請參考這個例子:Reflect-Diffuse 着色器。
float3 worldNormal - 世界空間中的法線向量(normal vector)。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。
float3 worldRefl; INTERNAL_DATA - 世界空間中的反射向量。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。為了獲得基於每個頂點法線貼圖( per-pixel normal map)的反射向量(reflection vector)需要使用世界反射向量(WorldReflectionVector (IN, o.Normal))。請參考這個例子: Reflect-Bumped着色器。
float3 worldNormal; INTERNAL_DATA -世界空間中的法線向量(normal vector)。如果表面着色器(surface shader)不寫入法線(o.Normal)參數,將包含這個參數。為了獲得基於每個頂點法線貼圖( per-pixel normal map)的法線向量(normal vector)需要使用世界法線向量(WorldNormalVector (IN, o.Normal))。
輸出端:
struct SurfaceOutput {
half3 Albedo;
half3 Normal;
half3 Emission;
half Specular;
half Gloss;
half Alpha;
};
最后說兩句我覺得比較重要的話:
1、着色器都有它規定的指令限制、寄存器、和紋理的限制,有一些復雜的效果需要使用到較多的寄存器和指令,可能會超出限制,比如使用着色器來實現骨骼動畫就很容易超出限制了。不論在什么平台編寫着色器(unity3D或者stage3D之類)都需要先了解清楚有什么限制,不然等到編譯的時候就哭了。
2、着色器的限制有時候是跟發布平台的硬件支持有關系的,某些發布的平台(比如手機端)不支持你指定的高級別着色器模式,它有可能會根據實際情況降低級別,或者甚至顯示不出來。所以編寫着色器效果之前要考慮一下需要發布的平台,不要老是問一些諸如“為什么我在Unity3D的編輯器看的效果是正常的,為什么發布到手機就看不到效果”之類的問題了。
原文地址:http://blog.csdn.net/a6627651/article/details/50545643