【Unity Shader入門】Shader基礎概念:渲染流水線

【Unity Shader入門】Shader基礎概念:渲染流水線
【Unity Shader入門】Shader編程基礎:ShaderLab語法
【Unity Shader入門】Shader數學基礎:向量(矢量)
【Unity Shader入門】Shader數學基礎:矩陣
【Unity Shader入門】Shader數學基礎:矩陣變換
【Unity Shader入門】Shader編程初級:Shader結構

前言

Shader(着色器):是可以在GPU上運行的一段程序,通過Shader可以進行一些渲染相關的設置。

GPU的優越性:
GPU具有高並行結構,所以GPU在處理圖形數據和複雜算法方面擁有比CPU更高的效率。CPU大部分面積爲控制器和寄存器,與之相比,GPU擁有更多的ALU(Arithmetic Logic Unit,邏輯運算單元)用於數據處理,這樣的結構適合對密集型數據進行並行處理。GPU採用流式並行計算模式,可對每個數據進行獨立的並行結算,所謂“對數據進行獨立計算”,既,流內任意元素的計算不依賴於其他同類型數據。
GPU缺陷:
由於“任意一個元素的計算不依賴於其他同類型數據”,導致“需要知道數據之間相關性”的算法,在GPU上難以得到實現,一個典型的例子就是射線與物體的求交運算。GPU中的控制器少於CPU,致使控制能力有限。

三大Shader編程語言(CG/HLSL/GLSL)
Shader Language的發展方向是設計出在便攜性方面可以和C++、Java等相比的高級語言,“賦予程序員靈活而方便的編程方式”,並“儘可能的控制渲染過程”同時“利用圖形硬件的並行性,提高算法效率”。
GLSL(OpenGL Shading Language)由OpenGL安委會提供,在OpenGL中進行着色器編程的語言
HLSL(High Level Shading Language)由Microsoft公司提供,通過Direct3D圖形軟件庫來編寫的着色器語言。
CG(C for Graphic)由NVIDIA公司和Microsoft公司合作提供,有自己的一套關鍵字和函數庫,獨立於三維編程接口,在Direct3D和OpenGL上都可工作。

ShaderLab: Unity 自己又封裝了一層CG/HLSL/GLSL的接口,但爲了實現跨平臺,Unity重點支持Cg着色器語言。

渲染流水線

應用階段:CPU負責(絕對控制權)
1、準備場景數據;
2、不可見剔除;
3、設置渲染狀態。

幾何階段:GPU負責。
重要任務是把頂點座標變化倒屏幕空間中,在交給光柵器進行處理。

光柵化階段:GPU負責。
使用上個階段傳遞的數據來產生屏幕上像素並最終渲染出圖像。

渲染流水線
應用階段
輸出渲染圖元
幾何階段
輸出屏幕空間的頂點信息
光柵化階段

CPU應用階段

1、把數據加載倒顯存中
2、設置渲染狀態
3、調用Draw Call

數據加載倒顯存中:將渲染所需數據從硬盤加載到內存中,網格紋理等數據又被加載到顯存中(一般加載到顯存後內存中的數據就會被移除)

設置渲染狀態:這些狀態定義了場景中網格是怎麼被渲染的。例如,使用哪個頂點着色器,片元着色器,光源屬性,材質等。

調用Draw Call:就是一個命令,它的發起方是CPU接收方是GPU。這個命令僅僅會指向一個需要被渲染的圖元列表,而不會包含任何材質信息。

GPU流水線

幾何階段和光柵化階段:
開發者無法擁有絕對的控制權,其實現的載體是GUP。GPU通過實現流水線化,大大加快了渲染速度。雖然我們無法完全控制這兩個階段的實現細節,但是GPU向開發者開放了很多控制權。

GPU流水線工作流程:

光柵化階段
幾何階段
三角形設置
三角形遍歷
片元着色器
逐片元操作
頂點着色器
曲面細分着色器
幾何着色器
裁剪
屏幕映射
頂點數據
屏幕映射
屏幕圖像
綠色代表完全可編程控制
黃色代表可配置但不可編程
藍色代表GPU固定實現

