轉載請標明出處http://www.cnblogs.com/zblade/
在學習了一段時間的Unity Shader后,打算寫一些知識總結,便於今后的查找。如有錯誤,希望大家指出更改。
本文參照的unity入門精要一書,做一個知識歸納,如有興趣可以看看其開源的部分,是一本比較好的入門shader書。
一、渲染流水線
學習shader的知識,最重要的是要理解渲染流水線,基於渲染流水線,才能進一步的理解和學習下面的各個部分的shader。基於 Real-time rendering一書,渲染流水線可以分為三個部分:
1、應用階段
在unity shader中,應用階段主要完成三個基本的事:
1)准備渲染的場景和模型;
2)設置渲染的狀態,主要是渲染所用到的材質、shader、貼圖等;
3)對渲染對象做一個粗粒度的剔除操作。
這一階段都是在CPU階段執行的,特別是渲染狀態的設置,對於后期的渲染有較大的影響,常見的批處理等操作都是在這階段完成的。對於CPU階段的優化,我也翻譯了一篇外文如何優化CPU造成的渲染性能問題,可以查看這兒:Unity渲染優化中文翻譯(二)——CPU的優化策略
2、幾何階段
基於應用階段的渲染圖元(primitives)以及渲染狀態的設置,在幾何階段,主要完成的任務是對模型的頂點進行坐標轉換,轉換到屏幕空間投影坐標系中。此外一些其他的轉換操作也可以在這個階段執行,比如描邊涉及的頂點位置偏移,模型空間到切線空間的轉換矩陣計算等等。
3、光柵化階段
基於幾何階段得到的頂點坐標數據,這一階段的主要任務是基於頂點確定三角形,再對三角形進行插值操作,得到片元像素,最后進行逐像素的操作得到像素顏色。通過一些模板測試,深度測試,透明度測試等等最后將可以顯示的像素更新到顏色緩存區中,通過雙重緩存的機制更新屏幕顏色。
二、GPU的渲染流水線
我們通常接觸的更多的是GPU的渲染流水線,CPU主要工作是將數據發送的顯存中,然后設置渲染狀態(SetPass的操作),然后調用Draw Call。在CPU和GPU的中間有一個指令緩沖隊列,CPU插入指令,GPU取出指令進行渲染,所以整體的渲染的工作主要還是在GPU上執行。現階段的Unity Shader的GPU渲染流水線主要為:

細分整個流水線,可以參考上面的流程圖以及參考書中的過程,可以細分為:
頂點數據—>頂點着色器—>曲面細分着色器—>幾何着色器—>裁剪—>投影—>三角形設置—>三角形遍歷—>片元着色器—>逐片元操作—>基本測試—>顏色緩存區
Unity提供的主要是Surface shader,但是其本質還是頂點/片元着色器的構成,所以很多的時候在實際項目中,應用頂點/片元着色器的居多。我們在編寫自己的shader的時候,更多的是在vertex/fragment這兩個部分的shader中編寫,當然涉及到其他的一些操作比如剔除、透明度、陰影等可以通過一些Unity shader中的語義指令來設置。
三、基本的光照知識
1、渲染路徑
在unity中,主要的渲染路徑分為三種:頂點光照渲染路徑、延遲渲染路徑、前向渲染路徑。頂點光照渲染路徑現在應用較少,逐漸走出應用的主流,現在應用較多的是前向渲染路徑和延遲渲染路徑。
在前向渲染路徑中,其基本的操作分為兩個部分:forwardBase和forwardAdd,在forwardBase中主要計算逐像素光源的渲染,在forwardAdd中計算其他光源的光照計算。在前向渲染路徑中,光照的渲染過程分為兩個部分,先進行深度的測試,如果通過深度測試,則執行對應的渲染操作。在前向渲染路徑中,每個逐像素光源都會對物體執行一次渲染,所以如果場景中有N個物體,M個光源,就會執行N*M次渲染,顯然這對於大場景和多像素光源的應用場景有一定的性能限制。
在延遲渲染路徑中,只會執行2個Pass的渲染操作,第一個pass主要執行基本的深度測試,並進行G-緩沖;在第二個pass中,基於G-緩沖的結果進行具體的光照計算等渲染操作。所以延遲渲染的意思是指渲染操作被延遲到第二個pass中執行了。這樣只用2個pass就實現渲染,可以提高渲染的性能。
2、基本光照模型
在shader中,應用的最為廣泛的光照模型是Phong光照模型:光照 = 環境光 + 自發光+ 漫反射光 + 高光反射光
1)環境光,通常可以直接利用UNITY_LIGHTMODEL_AMBIENT.xyz獲取到,自發光通常沒有采用,但是如果模型的頂點中帶有顏色,可以在模型將模型的頂點數據導出用作自發光,可以參考這兒:訪問頂點顏色
2)漫反射光
漫反射是shader中操作最多的一種光照,大部分的光照都是對其進行操作,其基本的計算公式為:
Ccolor=Cdiffuse * Clight * max(a,dot(Vnormal,Vlight))
這里解釋一下為什么需要max操作,因為光源方向和法線方向夾角未必是銳角,在為鈍角的時候得到的點乘結果為負數,這樣得到的光照是指模型背面部分的頂點光照計算,這部分計算其實是不需要的,所以都設置為0,max操作就是用來確保最終的計算結果大於等於0。
對於漫反射,在某些部分可能得到的光照顏色接近於灰暗,在某些情況下需要對光照結果做一個整體亮度的提高,所以V社提出了半Lambert的光照模型:
Ccolor= 0.5 * Cdiffuse + 0.5;
其實質就是將整體的計算結果做了一個縮放和偏移后,得到的變化曲線。
除了半Lambert的操作外,在某些時候,如果我們需要對模型進行一種漸變的處理,可以用一張漸變紋理來改變模型的漫反射計算,我會在后面的漸變紋理中提到這部分的操作。
3)高光反射
高光反射是另外一種應用較多的光照,其基本的計算公式為:
Ccolor = Cspecular * Clight * pow(max(Vreflect,Vview),mgloss)
Blinn-Phong的光照模型中,高光反射計算公式可以改為:
Ccolor = Cspecular * Clight * pow(max(Vnormal,Vhalf),mgloss)
其中Vhalf =normalize( normalize(Vview) + normalize(Vlight))
高光反射,主要的是實現對光照的高亮,但是某些時候需要對高亮的結果進行一定的平滑處理,所以可以采用遮罩紋理來處理這樣的操作,主要是通過遮罩紋理的某個通道來實現對高光的計算結果進行平滑處理。
3、基本的陰影和衰減
Unity中,對於衰減,主要是通過一張光照衰減紋理(_LightTexture0)的采樣得到,其采樣操作主要是在光照空間中進行。對於陰影,一般不會采用實時陰影的方式(性能耗費太高),但是現在隨着技術的發展,越來越多的游戲采用的實時光照,當然這是對整體的光照的優化有一個極其高的情況下。一般情況下都是將場景烘培,對模型自身添加相應的光照設置。
在unity中,主要是通過一個shadowCaster的shader來實現對陰影的計算,對於物體是否投射陰影可以在其自身的組件上設置。而unity將陰影和衰減的計算合並在一起,主要通過三個基本的內部操作來實現:在a2v的結構體中設置SHADOW_COORDS(n),在vert shader中進行TRANSFER_SHADOW(o),最后在frag shader中進行 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos),此時得到的atten就是綜合了衰減和陰影的結果,可以用其來作為衰減和陰影的因子與最終的光照顏色相乘。
