【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 amp; 纹理混合...
本系列文章由@淺墨_毛星云?出品,轉載請注明出處。??
文章鏈接:?http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html
作者:毛星云(淺墨)?? ?微博:http://weibo.com/u/1723155442
郵箱:?happylifemxy@163.com
QQ交流群:330595914
很多其它文章盡在:http://www.hpw123.net
?
?
本文介紹了Unity中子著色器、通道和標簽相關的具體概念與寫法。以及紋理的設置方法,主要的紋理混合寫法。寫了5個Shader作為本文Shader解說的實戰內容,最后創建了一個夢幻的光之城堡場景進行了Shader的測試。依然是國際慣例。先上本文配套程序的截圖。
光之城堡:
?
?
山坡上遠眺:
?
通向森林的路:
古墓:
?
霧氣氤氳的森林:
?
?
來一張上帝視角:
OK,圖先就上這么多。文章末尾有很多其它的執行截圖。并提供了原project的下載。可執行的exe下載在這里:
?
【游戲場景可執行的exe請點擊這里下載試玩】
?
?
?我們正式開始。
?
?
?
?
一、子著色器(SubShader)相關內容解說
話不多說。我們接著上篇文章繼續講。
Unity中的每個著色器都包括一個subshader的列表,當Unity須要顯示一個網格時。它能發現使用的著色器,并提取第一個能執行在當前用戶的顯示卡上的子著色器。
?
我們知道,子著色器定義了一個渲染通道的列表,并可選是否為全部通道初始化所須要的通用狀態。
子著色器的寫法例如以下:
?
Subshader{ [Tags] [CommonState] Passdef [Passdef ...] }
?
也就是通過可選標簽,通用狀態 和 一個Pass 定義的列表構成了子著色器。
當Unity選擇用于渲染的子著色器時。它為每個被定義的通道渲染一次對象(可能會很多其它,這取決于光線的交互作用)。當對象的每一次渲染都是非常費資源之時,我們便使用盡量少的通道來定義一個著色器。當然,有時在一些顯示硬件上須要的效果不能通過單次通道來完畢。
自然就得使用多通道的子著色器了。
另外,通道定義的類型包含a regular Pass, a Use Pass or aGrab Pass。
不論什么出如今通道定義的狀態同一時候也能整個子著色器塊中可見。
這將使得全部通道共享狀態。
?
?
1.1?關于子著色器標簽(SubShader Tags)
?
?
?
子著色器使用標簽來告訴渲染引擎期望何時和怎樣渲染對象。其語法例如以下:
?
Tags?{ "TagName1" ="Value1" "TagName2" = "Value2" }
?
也就是。為標簽"TagName1"指定值"Value1"。為標簽"TagName2"指定值"Value2"。我們能夠設定隨意多的標簽。
標簽是標準的鍵值對,也就是能夠依據一個鍵值獲得相應的一個值的。SubShader 中的標簽是用來決定渲染的次序和子著色器中的其它變量的。
?
?
?
1.1.1 ?決定渲染次序——隊列標簽(Queue tag)
?
?
我們能夠使用 Queue 標簽來決定對象被渲染的次序。著色器決定它所歸屬的對象的渲染隊列,不論什么透明渲染器能夠通過這個辦法保證在全部不透明對象渲染完成后再進行渲染。
有四種提前定義(predefined)的渲染隊列,在提前定義隊列之間還能夠定義很多其它的隊列。這四種提前定義的標簽例如以下:
?
- 后臺(Background)?- 這個渲染隊列在全部隊列之前被渲染,被用于渲染天空盒之類的對象。
- 幾何體(Geometry,默認值)- 這個隊列被用于大多數對象。 不透明的幾何體使用這個隊列。
- 透明(Transparent)?- 這個渲染隊列在幾何體隊列之后被渲染。採用由后到前的次序。不論什么採用alpha混合的對象(也就是不正確深度緩沖產生寫操作的著色器)應該在這里渲染(如玻璃,粒子效果等)
- 覆蓋(Overlay)?- 這個渲染隊列被用于實現疊加效果。
不論什么須要最后渲染的對象應該放置在此處。(如鏡頭光暈等)
?
一個使用Tags的示比例如以下:
[cpp]?view plaincopyprint?
?
1.1.2?自己定義中間隊列
讓我們來舉例說明怎樣在透明隊列中渲染對象。普通情況下,幾何體渲染隊列為了達到最優的性能優化了對象的繪制次序。而其它渲染隊列根據舉例排序對象,從最遠的對象開始渲染到近期的對象。
而對于特殊的須要,能夠使用中間隊列來滿足。在Unity實現中每個隊列都被一個整數的索引值所代表。
后臺為1000。幾何體為2000,透明為3000,疊加層為4000. 著色器能夠自己定義一個隊列,如:
?
Tags?{ "Queue" ="Geometry+1" }
?
由于渲染隊列是從小到大來數的,這就會使對象在全部不透明的對象渲染之后但卻在全部透明物體前被渲染,該渲染隊列的索引值為2001。
當我們希望某些對象總是在其它某些對象前被繪制的情況下。這用起來就非常方便了。
比方。在絕大多數時候,透明的水總是應該在全部不透明的物體之后并在透明對象前被渲染,這就能夠通過中間隊列來滿足渲染需求。
?
1.1.3?關于忽略投影標簽(IgnoreProjector tag)
?
后面我們會接觸到,若設置IgnoreProjector(忽略投影)標簽為"True",那么使用這個著色器的對象就不會被投影機制(Projectors)所影響。
這對半透明的物體來說是一個福利,由于臨時沒有對他們產生投影的比較合適的辦法。那么直接忽略掉即可了。
?
?
二、 通道(Pass)相關內容解說
Pass通道塊控制被渲染的對象的幾何體。
其語法定義是這種:
?
Pass?{ [Name and Tags] [RenderSetup][TextureSetup] }
?
基本通道命令包括一個可選的渲染設置命令的列表,和可選的被使用的紋理的列表。
?
?
2.1?通道中的名稱與標簽(Name and tags )
?
一個通道能定義它的Name 和隨意數量的Tags。通過使用tags來告訴渲染引擎在什么時候該怎樣渲染他們所期望的效果。語法例如以下:
?
Tags?{ "TagName1" ="Value1" "TagName2" = "Value2" }
?
指定TagName1 的值為Value1 ,TagName2 的值為 Value2 你能夠指定非常多自己喜歡的標簽,以下會具體來列舉。
?
標簽基本上是鍵-值對的形式。 內部的Pass標簽用來控制光照管道(環境光照。頂點光照和像素光照)中pass 的任務和一些其他選項。注意下面的標簽必須在pass段內部,而不是在SubShader中被識別。
?
2.1.1?光照模式標簽(LightMode tag)
?
?
LightMode 標簽定義了Shader的光照模式。詳細含義以后會在講渲染管線時講到。以下我們先簡單了解一下有哪些光照模式可選,以及他們的詳細作用:
?
- Always: 總是渲染。沒有運用光照。
- ForwardBase:用于正向渲染,環境光、方向光和頂點光等
- ForwardAdd:用于正向渲染,用于設定附加的像素光,每一個光照相應一個pass
- PrepassBase:用于延遲光照,渲染法線/鏡面光。
- PrepassFinal:用于延遲光照,通過結合紋理,光照和自發光渲染終于顏色
- Vertex: 用于頂點光照渲染,當物體沒有光照映射時。應用全部的頂點光照
- VertexLMRGBM:用于頂點光照渲染,當物體有光照映射的時候使用頂點光照渲染。在平臺上光照映射是RGBM 編碼
- VertexLM:用于頂點光照渲染。當物體有光照映射的時候使用頂點光照渲染。在平臺上光照映射是double-LDR 編碼(移動平臺。及老式臺式CPU)
- ShadowCaster: 使物體投射陰影。
- ShadowCollector: 為正向渲染對象的路徑,將對象的陰影收集到屏幕空間緩沖區中。
?
?
2.1.2?條件選項標簽 (RequireOptions tag )
?
若想要在一些外部條件得到滿足時某pass才渲染,就能夠通過使用RequireOptions標簽,它的值是一個空格切割的字符串,眼下由Unity3d支持的選項僅僅有一個,就是渲染植被之時:
?
SoftVegetation: 假設在QualitySettings中開啟渲染軟植被(Edit->Project Settings->Quality),則該pass能夠渲染
?
?
?
2.2?關于渲染設置 (Render Setup )
?
?
通道設定顯示硬件的各種狀態,比如能打開alpha混合,能使用霧,等等。這些命令例如以下:
?
Material?{ Material Block }
定義一個使用頂點光照管線的材質,詳情參考上次我們講的Material
?
Lighting?On | Off
開啟或關閉頂點光照。開啟燈光之后,頂點光照才會有作用
?
Cull?Back | Front | Off
設置多邊形剔除模式,具體內容后面的文章會解說到。
?
ZTest?(Less | Greater | LEqual | GEqual |Equal | NotEqual | Always)
設置深度測試模式,具體內容后面的文章會解說到。
?
ZWrite?On | Off
設置深度寫模式,具體內容后面的文章會解說到。
?
Fog?{ Fog Block }
設置霧參數,具體內容后面的文章會解說到。
?
AlphaTest?(Less | Greater | LEqual | GEqual| Equal | NotEqual | Always) CutoffValue
開啟alpha測試
?
Blend?SourceBlendMode |DestBlendMode
設置alpha混合模式
?
Color?Color value
設置當頂點光照關閉時所使用的顏色
?
ColorMask?RGB | A | 0 | any combination of R, G, B, A
設置顏色寫遮罩。
設置為0將關閉全部顏色通道的渲染
?
Offset?OffsetFactor , OffsetUnits
設置深度偏移
?
SeparateSpecular?On | Off
開啟或關閉頂點光照相關的平行高光顏色。
?
ColorMaterial?AmbientAndDiffuse | Emission
當計算頂點光照時使用每頂點的顏色
?
?
2.3?關于紋理設置(Texture Setup )
?
在完畢渲染設定后,我們能夠指定一定數量的紋理和當使用 SetTexture 命令時所採用的混合模式:
?
SetTexture?[texture property]{ [Combineoptions] }
?
紋理設置,用于配置固定函數多紋理管線,當自己定義fragment shaders 被使用時,這個設置也就被忽略掉了。
?
2.4?一些細節
?
2.4.1?關于每像素光照(Per-pixel Lighting )
?
每像素光照管線通過多次通道渲染對象來完畢。Unity渲染對象一次來獲取陰影色和不論什么頂點光照。然后再在額外的并行通道中渲染出每像素光照的效果。
?
2.4.2?關于每頂點光照(Per-vertex Lighting)
每頂點光照是標準的Direct3D/OpenGL光照模式,通過計算每一個頂點的光照來完畢。Lighting on命令開啟光照。而我們知道,光照被材質塊。顏色材質和平行高光等命令所影響。
?
2.5?一些高端特效的通道命令
?
?
有時候,我們會寫一些特殊的通道。要多次重復利用普通的功能或是實現高端的特效。
應對這些情況。Unity中就有一些高級點武器能夠選用,這里簡單講一講吧,如今先略微有個概念就好。
?
2.5.1?UsePass——包括已經寫好的通道
UsePass 能夠包括來自其它著色器的通道,來降低反復的代碼。
?
比如,在很多像素光照著色器中,陰影色或頂點光照通道在在對應的頂點光照著色器中是同樣的。UsePass命令僅僅是包括了還有一個著色器的給定通道。
比如例如以下的命令能夠使用內置的高光著色器中的名叫"Base"的通道:
?
UsePass?"Specular/BASE"
?
而為了讓UsePass可以認識到指定的是誰,必須給希望使用的通道命名,弄個身份證。通道中的Name命令就是這個功能:
?
Name?"MyPassName"
2.5.2?GrabPass——捕獲屏幕內容到紋理中
GrabPass 能夠捕獲物體所在位置的屏幕的內容并寫入到一個紋理中。通常在靠后的通道中使用,這個紋理能被用于興許的通道中完畢一些高級圖像特效。
?
?
一個示比例如以下:
?
[cpp]?view plaincopyprint?
?
?
?
?
三、紋理(Texturing)相關內容解說
紋理在主要的頂點光照計算完畢之后被應用,這也就是SetTexture 命令必須放置在通道的末尾的原因了。在著色器中通過SetTexture 命令來完畢。
須要注意的是,SetTexture 命令在使用了片段著色器時不會生效;由于在片段著色器下像素操作被全然描寫敘述在著色器中。
?
?
材質貼圖能夠用來實現舊式風格的混合器效果。
我們能夠在一個通道中使用多個SetTexture命令, SetTexture全部紋理都是按代碼順序來加入的。也就是如同Photoshop中的圖層操作一樣。SetTexture的語法例如以下:
?
SetTexture?[TexturePropertyName]?{?TextureBlock?}
?
解釋:分配一個紋理,當中TexturePropertyName必須為一個紋理。也就是在shader最開始的Properties中的屬性。
在TextrueBlock中設置怎樣應用紋理。即紋理塊控制紋理怎樣被應用。而在紋理塊中能運行3種命令:合并操作。矩陣操作、與常量顏色進行混合操作。
?
?
3.1?紋理合并命令
combine?src1?*?src2
將源1和源2的元素相乘。結果會比單獨輸出不論什么一個都要暗
?
combine?src1?+?src2
將將源1和源2的元素相加。結果會比單獨輸出不論什么一個都要亮
?
combine?src1?-?src2
源1 減去 源2
?
combine?src1?+-?src2
先相加。然后減去0.5(也就是加入了一個符號)
?
combine?src1?lerp (src2)?src3
使用源2的透明度通道值在源3和源1中進行差值,注意差值是反向的:當透明度值是1是使用源1。透明度為0時使用源3
?
combine?src1?*?src2?+?src3
源1和源2的透明度相乘。然后加上源3
?
combine?src1?*?src2?+-?src3
源1和源2的透明度相乘。然后和源3做符號加
?
combine?src1?*?src2?-?src3
源1和源2的透明度相乘,然后和源3相減
?
?
當中,全部src屬性都能夠是previous,constant, primary or texture當中的一個。
?
- Previous?是上一次SetTexture的結果
- Primary?是來自光照計算的顏色或是當它綁定時的頂點顏色
- Texture是在SetTexture中被定義的紋理的顏色
- Constant是被ConstantColor定義的顏色
?
一些小技巧:
1.上述的公式都均能通過keyword Double 或是 Quad 將終于顏色調高亮度2倍或4倍。
2.全部的src屬性,除了差值參數都能被標記一個“-”負號來使終于顏色反相。
3.全部src屬性能通過尾隨 alpha 標簽來表示僅僅取用alpha通道。
?
?
3.2?顏色常量命令
?
ConstantColor?color
?
定義在combine命令中能被使用的常量顏色
?
?
3.3?紋理矩陣命令
?
matrix?[MatrixPropertyName]
?
使用給定矩陣變換紋理坐標
?
?
3.4?一些細節
較老的顯卡對紋理通常會使用分層的操作方案,而紋理在每一層后被應用一次顏色的改動。對每個紋理,一般來說紋理都是和上一次操作的結果混合。如圖:
?
?
須要注意的是,對于“純正”的“固定功能流水線”設備(比方說OpenGL, OpenGL ES 1.1, Wii)。每一個SetTexture階段的值被限制為0到1的范圍之間。而其它的設備(如Direct3D, OpenGL ES 2.0)中,這個范圍就不一定是固定的。這樣的情況就可能會影響SetTexture階段。可能使產生的值高于1.0。
?
3.4.1?關于分離的透明度和顏色混合(Separate Alpha & Color computation)
?
在默認情況下,混合公式被同一時候用于計算紋理的RGB通道和透明度。
同一時候,我們也能指定針對透明度來單獨計算,比方這樣。將RGB操作和Alpha操作隔開:
SetTexture?[_MainTex] { combine previous *texture, previous + texture }
如上所述,我們對RGB的顏色做乘然后對Alpha透明度相加
?
?
3.4.2?關于反射高光(Specular highlights)
默認情況下primary顏色是漫反射。陰影色和高光顏色(在光線計算中定義)的加和。假設我們將通道設置中的SeparateSpecular On 寫上,高光色便會在混合計算后被增加,而不是之前。PS:Unity內置的頂點著色器就是加上SeparateSpecular On的。
?
?
3.4.3?關于顯卡的硬件支持情況說明
我們上篇文章中已經講到過,一些舊的顯示卡不能支持某些紋理混合模式。且不同的卡有不同數目的SetTexture階段可用。所以我們應該為想支持的顯卡來分開寫SubShader,適應各種情況?。
PS::支持像素著色器1.1版本號的顯卡(即NVIDIA GeForce 3 或更高, ATI Radeon 8500 或更高, Intel 9xx)支持全部的混合器模式,而且能夠擁有至少4級渲染階段。下表簡述了硬件支持情況。
?
| Card 顯卡 | Stage count 級數 | Combiner modes?not?supported不支持的結合模式 |
| NVIDIA GeForce 3/4Ti and up | 4 | In OpenGL on Windows, src1*src2-src3 is not supported |
| NVIDIA TNT2, GeForce 256, GeForce 2, GeForce 4MX | 2 | In OpenGL on Windows, src1*src2-src3 is not supported |
| ATI Radeon 9500 and up | 4-8 | 8 in OpenGL, 4 in D3D9 |
| ATI Radeon 8500-9250 | 4-6 | 6 in OpenGL, 4 in D3D9 |
| ATI Radeon 7500 | 3 | ? |
| ATI Rage | 2 | src1*src2+src3? |
?
?
?
?
?
?
四、Shader書寫實戰
上面講了一堆一堆的概念和寫法,預計大家一遍看下來頭都大了。
沒關系,依然是讓我們看一些演示樣例Shader的寫法。弄清楚上面這一堆堆的概念是怎樣應用的。主要是紋理相關內容的Shader書寫
?
?
1. Alpha紋理混合
?
先看看怎樣用本文解說的寫法。寫出一個簡單的紋理混合Shader。
首先設置第一個混合器僅僅使用_MainTex,然后使用_BlendTex的Alpha通道來淡入_BlendTex的RGB顏色:
?
[cpp]?view plaincopyprint?
進行混合的兩張紋理例如以下:
??
?
此Shader編譯后賦給材質的效果例如以下:
?
?
?
?
2.紋理的Alpha通道與自發光相混合
?
這個著色器使用_MainTex的Alpha來描寫敘述什么地方應用光照。
它通過分兩個階段應用紋理來實現。第一個階段,紋理的Alpha值被用來在頂點顏色和純白色之間混合。第二階段,乘入紋理的RGB通道:
?
[cpp]?view plaincopyprint?
此Shader編譯后賦給材質的效果例如以下:
?
?
?
?
?
?
3.?紋理Alpha與自發光混合可調色版
?
這次我們給出一個Color屬性。讓自發光顏色可調:
?
[cpp]?view plaincopyprint?
此Shader編譯后賦給材質的效果例如以下,能夠自由調節顏色:
?
?
?
4.?頂點光照+紋理Alpha自發光混合
?
?
我們將本文中介紹的知識點和上一篇文章中頂點光照相關的內容結合起來,主要是在Pass中加入了一句,讓頂點光照能夠和紋理顏色結合起來:
[cpp]?view plaincopyprint?
完整的Shader代碼例如以下:
[cpp]?view plaincopyprint?
此Shader編譯后賦給材質的效果例如以下:
?
?
?
?
?
5.?頂點光照+自發光混合+紋理混合
在剛剛介紹的第四個Shader的基礎上。加上第一個Shader中解說的紋理混合。就做成了本文終于的頂點光照+自發光混合+紋理混合Shader:
?
[cpp]?view plaincopyprint?
此Shader編譯后賦給材質的效果例如以下:
?
換些高光顏色玩一玩:
?
?
?正常白色高光版:
?
?
五、終于游戲場景效果演示——光之城堡
?
上一次我們處于盛大的暴風雪之中。這次的場景,最好還是讓我們來到夢幻又神奇的的光之城堡,領略一番不一樣的味道。以大師級美工鬼斧神工的場景作品為基礎。淺墨增加了音樂,并調整了場景布局,增加了很多其它高級特效,于是便得到了如此這次讓人頗顯震撼的夢幻場景。
執行游戲,我們來到繁花盛開的光之城堡:
抬頭,看陽光透過樹梢:
?
低頭,是花草搖曳:
?
?
左側,是通向森林的路:
?
右側,是綿延的花海:
?
來到霧氣氤氳的森林:
?
回望。一線天:
?
陽光透過樹梢,滿滿的生機:
?
絢爛的花海:
?
?
走出森林,前方是一片幽暗之地:
?
走進去。原來是一個小型古墓:
?
在小山坡上遠眺:
?
?
?
來一張上帝視角,大好河山盡收眼底:
?
依然是一張今天所講的Shader的全家福:-
?
?
透過洞口的可見光線偏移:
?
?
OK,美圖就放這么多。游戲場景可執行的exe能夠在文章開頭中提供的鏈接下載。
?
另外,本次的project加載后會報Mismatched serialization in thebuiltin class 'Mesh'. (Read 100788 bytes but expected 100789 bytes)系列錯誤。沒關系,這是unity4.6之前版本號的一個bug,不影響正常使用。clear掉即可了。
?
?
本篇文章的演示樣例程序請點擊此處下載:
?
【淺墨Unity3D Shader編程】之三 光之城堡篇配套Unityproject下載
?
?
?
淺墨近期事情實在是有些多,所以僅僅好下周停更一次了。
下下個周一,我們不見不散。
轉載于:https://www.cnblogs.com/jhcelue/p/6843845.html
總結
以上是生活随笔為你收集整理的【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 amp; 纹理混合...的全部內容,希望文章能夠幫你解決所遇到的問題。