1.頂點數據:是由應用階段加載到顯存中,再由Draw Call指定的。這些數據隨後被傳遞給頂點着色器。
2.頂點着色器:是完全可編程的,它通常用於實現頂點的空間變換,頂點着色器等功能。
3.曲面細分着色器:是一個可選着色器,用於細分圖元。
4.幾何着色器:是可選着色器,可以被用於執行逐圖元的着色操作,或者被產生於更多的圖元。
5.裁剪:這一階段的目的是將那些不在的攝像機視野內的頂點裁剪掉,剔除某些三角圖元的面片。這個階段可配置。
6.屏幕映射:這一階段不可配置和編程,負責把每個圖元的座標轉換到屏幕座標系中。
7.三角形設置、三角形遍歷:都是光柵化階段的固定函數。
8.片元着色器:是完全可編程的,用於實現逐片元的着色操作。
9.逐片元操作:這個階段負責很多重要操作,如修改顏色,深度緩衝,進行混合等,不可編程,但是可配置。

頂點着色器

頂點着色器的處理單位是頂點,也就是說,輸入進來的每個頂點都會調用一次頂點着色器。頂點着色器本身不可以創建或者銷燬任何頂點,而且無法得到頂點與頂點直接的關係。GPU可以利用本身的特性快速處理每個頂點。

頂點着色器主要完成的工作:座標變化及逐頂點光照。當然除此之外還可以輸出後續階段所需數據等。(計算法線,模擬布料、波浪等頂點動畫)

//將座標點從模型空間轉換到裁剪空間
o.vertex = mul(UNITY_MVP,v.vertex); // unity5.x
o.vertex = UnityObjectToClipPos(v.vertex); // unity2017及以上
頂點着色器工作流程
模型空間轉到齊次裁剪空間工作流程
模型空間
世界座標空間
觀察空間
齊次裁剪空間
計算頂點顏色

裁剪

一個圖元與攝像機的關係有3種:完全在視野內,部分在視野內,完全在視野外。完全在視野內的圖元就繼續傳遞給下一個流水線階段,完全在視野外的圖元不會繼續向下傳遞,那些部分在視野內的需要進行裁剪。

屏幕映射

屏幕映射的任務是將裁剪後的齊次座標(NDC)轉換到屏幕座標系,屏幕座標系是一個二維座標系,和用於顯示畫面的分辨率有很大關係。

三角形設置

這個階段會計算光柵化一個三角形網格所需要的所有信息。上一個階段輸出的都是三角網格的頂點,既我們得到的是三角網格每條邊的兩個端點。如果要得到正規三角網格對像素的覆蓋情況,就必須計算每條邊上的像素座標。爲了能夠計算邊界像素的座標信息,就需要得到三角形邊界的表示方式。

三角形遍歷

三角形遍歷階段將會檢查每個像素是否被一個三角網格所覆蓋。如果被覆蓋的情況下,就會產生一個片元。而這樣一個找到那些像素被三角網格覆蓋的過程叫做三角形遍歷,也被稱作掃描變化。
三角形遍歷階段會根據上一個階段的計算結果來判斷一個三角網格覆蓋了哪些像素,並使用三角網格3個頂點的頂點信息對整個覆蓋區域的像素進行插值。

片元着色器

片元着色器的輸入是上一個階段對頂點信息插值的到的結果,具體來說是根據那些從頂點着色器中輸出的數據插值得到的。而其輸出是一個或者多個顏色值。
這一階段可以完成很多重要的渲染技術,其中最重要的技術之一就是紋理採樣。爲了在片元着色器中進行紋理採樣,我們通常會在頂點着色器階段輸出每個頂點對應的紋理座標,然後經過光柵化階段對三角網格的3個頂點對應的紋理座標進行插值後,就可以得到覆蓋的片元的紋理座標。

逐片元操作

逐片元操作時OpenGL中的說法,在DX中這個階段被稱作輸出合併階段。
(1)決定每個片元的可見性,涉及很多測試工作,例如深度測試,模板測試。
(2)如果一個片元通過了所有測試後,就需要把這個片元的顏色值和已經存儲在顏色緩衝區的色彩進行合併,或者說混合。

逐片元操作工作流程
片元
模板測試
深度測試
混合
顏色緩衝區

模板測試

模板測試,與之相關的時模板緩衝(Stencil Buffeer)。模板緩衝和顏色緩衝。深度緩衝幾乎是一類東西。如果開啓了模板測試,GPU首先讀取(使用讀取掩碼)模板緩衝區中該片元位置的模板值,然後將該值和讀取(使用讀取掩碼)到的參考進行比較,這個比較函數可以由開發者指定的,例如小於等於捨棄該片元,或者大於等於捨棄該片元。如果這個片元沒有通過測試,該片元就會被捨棄。不管一個片元有沒有通過模板測試,我們都可以根據模板測試和之後的深度測試結果來修改模板緩衝區,這個操作也是由開發者指定的。模板測試通常用於限制渲染區域,或者渲染陰影,輪廓渲染等。

