創建一個shader
什麼是Shader
Shader(着色器)是一段能夠針對3D對象進行操作、並被GPU所執行的程序。Shader並不是一個統一的標準,不同的圖形接口的Shader並不相同。OpenGL的着色語言是GLSL, NVidia開發了Cg,而微軟的Direct3D使用高級着色器語言(HLSL)。而Unity的Shader 是將傳統的圖形接口的Shader(由 Cg / HLSL編寫)嵌入到獨有的描述性結構中而形成的一種代碼生成框架,最終會自動生成各硬件平臺自己的Shader,從而實現跨平臺。
Unity Shader 其實並不難,初學者往往很迷惑是因爲它有太多固定的命令和結構,而這些命令又需要我們對3D渲染有一定的瞭解才能知道它們是做什麼的。
Shader種類
OpenGL和Direct3D都提供了三類着色器:
- 頂點着色器:處理每個頂點,將頂點的空間位置投影在屏幕上,即計算頂點的二維座標。同時,它也負責頂點的深度緩衝(Z-Buffer)的計算。頂點着色器可以掌控頂點的位置、顏色和紋理座標等屬性,但無法生成新的頂點。頂點着色器的輸出傳遞到流水線的下一步。如果有之後定義了幾何着色器,則幾何着色器會處理頂點着色器的輸出數據,否則,光柵化器繼續流水線任務。
- 像素着色器(Direct3D),常常又稱爲片斷着色器(OpenGL):處理來自光柵化器的數據。光柵化器已經將多邊形填滿並通過流水線傳送至像素着色器,後者逐像素計算顏色。像素着色器常用來處理場景光照和與之相關的效果,如凸凹紋理映射和調色。名稱片斷着色器似乎更爲準確,因爲對於着色器的調用和屏幕上像素的顯示並非一一對應。舉個例子,對於一個像素,片斷着色器可能會被調用若干次來決定它最終的顏色,那些被遮擋的物體也會被計算,直到最後的深度緩衝纔將各物體前後排序。
- 幾何着色器:可以從多邊形網格中增刪頂點。它能夠執行對CPU來說過於繁重的生成幾何結構和增加模型細節的工作。Direct3D版本10增加了支持幾何着色器的API, 成爲Shader Model 4.0的組成部分。OpenGL只可通過它的一個插件來使用幾何着色器。
Unity Shader 分爲 表面着色器(Surface Shader)和 頂點片段着色器(Vertex And Fragment Shader)
- 表面着色器(Surface Shader)是Unity提出的一個概念。編寫着色器與光照的交互是複雜的,光源有很多類型,不同的陰影選項,不同的渲染路徑(正向和延時渲染),表面着色器將這一部分簡化。Unity建議使用表面着色器來編寫和光照有關的Shader。
- 頂點片段着色器(Vertex And Fragment Shader)和OpenGL,Direct3D中的頂點着色器和片段着色器沒有什麼區別。頂點片段着色器比表面着色器使用更自由也更強大,當然光照需要自行處理。Unity也允許在裏面編寫幾何着色器,一般用得不多。
Unity Sahder的模板 類型
模板名 | 功能 |
Standard Surface Shader | 包含標準光照模型的表面着色器模板。 |
Unlit Shader | 產生一個不包含光照(但包含霧效)的基本的頂點/片元着色器。 |
ImageEffect Shader | 實現各種屏幕後處理效果的基本模版。 |
Compute Shader | 特殊的Shader,利用GPU的並行性來進行一些與常規渲染流水線無關的計算。 |
Unity Shader 結構
// 着色器名稱
Shader "ShaderName" {
// 屬性
Properties {
Name ("display name", PrpertyType) = DefaultValue
}
// 子着色器
SubShader {
// 可選的,標籤(告訴Unity如何、何時渲染對象)
[Tags]
// 可選的,狀態設置(開關混合、深度測試,剔除模式,設置深度測試使用函數)
[RenderSetup]
// 完整渲染流程
Pass {
[Name]
[Tags]
[RanderSetup]
}
}
//SubShader不能運行時執行(同時渲染陰影)
Fallback "VertexLit"
}
● Properties
- 屬性: 不一定要在Properties語義塊中聲明,也可以在Cg中定義變量(可通過腳本向Shader傳遞)。
- 作用: 只是爲了讓這些屬性顯示在材質面板中。
- 定義:
Name ("display name", PrpertyType) = DefaultValue
- Name:名字。在Shader中訪問該變量時,要聲明這個名字。
- display name:顯示的名字。即面板顯示的標籤名字。
- PropertyType:屬性類型。
- DefaultValue:默認值。
Properties { // Numbers and Sliders _Int ("Int", Int) = 2 _Float ("Float", Float) = 1.5 _Range("Range", Range(0.0, 5.0)) = 3.0 // Colors and Vectors _Color ("Color", Color) = (1,1,1,1) _Vector ("Vector", Vector) = (2, 3, 6, 1) // Textures _2D ("2D", 2D) = "" {} _Cube ("Cube", Cube) = "white" {} _3D ("3D", 3D) = "black" {} } //對於 SubShader 數據類型 ************************************************************************************ Range Color 2D Rect Cube Float Vector ************************************************************************************ // Color , Vector 對於的數據類型: float4 half4 fixed4 // Range , Float 對於的數據類型: float half fixed // 2D 對於的數據類型: sampler2D // 3D 對於的數據類型: sampler3D // Cube 對於的數據類型: samplerCube
● SubShader
子着色器 : 每一個Unity Shader文件可以包含多個SubShader語義塊,但最少要有一個。Unity會掃描所有SubShader語義塊,然後選擇第一個能夠在目標平臺上運行的,如果都不支持,則會使用Fallback指定的Shader。
定義:
- Tags:標籤,可選。
- RenderSetup:狀態,可選。
- Pass:一次完整的渲染流程。可以在裏面聲明標籤和狀態。
● RenderSetup
設置顯卡的各種狀態。如果需要應用到所有Pass塊中,則在SubShader中設置。否則如雙面渲染,第一個Pass剔除正面(Cull Front)來渲染背面,第二個Pass剔除背面(Cull Back)來渲染正面。
狀態名稱 | 參數 | 含義 |
Cull | Cull Back | Front | Off | 剔除模式: 剔除背面 | 剔除正面 | 關閉剔除 |
ZTest | ZTest Less Greater | LEqual | GEqual | Equal | NotEqual | Always | 設置深度測試 使用的函數 |
ZWrite | ZWrite On | Off | 開啓 關閉 深度寫入 |
Blend | Blend SrcFactor DstFactor | 混合模式 |
● Tags
是鍵值對,都爲字符串類型。用於告訴Unity:SubShader要怎麼樣以及合適渲染這個對象。
結構:Tags { "TagName1" = "Value1" "TagName2" = "Value2" }注意:下面這些標籤都只可以在SubShafer中聲明,不可以在Pass塊中聲明。
標籤類型 說明 例子 Queue 控制渲染順序, 指定該物體屬於哪一個渲染列隊; 通過這種方式 可以保證所有的透明物體 可以在所有不透明 物體後面被渲染。 我們也可以自定義 使用渲染列隊來 控制 物體的渲染順序 Tags { "Queue" ="Transparent"} RenderType 對着色器 進行分類, 例如 這是一個 不透明的 着色器, 或者是一個 透明的着色器; 這可以被用於 着色器替換的功能 Tags { "RenderType"="Opaque" } DisableBatching 一些 SubShader 使用 Unity 的 批處理功能是 會出問題; 有時候可以 手動關閉 Tags {"DisableBatching" = "True" } ForceNoShadowCasting 是否投射陰影 Tags { "ForceNoShadowCasting"="True" } IgnoreProjecter 如果爲 True, 那麼將不會受到 Projecter 的影響,通過用於半透明 Tags { "IgnoreProjecter"="True" } CanUseSpriteAtlas 該 Shader 是否用於精靈 圖集 Tags { "CanUserSpriteAtlas"="True" } PreviewType 指定 材質球的 預覽形狀 Tags { "PreviewType"="Plane" }
● Pass語義塊
Pass { [Name] // 如 Name "MyPassName" [Tags] [RenderSetup] // Other code }
- Name:名稱。
- RenderSetup:同SubShader,還可以使用固定管線的着色器命令。
- Tags:不同與Subshader的標籤。
其他標籤:
- UsePass:直接使用別的Shader的Pass塊。在使用時必須使用大寫形式如:
UsePass "MyShader/MYPASSNAME"
。 - GrabPass:抓取屏幕,並將結果存儲到一張紋理中,用於後序的Pass處理。
Unity Sahder的形式:
● 表面着色器(Surface Shader)
這是 Unity自己創造的一種着色器代碼類型,在背後會轉換成對應的頂點/片元着色器,相當於再對頂點/片元着色器的抽象。定義在
CGPROGRAM
和ENDCG
之間,而不是在Pass中。
#pragma surface surf Lambert // 使用surf函數作爲表面着色器,用Lambert光照模型 struct Input { float4 color : COLOR; }; void surf (Input IN, inout SurfaceOutput o){ o.Albedo = 1; }
表面着色器:
Surface Shader示例:
Shader "Custom/Simple Surface Shader"
{
SubShader {
Tags{"RenderType"="Opaque"}
CGPROGRAM
#pragma surface surf Lambert
struct Input
{
float4 color:COLOR;
};
void surf (Input IN,inout SurfaceOutput o)
{
o.Albedo=1;
}
ENDCG
}
FallBack "Diffuse"
}
● 頂點/片元着色器(Vertex/Fragment Shader)
相比表面着色器更復雜,但更靈活。可以控制渲染的實現細節。同樣定義在
CGPROGRAM
和ENDCG
之間。
#pragma vertex vert // 使用vert函數作爲頂點着色器 #pragma fragment frag // 使用frag函數作爲片元着色器
定點片元着色器:
SubShader{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v : POSITION):SV_POSITION
{
return mul (UNITY_MATRIX_MVE,V);
}
fixed4 frag():SV_Target
{
return fixed4 (1.0,0.0,0.0,1.0);
}
ENDCG
}
}
● 固定函數着色器(Fixed Function Shader)
以上兩種都是可編程管線,而對於一些舊設備,就不支持了,這時就要用固定函數着色器完成渲染,也只能完成一些簡單的效果。定義在Pass塊中。
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
}
}
}