Unity一共提供了4 種Unity Shader 模板供我們選擇——
Standard Surface Shader:包含了標準光照模型基于物理的渲染方法表面著色器模板 (PBR)
Unlit Shader:一個不包含光照(但包含霧效)的基本的頂點/片元著色器
Image Effect Shader: 實現各種屏幕后處理效果
Compute Shader:利用GPU 的并行性來進行一些與常規渲染流水線無關的計算(GPU Skinning?) ps:(http://docs.unity3d.con/Manual/ComputeShaders.html)
ShaderLab
是Unity 提供的編寫Unity Shader 的一種說明性語言。它使用了一些嵌套在花括號內部的語義(syntax ) 來描述一個Unity Shader 文件的結構。
Shader ” ShaderName” {
Properties {
//屬性
Name (”display name", PropertyType) = DefaultValue
//一個字符串后跟一個花括號來指定的,其中,字符串要么是空的,要么是內置的紋理名稱,如“white "“ black ”“ gray "或者“ bump ”。
_Cube(”Cube ” , Cube ) = ”white”{}
}
SubShader {
//顯卡A使用的子著色器
//真正意義上的Shader 代碼會出現在這里
//表面著色器(Surface Shader )或者
// 頂點/片元著色器( Vertex/Fragment Shader )或者
//固定函數著色器( Fixed Function Shader )
//可選的 標簽
[Tags]
//可選的 狀態
[RenderSetup]
Pass {
// 例如 /只有定義了正確的LightMode,我們才能得到一些Unity 的內置光照變量
Tags { "LightMode"="ForwardBase" }
// /
// CGPROGRAM和ENDCG 來包圍CG 代碼片
//為了使用Properties語義塊中的屬性,需要定義一個和該屬性相同的變量類似c#
fixed4 _Diffuse;
}
// Other Passes
}
SubShader {
//顯卡B使用的子著色器
]
Fallback ”VertexLit”
ShaderLab 中常見的渲染狀態設置選項。
將會應用到所有的Pass。如果我們不想這樣(例如在雙面渲染中,我們希望在第一個Pass 中剔除正面來對背面進行渲染,在第二個Pass 中剔除背面來對正面進行渲染),可以在Pass 語義塊中單獨進行上面的設置。
標簽
SubShader 的標簽( Tags )是一個鍵值對(Key/Value Pair )。用來告訴Unity 的渲染引擎: SubShader我希望怎樣以及何時渲染這個對象。
Pass 語義塊
Pass 語義塊包含的語義如下:
Pass {
[Name]
[Tags]
[RenderSetup]
// Other code
}
//可以在Pass 中定義該Pass 的名稱
Name "MyPassName"
//通過這個名稱,我們可以使用ShaderLab 的UsePass 命令來直接使用其他Unity Shader 中的Pass
// 內部會把所有Pass 的名稱轉換成大寫字母的表示
UsePass "MyShader/MYPASSNAME”
-UsePass:如我們之前提到的一樣,可以使用該命令來復用其他Unity Shader 中的Pass;
-GrabPass:該Pass 負責抓取屏幕并將結果存儲在一張紋理中,以用于后續的Pass 處理
Fallback
最后保底的渲染方式,以上都不執行的話。
其中還包括陰影投射
shader種類的選擇
- 如果你想和各種光源打交道,你可能更喜歡使用表面著色器, 但需要小心它在移動平臺的性能表現。
-如果你需要使用的光照數目非常少, 例如只有一個平行光, 那么使用頂點/片元著色器是一個更好的選擇。
-最重要的是,如果你有很多自定義的渲染效果, 那么請選擇頂點/片元著色器。
渲染流水線
應用階段(Application Stage )、幾何階段( Geometry Stage )、光柵化階段( Rasterizer Stage )幾何階段
幾何階段用于處理所有和我們要繪制的幾何相關的事情。例如,決定需要繪制的圖元是什么,怎樣繪制它們,在哪里繪制它們。這一階段通常在GPU 上進行。
幾何階段負責和每個渲染圖元打交道,進行逐頂點、逐多邊形的操作。這個階段可以進一步分成更小的流水線階段,這在下一章中會講到。幾何階段的一個重要任務就是把頂點坐標變換到屏幕空間中,再交給光柵器進行處理。通過對輸入的渲染圖元進行多步處理后,這一階段將會輸出屏幕空間的二維頂點坐標、每個頂點對應的深度值、著色等相關信息,并傳遞給下一個階段。
光柵化階段
這一階段將會使用上個階段傳遞的數據來產生屏幕上的像素,并渲染出最終的圖像。這一階段也是在GPU 上運行。光柵化的任務主要是決定每個渲染圖元中的哪些像素應該被繪制在屏幕上。它需要對上一個階段得到的逐頂點數據(例如紋理坐標、頂點顏色等〉進行插值,然后再進行逐像素處理。
和上一個階段類似,光柵化階段也可以分成更小的流水線階段
什么是渲染狀態呢? 一個通俗的解釋就是,這些狀態定義了場景中的網格是怎樣被渲染的
幾何階段和光柵化階段可以分成若干更小的流水線階段, 這些流水線階段由GPU 來實現, 每個階段GPU 提供了不同的可配置性或可編程性。圖中展示了不同的流水線階段以及它們的可配置性或可編程性。
-頂點著色器( Vertex Shader ) 是完全可編程的,它通常用于實現頂點的空間變換、頂點著色等功能。
-曲面細分著色器( Tessellation Shader ) 是一個可選的著色器,它用于細分圖元。
-幾何著色器( Geometry Shader ) 同樣是一個可選的著色器,它可以被用于執行逐圖元( Per-Primitive)的著色操作,或者被用于產生更多的圖元。下一個流水線階段是
-裁剪( Clipping ),這一階段的目的是將那些不在攝像機視野內的頂點裁剪掉,并剔除某些三角圖元的面片。這個階段是可配置的。
例如,我們可以使用自定義的裁剪平面來配置裁剪區域,也可以通過指令控制裁剪三角圖元的正面還是背面。幾何概念階段的最后一個流水線階段是屏幕映射( Screen Mapping ) 。這一階段是不可配置和編程的,它負責把每個圖元的坐標轉換到屏幕坐標系中。
光柵化概念階段中的三角形設置(Triangle Setup ) 和三角形遍歷( Triangle Traversal )階段也都是固定函數(Fixed-Function )的階段。接下來的
片元著色器(Fragment Shader ),則是完全可編程的,它用于實現逐片元( Per-Fragment )的著色操作。最后,
逐片元操作( Per-Fragment Operations ) 階段負責執行很多重要的操作,例如修改顏色、深度緩沖、進行混合等,它不是可編程的,但具有很高的可配置性。
頂點著色器
我們可以通過改變頂點位置來模擬水面、布料等
最基本的頂點著色器必須完成的一個工作是,把頂點坐標從模型空間轉換到齊次裁剪空間。想想看, 我們在頂點著色器中是不是會看到類似下面的代碼:
o.pos = mul(UNITY_MVP, v.position);
屏幕映射
把每個圖元的x 和y 坐標轉換到屏幕坐標系(Screen Coordinates ) 下
這個過程實際是一個縮放的過程, 如圖2.10 所示。你可能會問,那么輸入的z 坐標會怎么樣呢?屏幕映射不會對輸入的z 坐標做任何處理。實際上,屏幕坐標系和z 坐標一起構成了一個坐標系,叫做窗口坐標系( Window Coordinates )。這些值會一起被傳遞到光柵化階段。
逐片元操作
模板測試( Stencil Test )
模板測試通常用于限制渲染的區域。另外,模板測試還有一些更高級的用法, 如渲染陰影、輪廓渲染等。
深度測試(Depth Test)
通常這個比較函數是小于等于的關系,即如果這個片元的深度值大于等于當前深度緩沖區中的值,那么就會舍棄它。這是因為,我們總想只顯示出離攝像機最近的物體,而那些被其他物體遮擋的就不需要出現在屏幕上。如果一個片元沒有通過深度測試,它就沒有權利更改深度緩沖區中的值。而如果它通過了測試,開發者還可以指定是否要用這個片元的深度值覆蓋掉原有的深度值, 這是通過開啟/關閉深度寫入來做到的。透明效果和深度測試以及深度寫入的關系非常密切。
片元通過上面測試,它就可以自豪地來到合并功能的面前。為什么需要合并?我們要知道,這里所討論的渲染過程是一個物體接著一個物體畫到屏幕上的。而每個像素的顏色信息被存儲在一個名為顏色緩沖的地方。因此,當我們執行這次渲染時,顏色緩沖中往往已經有了上次渲染之后的顏色結果,那么, 我們是使用這次渲染得到的顏色完全覆蓋掉之前的結果,還是進行其他處理?這就是合并需要解決的問題。
對于不透明物體,開發者可以關閉混合(Blend )操作。這樣片元著色器計算得到的顏色值就會直接覆蓋掉顏色緩沖區中的像素值。但對于半透明物體,我們就需要使用混合操作來讓這個物體看起來是透明的。
各種測試的順序是可以改變的 不一定非要片元之后再計算深度等等。