深度測試

如果開啓了深度測試,GPU會把該片元的深度值和已經存在與深度緩衝中的深度值進行比較。這個比較函數也是可以由開發者設置的,例如小於時捨棄該片元,或者大於時捨棄該片元。通常這個比較函數時小於等於,既如果這個片元的深度大於等於當前深度緩衝區中的值,那麼就捨棄它。這是因爲我們總想只顯示出離攝像機最近的物體,而那些被其他物體遮擋的就不需要出現在屏幕上。和模板測試不同的是,如果一個片元沒有通過深度測試,它就沒有權力更改深度緩衝區的值。如果一個片元通過測試,那麼開發者可以指定是否要用這個片元的深度值覆蓋所有的深度值。

合併混合

合併,渲染過程是一個物體接着一個物體畫到屏幕上,而每個像素的顏色信息被存儲在一個名爲顏色緩衝的地方。因此,當我們執行這次渲染時,顏色緩衝中往往已經有了上次渲染之後的顏色結果,那麼,我們使用這次渲染得到的顏色完全覆蓋掉之前的結果還是進行其他處理,就是合併需要解決的。
對於不透明物體,開發者可以關閉混合(Blend)操作。這樣片元着色器計算得到的顏色值就會直接覆蓋掉顏色緩衝區中的像素值。但對於半透明物體,就需要混合操作來讓這個物體看起來是透明的。

各種測試總結

各種測試的順序並不是唯一的,雖然從邏輯上來說這些測試是在片元着色器之後進行的,但對於大多數GPU來說,會盡可能在執行片元着色器之前進行這些測試。因爲當你在片元着色器進行了大量的計算及設置,最後測試沒通過,可以說是計算成本全都浪費了。作爲一個想充分提高速度的GPU,肯定是希望儘可能早的指定哪些片元會被捨棄,對這些片元就不再需要在使用片元着色器來計算他們的顏色。Unity的渲染流水中,深度測試就是在片元着色器之前。

但是,如果將這些測試提前的話,其檢驗結果可能會與片元着色器中的一些操作衝突。例如片元着色器在進行透明度測試,而這個片元沒有通過透明度測試,我們會在着色器中調用API(Clip)函數來手動將其捨棄,這就導致GPU無法提前執行各種測試,因此,如果片元着色器中的操作和提前測試發生衝突就會禁用提前測試。這樣性能上就會下降,也是透明度測試導致性能下降的原因。

當模型圖元經過層層計算及測試後,就會顯示到屏幕上。我們的屏幕顯示的就是顏色緩衝區中的顏色值。但是,爲了避免我們看到正在光柵化的圖元,GPU會使用雙重緩衝策略。對場景的渲染時發生在幕後的,既在後置緩衝中,一旦場景已經被渲染到後置緩衝中,GPU就會交換後置緩衝區和前置緩衝的內容,前置緩衝區就是顯示在屏幕上的圖像。由此,保證我們看到的圖像是連續的。

注意:這裏的流水名,順序在不同資料上看到可能是不一樣的。一個原因是由於圖像編程接口的實現不盡相同,另一個是GPU底層可能做一些優化等等。

附加知識

1、CPU於GPU如何並行工作?

我們之前看到的是一個流水線式的模式,如果CPU和GPU並行工作,就需要使用命令緩衝區(Command Buffer)。
命令緩衝區包含了一個緩衝隊列,由CPU向其中添加命令,而GPU衝中讀取命令,添加和讀取過程是相互獨立的。命令緩衝區使得CPU和GPU可以相互獨立工作。當GPU需要渲染一些對象時,它就可以衝命令隊列中取出一個命令並執行。
命令緩衝區有很多種類,Draw Call就是一種。其他命令還有改變渲染狀態等。

2、什麼是固定管線渲染?

固定函數的流水線(Fixed-Function Pipeline),簡稱固定管線,通常是指在較舊的GPU上實現的渲染流水線。這種流水線只給開發者提供一些配置操作,但是開發者沒有對流水線階段的完全控制權。
在Unity中目前的固定管線shader都會自動編譯頂點片元shader。

3、什麼是Shader?

GPU流水線上一些可高度編程的階段,而由着色器編譯出來的最終代碼是會在GPU上運行的;
有一些特定類型的着色器,入頂點着色器、片元着色器等。
依靠着色器我們可以控制流水線中的渲染細節,例如用頂點着色器來進行頂點變化及傳遞數據,用於片元着色器來進行逐像素渲染。

SIKI學院 Unity Shader入門(Unity2018.3)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章