一、剔除與深度測試(Culling & Depth Testing)相關內容
1.1 剔除(Culling)的概念
對於實時交互的3D環境而言,現實的速度和效率是非常重要的。雖然現在的硬件能力非常的快,但是要想保持30FPS的同時處理數十萬的三角形,以現在的主流機器來說還是有些困難的。
爲了解決這種問題,人們提出了很多方法,其中有LOD,有Culling。這兩種方法並不矛盾,而且我們往往需要在culling的基礎上再使用LOD進一步解決pipeline的負擔。
剔除是一種通過避免渲染背對觀察者的幾何體面來提高性能的優化措施。所有幾何體都包含正面和反面。剔除基於大多數對象都是封閉的事實;如果你有一個立方體,你不會看到背離你的那一面(總是隻有一面在你的前方),因此我們不需要繪製出背面。因此也被稱做背面剔除。
一言以蔽之,所謂剔除,就是被擋住或視角以外的我們看不到的物體,因爲它們無關緊要,所以我們就不去繪製,以節省資源,提高場景的運行效率。
1.2 深度測試(Depth Testing)的概念
在複雜的場景中,通常有多個物體需要繪製,這些物體之間通常會存在遮擋關係,離觀察點較遠的物體會因爲近處物體的者的遮擋而不可見或只有部分可見,Direct3D圖形系統提供了深度測試功能來實現這種效果。深度測試可以簡化複雜場景的繪製,確保只有場景內的對象的最靠近的表面參與繪製。
淺墨之前在寫DirectX相關博文的時候寫過對深度測試的形象化理解,在這邊也列出來吧:
把深度測試看做在一口井的井口處向井中觀望。把所有物體都賦予一個深度值,放到井中來顯示。深度越深的物體,離井口就越遠。深度越淺的物體,離井口就越近。井表面的深度值爲0。離井口近而深度淺的物體,可能會把離井口遠的物體遮擋住。最終顯示在屏幕上的開啓深度測試後的畫面,就如在井口處向井中觀望裏面物體顯示出的遮擋與層次的效果一樣。當然,離井口的深度就是每個物體在世界座標系中的矩陣的Z座標值了。
1.3 剔除與深度測試(Culling & DepthTesting)相關句法
語句之一:Cull Back | Front| Off
此語句用於控制幾何體的哪一面會被剔除(也就是不被繪製) 。其中:
- Cull Back—— 不繪製背離觀察者的幾何面
- Cull Front—— 不繪製面向觀察者的幾何面,用於由內自外的旋轉對象
- Cull Off —— 顯示所有面。用於特殊效果。
語句之二:ZWrite On | Off
此語句用於控制是否將來之對象的像素寫入深度緩衝(默認開啓),如果需要繪製純色物體,便將此項打開,也就是寫上ZWrite On。如果要繪製半透明效果,關閉深度緩衝,則用ZWrite Off。
語句之三:ZTest Less |Greater | LEqual | GEqual | Equal | NotEqual | Always
此語句用於控制深度測試如何執行。
缺省值是LEqual (繪製和存在的對象一致或是在其中的對象;隱藏其背後的對象),含義列舉對應如下:
Greater | 只渲染大於AlphaValue值的像素 |
GEqual | 只渲染大於等於AlphaValue值的像素 |
Less | 只渲染小於AlphaValue值的像素 |
LEqual | 只渲染小於等於AlphaValue值的像素 |
Equal | 只渲染等於AlphaValue值的像素 |
NotEqual | 只渲染不等於AlphaValue值的像素 |
Always | 渲染所有像素,等於關閉透明度測試。等於用AlphaTest Off |
Never | 不渲染任何像素 |
語句之四:Offset Factor ,Units
此語句用兩個參數(Facto和Units)來定義深度偏移。
- Factor參數表示 Z縮放的最大斜率的值。
- Units參數表示可分辨的最小深度緩衝區的值。
於是,我們就可以強制使位於同一位置上的兩個集合體中的一個幾何體繪製在另一個的上層。例如偏移量Offset 設爲0, -1(即Factor=0, Units=-1)的值使得靠近攝像機的幾何體忽略幾何體的斜率,而偏移量爲-1,-1(即Factor =-1, Units=-1)時,則會讓幾何體偏移一個微小的角度,讓觀察使看起來更近些。
二、Alpha測試(Alpha testing)相關內容講解
2.1 Alpha測試的概念
Unity中,Alpha測試(Alpha Testing)是阻止像素被寫到屏幕的最後機會。在Pineline中Alpha測試的位置如下:
在最終渲染出的顏色被計算出來之後,可選擇通過將顏色的透明度值和一個固定值比較。如果比較的結果失敗,像素將不會被寫到顯示輸出中。
Alpha測試在渲染凹形物體的透明部分時非常有用。顯卡上有着每個像素寫到屏幕上的深度記錄。如果一個新的像素比原來的像素的深度深,那麼新的像素就不會被寫到屏幕中。
讓我們看一幅圖:
仔細看上圖,會發現:
- 左邊的樹使用了透明度測試(AlphaTest),注意在它的圖形上的像素是如何完全透明或不透明的。
- 中間的樹只使用透明度混合(Alpha Blending)來渲染,注意由於深度緩衝的緣故靠近分支的透明部分是如何覆蓋更遠的葉子。
- 最右邊的樹是通過下文實戰中的第六個Shader“6.簡單的植被Shader”着色器來渲染的,實現了透明度混合和透明度測試的組合,細節沒有粗糙之處,渾然天成。
2.2 Alpha測試相關句法
語句之一:AlphaTest Off
此語句用於渲染所有像素(缺省)
語句之二:AlphaTest comparison AlphaValue
此語句用於設定透明度測試只渲染在某一確定範圍內的透明度值的像素。其中的comparison取值爲下表之一:
Greater | Only render pixels whose alpha is greater than AlphaValue. 大於 |
GEqual | Only render pixels whose alpha is greater than or equal to AlphaValue. 大於等於 |
Less | Only render pixels whose alpha value is less than AlphaValue. 小於 |
LEqual | Only render pixels whose alpha value is less than or equal to from AlphaValue. 小於等於 |
Equal | Only render pixels whose alpha value equals AlphaValue. 等於 |
NotEqual | Only render pixels whose alpha value differs from AlphaValue. 不等於 |
Always | Render all pixels. This is functionally equivalent to AlphaTest Off. 渲染所有像素,等於關閉透明度測試AlphaTest Off |
Never | Don’t render any pixels. 不渲染任何像素 |
而AlphaValue爲一個範圍在0到1之間的浮點值。也可以是一個指向浮點屬性或是一個範圍屬性,在後一種情況下需要使用標準的方括號寫法標註索引名字,如([變量名]).
三、基本霧效(Fog)設置
3.1 霧效相關背景和概念理解
霧效(Fog)參數用於控制霧相關的渲染。
在計算機圖形學中,霧化是通過混合已生成的像素的顏色和基於到鏡頭的距離來確定的最終的顏色來完成的。霧化不會改變已經混合的像素的透明度值,只是改變RGB值。
Unity中的缺省霧設定是基於Edit->RenderSettings中的設置的,且霧模式既可以是Exp2也可以是關閉;密度和顏色完全取自設定。
注意如果使用了片段着色器的話,着色器中的霧設定仍然是有效的。如果平臺沒有對固定管線的霧功能支持,Unity會實時補上着色器以支持需要的霧模式。
3.2 霧效相關的句法
語句之一:Fog { FogCommands }
此語句用於設定霧命令的內容,具體的填在大括號之中的內容見下面的語句。
語句之二:Mode Off | Global | Linear | Exp | Exp2
此語句用於定義霧模式。缺省是全局的,依據霧在渲染設定中是否打開確定可從無變化到平方值
語句之三:Color ColorValue
此語句用於設定霧的顏色值。
語句之四:Density FloatValue
此語句以指數的方式設定霧的密度。
語句之五:Range FloatValue , FloatValue
此語句用於爲linear類型的霧設定遠近距離值。
OK,本文的基本知識就將這麼多,接下來看看QianMo’s Toolkit的更新,然後開始基於上面講解的基礎知識的Shader實戰。
四、QianMo’s Toolkit迎來v1.2更新
如前面三篇文章中所言,QianMo’s Toolkit是淺墨在Unity中寫Shader時會用到的一些腳本小工具,用C#來實現。
此次在QianMo’s Toolkit Ver1.2版中加入了新的特性——檢測當前系統的cpu與顯卡的型號和參數,並顯示到遊戲窗口中。其實現代碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
//-----------------------------------------------【腳本說明】------------------------------------------------------- //
腳本功能: 在遊戲運行時顯示系統CPU、顯卡信息 //
使用語言: C# //
開發所用IDE版本:Unity4.5 06f 、Visual Studio 2010 //
2014年12月 Created by 淺墨 //
更多內容或交流,請訪問淺墨的博客:http://blog.csdn.net/poem_qianmo //--------------------------------------------------------------------------------------------------------------------- //-----------------------------------------------【使用方法】------------------------------------------------------- //
方法一:在Unity中拖拽此腳本到場景任意物體之上 //
方法二:在Inspector中[Add Component]->[淺墨's Toolkit v1.2]->[ShowSystemInfo] //--------------------------------------------------------------------------------------------------------------------- using
UnityEngine; using
System.Collections; //添加組件菜單 [AddComponentMenu( "淺墨's
Toolkit v1.2/ShowSystemInfo" )] public
class
ShowSystemInfo : MonoBehaviour { string
systemInfoLabel; public
Rect rect = new
Rect(10, 100, 300, 300); void
OnGUI() { //在指定位置輸出參數信息 GUI.Label(rect,systemInfoLabel); } void
Update () { //獲取參數信息 systemInfoLabel
= "
\n\n\nCPU型號:"
+ SystemInfo.processorType + "\n
("
+ SystemInfo.processorCount + "
cores核心數, "
+ SystemInfo.systemMemorySize + "MB
RAM內存)\n "
+ "\n
顯卡型號:"
+ SystemInfo.graphicsDeviceName + "\n
"
+ Screen.width
+ "x"
+ Screen.height + "
@"
+ Screen.currentResolution.refreshRate + "
("
+ SystemInfo.graphicsMemorySize + "MB
VRAM顯存)" ; } } |
添加此腳本到場景中的任意物體後,填一下其中的x和y座標的值,用於設置輸出在屏幕中的哪個位置。
接着點運行,於是在遊戲窗口的指定區域中出現了CPU和顯卡的信息:
於是,目前的QianMo’s Toolkit v1.2擁有的功能如下所示:
ShowFPS:在遊戲運行時顯示幀率相關信息
ShowObjectInfo:在測試過程裏,於場景中和遊戲窗口中分別顯示添加給任意物體文字標籤信息。隱藏和顯示可選,基於公告板技術實現。
ShowGameInfo:在遊戲運行時顯示GUI相關說明
ShowLogo:在遊戲運行時顯示Logo
ShowUI:在遊戲運行時顯示簡單的鑲邊UI。
SetMaxFPS :用於突破Unity每秒渲染 60幀的設定,自由設置最大幀率。
ShowObjectInfoInGamePlay:用於發佈遊戲之後顯示文本信息。
ShowSystemInfo:在遊戲運行時顯示系統CPU、顯卡信息
五、寫Shader實戰
根據上文講解的剔除、深度測試和Alpha測試操作的句法,我們將完成如下六個Shader的寫法:
1.用剔除操作渲染對象背面
2.渲染對象背面v2
3.用剔除實現玻璃效果
4.基本Alpha測試
5.頂點光照+可調透明度
6.簡單的植被Shader
讓我們通過詳細註釋的代碼一個一個將他們講清楚。
1.用剔除操作渲染對象背面
先寫一個非常簡單的只會渲染對象的背面的Shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Shader
"淺墨Shader編程/Volume4/12.用剔除操作渲染對象背面" { //--------------------------------【子着色器】-------------------------------- SubShader { Pass { //【1】設置頂點光照 Material { Emission(0.3,0.3,0.3,0.3) Diffuse
(1,1,1,1) } //【2】開啓光照 Lighting
On //【3】剔除正面(不繪製面向觀察者的幾何面) Cull
Front } } } |
我們將此Shader編譯後賦給材質,得到如下效果,可以發現比原本默認的材質顏色暗,因爲我們繪製的是物體的背面:
我們將材質賦給一個立方體,可以看到,通過此Shader我們可以渲染對象的背面,從而看到一個很奇怪立方體(因爲我們看到的是它的內部)。效果如下:
2. 用剔除操作渲染對象背面(第二版)
讓我們給上面寫的這個Shader加上之前我們已經掌握的頂點光照的一些內容,並通過定義出第二個通道,採用亮藍色來渲染物體的背面。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
Shader
"淺墨Shader編程/Volume4/13.渲染對象背面v2" { //-------------------------------【屬性】--------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (1,1,1,0) _SpecColor
( "高光顏色" ,
Color) = (1,1,1,1) _Emission
( "光澤顏色" ,
Color) = (0,0,0,0) _Shininess
( "光澤度" ,
Range (0.01, 1)) = 0.7 _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} } //--------------------------------【子着色器】-------------------------------- SubShader { //---------------------------【通道一】------------------------------ // 說明:繪製對象的前面部分,使用簡單的白色材質,並應用主紋理 //---------------------------------------------------------------------- Pass { //【1】設置頂點光照 Material { Diffuse
[_Color] Ambient
[_Color] Shininess
[_Shininess] Specular
[_SpecColor] Emission
[_Emission] } //【2】開啓光照 Lighting
On //
【3】將頂點顏色混合上紋理 SetTexture
[_MainTex] { Combine
Primary * Texture } } //--------------------------【通道二】------------------------------- // 說明:採用亮藍色來渲染背面 //---------------------------------------------------------------------- Pass { Color
(0,0,1,1) Cull
Front } } } |
同樣是將Shader先賦給一個材質,得到的效果如下:
我們將材質賦給一個立方體,直接看是一個非常正常的立方體:
但一旦鏡頭靠近,看到物體的內部時,就發現了我們定義的藍色部分了:
3.用剔除實現玻璃效果
很多時候,控制剔除比背面調試(debugging backfaces)更有用。比如遇到需要渲染透明的物體,經常會想要顯示一個對象的背面。如果不做任何剔除操作的話,我們會發現有時常有一部分背面會覆蓋在前面的一部分之上。下面就是一個解決這個問題的用於凸物體(球,立方體,車窗)的簡單玻璃效果的着色器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
Shader
"淺墨Shader編程/Volume4/14.用剔除實現玻璃效果" { //-------------------------------【屬性】--------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (1,1,1,0) _SpecColor
( "高光顏色" ,
Color) = (1,1,1,1) _Emission
( "光澤顏色" ,
Color) = (0,0,0,0) _Shininess
( "光澤度" ,
Range (0.01, 1)) = 0.7 _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} } //--------------------------------【子着色器】-------------------------------- SubShader { //【1】定義材質 Material { Diffuse
[_Color] Ambient
[_Color] Shininess
[_Shininess] Specular
[_SpecColor] Emission
[_Emission] } //【2】開啓光照 Lighting
On //【3】開啓獨立鏡面反射 SeparateSpecular
On //【4】開啓透明度混合(alpha
blending) Blend
SrcAlpha OneMinusSrcAlpha //--------------------------【通道一】------------------------------- // 說明:渲染對象的背面部分 //---------------------------------------------------------------------- Pass { //
如果對象是凸型, 那麼總是離鏡頭離得比前面更遠 Cull
Front //不繪製面向觀察者的幾何面 SetTexture
[_MainTex] { Combine
Primary * Texture } } //----------------------------【通道二】----------------------------- // 說明:渲染對象背對我們的部分 //---------------------------------------------------------------------- Pass { //
如果對象是凸型,那麼總是離鏡頭離得比背面更遠 Cull
Back //不繪製背離觀察者的幾何面 SetTexture
[_MainTex] { Combine
Primary * Texture } } } } |
同樣是將Shader先賦給一個材質,得到的效果如下,可以發現是透明的黃色玻璃的效果:
再將材質賦給一個物體,就讓此物體實現了簡單的玻璃效果:
4.基本Alpha測試
先看一個最簡單的能用的例子,使用一張帶有透明度通道的紋理。對象只會在透明度大於0.6 時顯示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
Shader
"淺墨Shader編程/Volume4/15.基本Alpha測試" { //-------------------------------【屬性】----------------------------------------- Properties { _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} } //--------------------------------【子着色器】-------------------------------- SubShader { //----------------------------【通道】------------------------------- // 說明:進行Alpha測試操作,且只渲染透明度大於60%的像素 //---------------------------------------------------------------------- Pass { //
只渲染透明度大於60%的像素 AlphaTest
Greater 0.6 SetTexture
[_MainTex] { combine texture } } } } |
此Shader編譯後賦給材質,材質又賦給物體的效果如下,且因爲選取的紋理的透明度除了局部線條以外,其他的都小於了60%,於是就是只剩下這大概的輪廓了:
5.頂點光照+可調透明度
讓我們在上面這個Shader的基礎上增加一些光照和並讓Alpha的閾值可以調節,於是便寫出瞭如下的Shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
Shader
"淺墨Shader編程/Volume4/16.頂點光照+可調透明度" { //-------------------------------【屬性】----------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (1,1,1,0) _SpecColor
( "高光顏色" ,
Color) = (1,1,1,1) _Emission
( "光澤顏色" ,
Color) = (0,0,0,0) _Shininess
( "光澤度" ,
Range (0.01, 1)) = 0.7 _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{ } _Cutoff
( "Alpha透明度閾值" ,
Range (0,1)) = 0.5 } //--------------------------------【子着色器】-------------------------------- SubShader { Pass { //
【1】使用Cutoff參數定義能被渲染的透明度閾值 AlphaTest
Greater [_Cutoff] //【2】設置頂點光照參數值 Material { Diffuse
[_Color] Ambient
[_Color] Shininess
[_Shininess] Specular
[_SpecColor] Emission
[_Emission] } //【3】開啓光照 Lighting
On //
【4】進行紋理混合 SetTexture
[_MainTex] { combine texture * primary } } } } |
賦給材質後的效果如下,可以自由調節Alpha透明度的閾值:
通過調節Alpha透明度的閾值,得到了顯示效果差異很大的材質:
將此材質賦給物體後的效果如下:
6.簡單的植被Shader
最後一個Shader,讓我們實現一個簡單的植物渲染Shader的寫法。
當渲染樹和植物時,透明度測試使許多遊戲中出現尖銳的邊緣。解決這個問題的方法之一是渲染對象兩次。首次通道中,我們只渲染超過50%透明度的像素。在第二次通道中,我們使用透明度混合上次我們切除的部分,而不記錄像素的深度。我們可能會使一些樹枝覆蓋近的其他樹枝,以實現逼真的效果。於是,簡單的植被Shader寫法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
Shader
"淺墨Shader編程/Volume4/17.簡單的植被Shader" { //-------------------------------【屬性】----------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (.5, .5, .5, .5) _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} _Cutoff
( "Alpha透明度閾值" ,
Range (0,.9)) = .5 } //--------------------------------【子着色器】-------------------------------- SubShader { //【1】定義材質 Material { Diffuse
[_Color] Ambient
[_Color] } //【2】開啓光照 Lighting
On //【3】關閉裁剪,渲染所有面,用於接下來渲染幾何體的兩面 Cull
Off //--------------------------【通道一】------------------------------- // 說明:渲染所有超過[_Cutoff]
不透明的像素 //---------------------------------------------------------------------- Pass { AlphaTest
Greater [_Cutoff] SetTexture
[_MainTex] { combine
texture * primary, texture } } //----------------------------【通道二】----------------------------- // 說明:渲染半透明的細節 //---------------------------------------------------------------------- Pass { //
不寫到深度緩衝中 ZWrite
off //
不寫已經寫過的像素 ZTest
Less //
深度測試中,只渲染小於或等於的像素值 AlphaTest
LEqual [_Cutoff] //
設置透明度混合 Blend
SrcAlpha OneMinusSrcAlpha //
進行紋理混合 SetTexture
[_MainTex] { combine
texture * primary, texture } } } } |
我們將一棵樹的葉子和樹皮的材質替換成剛剛寫好的Shader:
然後看看效果:
嗯,還是挺不錯的,整體顯示效果比用Unity自帶的Bumped Diffuse要出色一些(Unity自帶的Bumped Diffuse還可以在樹葉上看到透過的光影,這部分內容我們以後以後學了再加)。
一、剔除與深度測試(Culling & Depth Testing)相關內容
1.1 剔除(Culling)的概念
對於實時交互的3D環境而言,現實的速度和效率是非常重要的。雖然現在的硬件能力非常的快,但是要想保持30FPS的同時處理數十萬的三角形,以現在的主流機器來說還是有些困難的。
爲了解決這種問題,人們提出了很多方法,其中有LOD,有Culling。這兩種方法並不矛盾,而且我們往往需要在culling的基礎上再使用LOD進一步解決pipeline的負擔。
剔除是一種通過避免渲染背對觀察者的幾何體面來提高性能的優化措施。所有幾何體都包含正面和反面。剔除基於大多數對象都是封閉的事實;如果你有一個立方體,你不會看到背離你的那一面(總是隻有一面在你的前方),因此我們不需要繪製出背面。因此也被稱做背面剔除。
一言以蔽之,所謂剔除,就是被擋住或視角以外的我們看不到的物體,因爲它們無關緊要,所以我們就不去繪製,以節省資源,提高場景的運行效率。
1.2 深度測試(Depth Testing)的概念
在複雜的場景中,通常有多個物體需要繪製,這些物體之間通常會存在遮擋關係,離觀察點較遠的物體會因爲近處物體的者的遮擋而不可見或只有部分可見,Direct3D圖形系統提供了深度測試功能來實現這種效果。深度測試可以簡化複雜場景的繪製,確保只有場景內的對象的最靠近的表面參與繪製。
淺墨之前在寫DirectX相關博文的時候寫過對深度測試的形象化理解,在這邊也列出來吧:
把深度測試看做在一口井的井口處向井中觀望。把所有物體都賦予一個深度值,放到井中來顯示。深度越深的物體,離井口就越遠。深度越淺的物體,離井口就越近。井表面的深度值爲0。離井口近而深度淺的物體,可能會把離井口遠的物體遮擋住。最終顯示在屏幕上的開啓深度測試後的畫面,就如在井口處向井中觀望裏面物體顯示出的遮擋與層次的效果一樣。當然,離井口的深度就是每個物體在世界座標系中的矩陣的Z座標值了。
1.3 剔除與深度測試(Culling & DepthTesting)相關句法
語句之一:Cull Back | Front| Off
此語句用於控制幾何體的哪一面會被剔除(也就是不被繪製) 。其中:
- Cull Back—— 不繪製背離觀察者的幾何面
- Cull Front—— 不繪製面向觀察者的幾何面,用於由內自外的旋轉對象
- Cull Off —— 顯示所有面。用於特殊效果。
語句之二:ZWrite On | Off
此語句用於控制是否將來之對象的像素寫入深度緩衝(默認開啓),如果需要繪製純色物體,便將此項打開,也就是寫上ZWrite On。如果要繪製半透明效果,關閉深度緩衝,則用ZWrite Off。
語句之三:ZTest Less |Greater | LEqual | GEqual | Equal | NotEqual | Always
此語句用於控制深度測試如何執行。
缺省值是LEqual (繪製和存在的對象一致或是在其中的對象;隱藏其背後的對象),含義列舉對應如下:
Greater | 只渲染大於AlphaValue值的像素 |
GEqual | 只渲染大於等於AlphaValue值的像素 |
Less | 只渲染小於AlphaValue值的像素 |
LEqual | 只渲染小於等於AlphaValue值的像素 |
Equal | 只渲染等於AlphaValue值的像素 |
NotEqual | 只渲染不等於AlphaValue值的像素 |
Always | 渲染所有像素,等於關閉透明度測試。等於用AlphaTest Off |
Never | 不渲染任何像素 |
語句之四:Offset Factor ,Units
此語句用兩個參數(Facto和Units)來定義深度偏移。
- Factor參數表示 Z縮放的最大斜率的值。
- Units參數表示可分辨的最小深度緩衝區的值。
於是,我們就可以強制使位於同一位置上的兩個集合體中的一個幾何體繪製在另一個的上層。例如偏移量Offset 設爲0, -1(即Factor=0, Units=-1)的值使得靠近攝像機的幾何體忽略幾何體的斜率,而偏移量爲-1,-1(即Factor =-1, Units=-1)時,則會讓幾何體偏移一個微小的角度,讓觀察使看起來更近些。
二、Alpha測試(Alpha testing)相關內容講解
2.1 Alpha測試的概念
Unity中,Alpha測試(Alpha Testing)是阻止像素被寫到屏幕的最後機會。在Pineline中Alpha測試的位置如下:
在最終渲染出的顏色被計算出來之後,可選擇通過將顏色的透明度值和一個固定值比較。如果比較的結果失敗,像素將不會被寫到顯示輸出中。
Alpha測試在渲染凹形物體的透明部分時非常有用。顯卡上有着每個像素寫到屏幕上的深度記錄。如果一個新的像素比原來的像素的深度深,那麼新的像素就不會被寫到屏幕中。
讓我們看一幅圖:
仔細看上圖,會發現:
- 左邊的樹使用了透明度測試(AlphaTest),注意在它的圖形上的像素是如何完全透明或不透明的。
- 中間的樹只使用透明度混合(Alpha Blending)來渲染,注意由於深度緩衝的緣故靠近分支的透明部分是如何覆蓋更遠的葉子。
- 最右邊的樹是通過下文實戰中的第六個Shader“6.簡單的植被Shader”着色器來渲染的,實現了透明度混合和透明度測試的組合,細節沒有粗糙之處,渾然天成。
2.2 Alpha測試相關句法
語句之一:AlphaTest Off
此語句用於渲染所有像素(缺省)
語句之二:AlphaTest comparison AlphaValue
此語句用於設定透明度測試只渲染在某一確定範圍內的透明度值的像素。其中的comparison取值爲下表之一:
Greater | Only render pixels whose alpha is greater than AlphaValue. 大於 |
GEqual | Only render pixels whose alpha is greater than or equal to AlphaValue. 大於等於 |
Less | Only render pixels whose alpha value is less than AlphaValue. 小於 |
LEqual | Only render pixels whose alpha value is less than or equal to from AlphaValue. 小於等於 |
Equal | Only render pixels whose alpha value equals AlphaValue. 等於 |
NotEqual | Only render pixels whose alpha value differs from AlphaValue. 不等於 |
Always | Render all pixels. This is functionally equivalent to AlphaTest Off. 渲染所有像素,等於關閉透明度測試AlphaTest Off |
Never | Don’t render any pixels. 不渲染任何像素 |
而AlphaValue爲一個範圍在0到1之間的浮點值。也可以是一個指向浮點屬性或是一個範圍屬性,在後一種情況下需要使用標準的方括號寫法標註索引名字,如([變量名]).
三、基本霧效(Fog)設置
3.1 霧效相關背景和概念理解
霧效(Fog)參數用於控制霧相關的渲染。
在計算機圖形學中,霧化是通過混合已生成的像素的顏色和基於到鏡頭的距離來確定的最終的顏色來完成的。霧化不會改變已經混合的像素的透明度值,只是改變RGB值。
Unity中的缺省霧設定是基於Edit->RenderSettings中的設置的,且霧模式既可以是Exp2也可以是關閉;密度和顏色完全取自設定。
注意如果使用了片段着色器的話,着色器中的霧設定仍然是有效的。如果平臺沒有對固定管線的霧功能支持,Unity會實時補上着色器以支持需要的霧模式。
3.2 霧效相關的句法
語句之一:Fog { FogCommands }
此語句用於設定霧命令的內容,具體的填在大括號之中的內容見下面的語句。
語句之二:Mode Off | Global | Linear | Exp | Exp2
此語句用於定義霧模式。缺省是全局的,依據霧在渲染設定中是否打開確定可從無變化到平方值
語句之三:Color ColorValue
此語句用於設定霧的顏色值。
語句之四:Density FloatValue
此語句以指數的方式設定霧的密度。
語句之五:Range FloatValue , FloatValue
此語句用於爲linear類型的霧設定遠近距離值。
OK,本文的基本知識就將這麼多,接下來看看QianMo’s Toolkit的更新,然後開始基於上面講解的基礎知識的Shader實戰。
四、QianMo’s Toolkit迎來v1.2更新
如前面三篇文章中所言,QianMo’s Toolkit是淺墨在Unity中寫Shader時會用到的一些腳本小工具,用C#來實現。
此次在QianMo’s Toolkit Ver1.2版中加入了新的特性——檢測當前系統的cpu與顯卡的型號和參數,並顯示到遊戲窗口中。其實現代碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
//-----------------------------------------------【腳本說明】------------------------------------------------------- //
腳本功能: 在遊戲運行時顯示系統CPU、顯卡信息 //
使用語言: C# //
開發所用IDE版本:Unity4.5 06f 、Visual Studio 2010 //
2014年12月 Created by 淺墨 //
更多內容或交流,請訪問淺墨的博客:http://blog.csdn.net/poem_qianmo //--------------------------------------------------------------------------------------------------------------------- //-----------------------------------------------【使用方法】------------------------------------------------------- //
方法一:在Unity中拖拽此腳本到場景任意物體之上 //
方法二:在Inspector中[Add Component]->[淺墨's Toolkit v1.2]->[ShowSystemInfo] //--------------------------------------------------------------------------------------------------------------------- using
UnityEngine; using
System.Collections; //添加組件菜單 [AddComponentMenu( "淺墨's
Toolkit v1.2/ShowSystemInfo" )] public
class
ShowSystemInfo : MonoBehaviour { string
systemInfoLabel; public
Rect rect = new
Rect(10, 100, 300, 300); void
OnGUI() { //在指定位置輸出參數信息 GUI.Label(rect,systemInfoLabel); } void
Update () { //獲取參數信息 systemInfoLabel
= "
\n\n\nCPU型號:"
+ SystemInfo.processorType + "\n
("
+ SystemInfo.processorCount + "
cores核心數, "
+ SystemInfo.systemMemorySize + "MB
RAM內存)\n "
+ "\n
顯卡型號:"
+ SystemInfo.graphicsDeviceName + "\n
"
+ Screen.width
+ "x"
+ Screen.height + "
@"
+ Screen.currentResolution.refreshRate + "
("
+ SystemInfo.graphicsMemorySize + "MB
VRAM顯存)" ; } } |
添加此腳本到場景中的任意物體後,填一下其中的x和y座標的值,用於設置輸出在屏幕中的哪個位置。
接着點運行,於是在遊戲窗口的指定區域中出現了CPU和顯卡的信息:
於是,目前的QianMo’s Toolkit v1.2擁有的功能如下所示:
ShowFPS:在遊戲運行時顯示幀率相關信息
ShowObjectInfo:在測試過程裏,於場景中和遊戲窗口中分別顯示添加給任意物體文字標籤信息。隱藏和顯示可選,基於公告板技術實現。
ShowGameInfo:在遊戲運行時顯示GUI相關說明
ShowLogo:在遊戲運行時顯示Logo
ShowUI:在遊戲運行時顯示簡單的鑲邊UI。
SetMaxFPS :用於突破Unity每秒渲染 60幀的設定,自由設置最大幀率。
ShowObjectInfoInGamePlay:用於發佈遊戲之後顯示文本信息。
ShowSystemInfo:在遊戲運行時顯示系統CPU、顯卡信息
五、寫Shader實戰
根據上文講解的剔除、深度測試和Alpha測試操作的句法,我們將完成如下六個Shader的寫法:
1.用剔除操作渲染對象背面
2.渲染對象背面v2
3.用剔除實現玻璃效果
4.基本Alpha測試
5.頂點光照+可調透明度
6.簡單的植被Shader
讓我們通過詳細註釋的代碼一個一個將他們講清楚。
1.用剔除操作渲染對象背面
先寫一個非常簡單的只會渲染對象的背面的Shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Shader
"淺墨Shader編程/Volume4/12.用剔除操作渲染對象背面" { //--------------------------------【子着色器】-------------------------------- SubShader { Pass { //【1】設置頂點光照 Material { Emission(0.3,0.3,0.3,0.3) Diffuse
(1,1,1,1) } //【2】開啓光照 Lighting
On //【3】剔除正面(不繪製面向觀察者的幾何面) Cull
Front } } } |
我們將此Shader編譯後賦給材質,得到如下效果,可以發現比原本默認的材質顏色暗,因爲我們繪製的是物體的背面:
我們將材質賦給一個立方體,可以看到,通過此Shader我們可以渲染對象的背面,從而看到一個很奇怪立方體(因爲我們看到的是它的內部)。效果如下:
2. 用剔除操作渲染對象背面(第二版)
讓我們給上面寫的這個Shader加上之前我們已經掌握的頂點光照的一些內容,並通過定義出第二個通道,採用亮藍色來渲染物體的背面。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
Shader
"淺墨Shader編程/Volume4/13.渲染對象背面v2" { //-------------------------------【屬性】--------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (1,1,1,0) _SpecColor
( "高光顏色" ,
Color) = (1,1,1,1) _Emission
( "光澤顏色" ,
Color) = (0,0,0,0) _Shininess
( "光澤度" ,
Range (0.01, 1)) = 0.7 _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} } //--------------------------------【子着色器】-------------------------------- SubShader { //---------------------------【通道一】------------------------------ // 說明:繪製對象的前面部分,使用簡單的白色材質,並應用主紋理 //---------------------------------------------------------------------- Pass { //【1】設置頂點光照 Material { Diffuse
[_Color] Ambient
[_Color] Shininess
[_Shininess] Specular
[_SpecColor] Emission
[_Emission] } //【2】開啓光照 Lighting
On //
【3】將頂點顏色混合上紋理 SetTexture
[_MainTex] { Combine
Primary * Texture } } //--------------------------【通道二】------------------------------- // 說明:採用亮藍色來渲染背面 //---------------------------------------------------------------------- Pass { Color
(0,0,1,1) Cull
Front } } } |
同樣是將Shader先賦給一個材質,得到的效果如下:
我們將材質賦給一個立方體,直接看是一個非常正常的立方體:
但一旦鏡頭靠近,看到物體的內部時,就發現了我們定義的藍色部分了:
3.用剔除實現玻璃效果
很多時候,控制剔除比背面調試(debugging backfaces)更有用。比如遇到需要渲染透明的物體,經常會想要顯示一個對象的背面。如果不做任何剔除操作的話,我們會發現有時常有一部分背面會覆蓋在前面的一部分之上。下面就是一個解決這個問題的用於凸物體(球,立方體,車窗)的簡單玻璃效果的着色器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
Shader
"淺墨Shader編程/Volume4/14.用剔除實現玻璃效果" { //-------------------------------【屬性】--------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (1,1,1,0) _SpecColor
( "高光顏色" ,
Color) = (1,1,1,1) _Emission
( "光澤顏色" ,
Color) = (0,0,0,0) _Shininess
( "光澤度" ,
Range (0.01, 1)) = 0.7 _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} } //--------------------------------【子着色器】-------------------------------- SubShader { //【1】定義材質 Material { Diffuse
[_Color] Ambient
[_Color] Shininess
[_Shininess] Specular
[_SpecColor] Emission
[_Emission] } //【2】開啓光照 Lighting
On //【3】開啓獨立鏡面反射 SeparateSpecular
On //【4】開啓透明度混合(alpha
blending) Blend
SrcAlpha OneMinusSrcAlpha //--------------------------【通道一】------------------------------- // 說明:渲染對象的背面部分 //---------------------------------------------------------------------- Pass { //
如果對象是凸型, 那麼總是離鏡頭離得比前面更遠 Cull
Front //不繪製面向觀察者的幾何面 SetTexture
[_MainTex] { Combine
Primary * Texture } } //----------------------------【通道二】----------------------------- // 說明:渲染對象背對我們的部分 //---------------------------------------------------------------------- Pass { //
如果對象是凸型,那麼總是離鏡頭離得比背面更遠 Cull
Back //不繪製背離觀察者的幾何面 SetTexture
[_MainTex] { Combine
Primary * Texture } } } } |
同樣是將Shader先賦給一個材質,得到的效果如下,可以發現是透明的黃色玻璃的效果:
再將材質賦給一個物體,就讓此物體實現了簡單的玻璃效果:
4.基本Alpha測試
先看一個最簡單的能用的例子,使用一張帶有透明度通道的紋理。對象只會在透明度大於0.6 時顯示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
Shader
"淺墨Shader編程/Volume4/15.基本Alpha測試" { //-------------------------------【屬性】----------------------------------------- Properties { _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} } //--------------------------------【子着色器】-------------------------------- SubShader { //----------------------------【通道】------------------------------- // 說明:進行Alpha測試操作,且只渲染透明度大於60%的像素 //---------------------------------------------------------------------- Pass { //
只渲染透明度大於60%的像素 AlphaTest
Greater 0.6 SetTexture
[_MainTex] { combine texture } } } } |
此Shader編譯後賦給材質,材質又賦給物體的效果如下,且因爲選取的紋理的透明度除了局部線條以外,其他的都小於了60%,於是就是只剩下這大概的輪廓了:
5.頂點光照+可調透明度
讓我們在上面這個Shader的基礎上增加一些光照和並讓Alpha的閾值可以調節,於是便寫出瞭如下的Shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
Shader
"淺墨Shader編程/Volume4/16.頂點光照+可調透明度" { //-------------------------------【屬性】----------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (1,1,1,0) _SpecColor
( "高光顏色" ,
Color) = (1,1,1,1) _Emission
( "光澤顏色" ,
Color) = (0,0,0,0) _Shininess
( "光澤度" ,
Range (0.01, 1)) = 0.7 _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{ } _Cutoff
( "Alpha透明度閾值" ,
Range (0,1)) = 0.5 } //--------------------------------【子着色器】-------------------------------- SubShader { Pass { //
【1】使用Cutoff參數定義能被渲染的透明度閾值 AlphaTest
Greater [_Cutoff] //【2】設置頂點光照參數值 Material { Diffuse
[_Color] Ambient
[_Color] Shininess
[_Shininess] Specular
[_SpecColor] Emission
[_Emission] } //【3】開啓光照 Lighting
On //
【4】進行紋理混合 SetTexture
[_MainTex] { combine texture * primary } } } } |
賦給材質後的效果如下,可以自由調節Alpha透明度的閾值:
通過調節Alpha透明度的閾值,得到了顯示效果差異很大的材質:
將此材質賦給物體後的效果如下:
6.簡單的植被Shader
最後一個Shader,讓我們實現一個簡單的植物渲染Shader的寫法。
當渲染樹和植物時,透明度測試使許多遊戲中出現尖銳的邊緣。解決這個問題的方法之一是渲染對象兩次。首次通道中,我們只渲染超過50%透明度的像素。在第二次通道中,我們使用透明度混合上次我們切除的部分,而不記錄像素的深度。我們可能會使一些樹枝覆蓋近的其他樹枝,以實現逼真的效果。於是,簡單的植被Shader寫法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
Shader
"淺墨Shader編程/Volume4/17.簡單的植被Shader" { //-------------------------------【屬性】----------------------------------------- Properties { _Color
( "主顏色" ,
Color) = (.5, .5, .5, .5) _MainTex
( "基礎紋理
(RGB)-透明度(A)" ,
2D) = "white"
{} _Cutoff
( "Alpha透明度閾值" ,
Range (0,.9)) = .5 } //--------------------------------【子着色器】-------------------------------- SubShader { //【1】定義材質 Material { Diffuse
[_Color] Ambient
[_Color] } //【2】開啓光照 Lighting
On //【3】關閉裁剪,渲染所有面,用於接下來渲染幾何體的兩面 Cull
Off //--------------------------【通道一】------------------------------- // 說明:渲染所有超過[_Cutoff]
不透明的像素 //---------------------------------------------------------------------- Pass { AlphaTest
Greater [_Cutoff] SetTexture
[_MainTex] { combine
texture * primary, texture } } //----------------------------【通道二】----------------------------- // 說明:渲染半透明的細節 //---------------------------------------------------------------------- Pass { //
不寫到深度緩衝中 ZWrite
off //
不寫已經寫過的像素 ZTest
Less //
深度測試中,只渲染小於或等於的像素值 AlphaTest
LEqual [_Cutoff] //
設置透明度混合 Blend
SrcAlpha OneMinusSrcAlpha //
進行紋理混合 SetTexture
[_MainTex] { combine
texture * primary, texture } } } } |
我們將一棵樹的葉子和樹皮的材質替換成剛剛寫好的Shader:
然後看看效果:
嗯,還是挺不錯的,整體顯示效果比用Unity自帶的Bumped Diffuse要出色一些(Unity自帶的Bumped Diffuse還可以在樹葉上看到透過的光影,這部分內容我們以後以後學了再加)。