Unity Shader學習-1.渲染流水線

先了解一下什麼是流水線:(下面是百度百科的定義)

流水線:流水線又稱爲裝配線,一種工業上的生產方式,指每一個生產單位只專注處理某一個片段的工作,以提高工作效率及產量

一、什麼是渲染流水線

1.概念

渲染流水線的概念與上面說到的流水線的概念也是一致的,其主要任務是輸入一個三維場景,然後輸出一個二維的圖像。這個過程是由CPUGPU一起完成的。

2.渲染流水線的三個階段

渲染流水線可以分爲三個階段:應用階段幾何階段光柵化階段

a.應用階段(CPU處理)

這一階段是由開發者主導的,在這一階段中開發都有3個主要的任務:
- 首先,需要準備好場景數據(攝相機位置、視錐體、模型和光源等)
- 接着,還需要做粗粒度的剔除工作
- 最後,需要設置好每個模型的渲染狀態(使用的材質、使用的紋理、使用的Shader等)

這一階段最重要的輸出是渲染所需的幾何信息,即渲染圖元,渲染圖元可以是點、線、三角面等。

b.幾何階段(GPU處理)

幾何階段主要用於處理所有和我們繪製的幾何相關的事情。幾何階段負責和每個渲染圖元打交道,進行逐頂點逐多邊形的操作。這個階段可以進一步分成更小的流水線階段。
幾何階段的一個重要任務就是把頂點座標變換到屏幕空間中,再交給光柵器進行處理。
總結:輸入的渲染圖元->屏幕空間的二維頂點座標、每個頂點對應深度、着色等信息

c.光柵化階段(GPU處理)

將會使用上一個階段傳遞的數據來產生屏幕上的像素,並渲染出最終的圖像。主要任務是決定每個渲染圖元中的哪些像素應該被繪製在屏幕上。

3.CPU與GPU之間的通信

渲染流水線的起點是CPU,即應用階段。應用階段可以分爲下面3個階段:

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

1.數據加載到顯存中

基本步驟就是紋理、網格等數據從硬盤加載到系統內存再加載到顯存。數據加載到顯存後系統內存中的數據就可以被移除了,但是對於一些數據來說CPU需要訪問他們,例如用於碰撞檢測用的網格數據,這些數據則會被保留。

2.設置渲染狀態

渲染狀態指的是場景中的網格是如何被渲染的,例如使用哪個Vertex Shader或者哪個Fragment Shader、光源屬性、材質等。

3.調用Draw Call

`Draw Call`指的是一個命令,發起方爲CPU,接收方爲GPU。當給定了一個Draw Call時,GPU會根據渲染狀態(例如材質、紋理、着色器等)和所有輸入的頂點數據進行`計算`,最終輸出成在屏幕上的像素。這個計算的過程就是GPU流水線。

4.GPU流水線

當GPU收到CPU發送過來的Draw Call指令後,會進行一系列的流水線操作,最終把圖元渲染到屏幕上。對於幾何階段光柵化階段其實現的載體是GPU,而開發者對這兩個階段的實現細節無法完全控制。而GPU流水線可以細分成不同的流水線階段

Alt text

(1)頂點着色器:

從圖中可以看到GPU的流水線接收頂點數據作爲輸入,接着頂點數據被傳遞給頂點着色器。頂點着色器的處理單元是頂點,輸入進來的每個頂點都會調用一次頂點着色器。(頂點着色器本身不可以創建或銷燬任何頂點,並無法得到頂點與頂點之間的關係)

頂點着色器是完全可編程的,它主要完成的工作有:座標變換和逐頂點光照。
- 座標變換:就是對頂點的座標進行某種變換—把頂點座標從模型空間轉換到齊次裁剪空間。我們可以通過座標變換來模擬水面、面料等。

(2)曲面細分着色器:

是一個可選的着色器,主要用於細分圖元。

(3)幾何着色器:

是一個可選的着色器,可用於執行逐圖元的着色操作,或者被用於產生更多的圖元。

(4)裁剪:

這一階段是可配置的。目的是把那些不在視野內的頂點裁剪掉,並剔除某些三角形圖元的面片。
一個圖元與攝像機視野的關係有3種:完全在視野內、部分在視野內、完全在視野外。
- 完全在視野內的圖元會繼續傳遞給下一個流水線階段
- 完全在視野外的圖元不會繼續向下傳遞
- 部分在視野內的圖元需要做裁剪處理

和頂點着色器不同的是,這一步是不可編程的。我們無法通過編程來控制裁剪的過程,而是硬件上的固定操作。

(5)屏幕映射:

這一階段是可配置和編程的,負責把每個圖元的座標(三維座標系)轉換成屏幕座標(二維座標系)
。這一步輸入的座標仍然是三維座標系下的座標。屏幕映射得到的屏幕座標決定了這個頂點對應屏幕上哪個像素以及距離這個像素的距離。
注意:OpenGL把屏幕的左下角當成最小的窗口座標值,而DirectX則定義屏幕的左上角爲最小的窗口座標值。(就不能統一一下嗎。)
Alt text
這樣的差異會給開發者造成不少的坑,如果你發現開發過程中得的圖像是倒轉的,那可能是因爲這個問題。

(6)三角形設置:(開始進入光柵化階段)

中文名 光柵化
釋 義: 將圖轉化爲一個個柵格組成的圖象
過 程: 把頂點數據轉換爲片元的過程
特 點: 每個元素對應幀緩衝區中的一像素

上一階段我們可以得到的數據是屏幕座標系下的頂點位置以及和它們相關的其他信息,如深度值、法線方向、視角方向等。
光柵化階段的目標:1.計算每個圖元覆蓋了哪些像素 2.爲這些像素計算它們的顏色

三角形設置作爲光柵化的第一個階段,會計算出光柵化一個三角網格所需要的信息。上一階段的輸出是三角網格的頂點,如果我們想得到整個三角網格的覆蓋情況,就必須計算每條邊上的像素座標而得到三角形邊界的表示方式。這樣一個得到三角形邊界表示方式的過程就是三角形設置。

(7)三角形遍歷:

這一階段會檢查每個像素是否被一個三角風格所覆蓋。如果覆蓋的話,就會生成一個片元,這樣一個查找哪些像素被三角形覆蓋的過程就是三角形遍歷。

上一階段得到的三角網格的表示形式會在這一階段用來判斷一個三角網格覆蓋了哪些像素,並使用三角網格的3個頂點信息對整個覆蓋區域的像素進行插值。這一階段輸出的是片元的序列。
注意:一個片元並不是真正意義上的像素,而是包含了很多狀態的集合,這些狀態用於計算每個像素的最終顏色。這些狀態包括了屏幕座標、深度信息,及從幾何階段輸出的頂點信息,如法線和紋理座標等。

(8)片元着色器:

片元着色器的輸入就是上一階段對頂點信息插值得到的結果,更具體點說,是根據從頂點着色器中輸出的數據插值得到的。而這一階段的輸出是一個或者多個顏色值。這一階段可以完成很多重要的渲染技術,如紋理採樣,但是它的侷限在於,它僅可以影響單個片元。

(9)逐片元操作:(渲染流水線最後一步)

這一階段的目的是:合併。那麼是合併哪些數據呢?
這一除非的幾個主要任務:
- 決定每個片元的可見性。這涉及了深度測試、模板測試等
- 如果一個片元通過了所有的測試,那麼就要把這個片元的顏色值和已經存儲在顏色緩衝區中的顏色進行合併,或者說是混合。

這一階段是可編程的,我們可以設置每一步的細節。

可見性:

這一階段顏色首先要解決每個片元的可見性問題,每個片元需要進行以下的測試,如果在中間任何一個階段沒通過測試的都將被捨棄。
Alt text


模板測試:
如果開啓了模板測試,GPU會首先讀取模板緩衝區中該片元位置的模板值,然後將該值和讀取到的參考值(可以開發者自己指定)進行比較,開發者可以設定成小於時捨棄或者大於等於時捨棄該片元。模板測試通常用於限制渲染的區域,另外模板測試還有別的高級用法,如渲染陰影,輪廓渲染。

深度測試:
如果一個片元幸運的通過了模板測試,那麼就會進行深度測試,如果開啓了深度深度,GPU就會把該片的深度值和已經存在於深度緩衝區中的深度值進行比較,這個比較函數也是由開發者設定的,可以選擇大於此值時捨棄也可以選擇小於等於此值時捨棄。但通常這個比較函數是小於等於的關係,這是因爲我們我們總想只顯示出離攝像機最近的物體,而那些被其他物體遮擋的片元就不需要出現在屏幕上。和模板測試不同的是,如果一個片元沒有通過深度測試,他就沒有權利修改深度緩衝區中的值。而如果通過了測試,開發者可以通開啓/關閉深度寫入來決定是否要利用這個片元的深度值覆蓋緩衝區中的值。

混合:
對於不透明物體,開發者可以關閉混合操作,這樣片元着色器計算得到的顏色值就會直接覆蓋掉顏色緩衝區中的像素值。但對於半透明的物體,我們就需要開啓混合操作來讓這個物體看起來是半透明的。混合操作是高度可配置的,開發者可以選擇開啓/關閉混合功能。如果開啓了,GPU會取出源顏色和目標顏色,將兩種顏色進行混合。源顏色是指片元着色器得到的顏色值,而目標顏色則是已經存在於顏色緩衝區中的顏色值。

透明度測試會導致性能下降的原因:如果我們在片元着色器中進行了透明度測試,而這個片元沒有通過透明度測試,我們會在着色器中調用API(例如clip操作)來手動將其捨棄掉。這就會導致GPU無法提前執行各種測試。所以現代的GPU會判斷片元着色器中的操作是否和提前測試發生衝突。如果有衝突,就會禁用提前測試。但這樣會導致更多的片元需要進行處理了,因爲性能會下降

二、關鍵詞答疑

1.OpenGL和DirectX

開發者直接訪問GPU是一件非常麻煩的事情,可能需要與各種寄存器、顯存打交道,而圖像編程接口在這些硬件的基礎上實現了一層抽象。
而OpenGL和DirectX就是這些圖像應用編程接口,他們之間江湖恩怨,可以去看這篇文章。這些接口架起了上層應用程序與底層GPU的溝通橋樑。上層應用程序向這些接口渲染命令,而這些接口會依次向顯示驅動發送渲染命令,而顯卡驅動會把這些命令翻譯成GPU能聽懂的語言來讓他們進行工作。

2.HLSL、GLSL和CG

這三個指的都是着色器的編程語言。
- HLSL:High Level Shading Language,DirectX的着色器語言,由微軟控制着色器的編譯,就算使用了不同的硬件,其編譯結果也是一樣的,其使用的平臺比較侷限,幾乎都是微軟自己的產品,如Windows、Xbox 360等

  • GLSL:OpenGL Shading Language,OpenGL的着色器語言,優點在於其跨平臺性,可以在Windows、Mac、Linux甚至移動平臺使用,這種跨平臺性是由於OpenGL沒有提供着色器編譯器,而是由顯卡驅動來完成着色器的編譯工作的。即只要顯示驅動支持對GLSL的編譯它就可以運行。

  • CG:C for Graphics,NVIDIA的着色器語言,實現了真正意義上的跨平臺,它會根據平臺不同,編譯成相應的中間語言。

3.Draw Call

Draw Call本身的意義很簡單,就是CPU調用圖像編程接口。

1.CPU和GPU是如何實現並行工作的?

主要的解決方案是命令緩衝區,命令緩衝區包含了一個命令隊列,由CPU向其中添加命令,而由GPU從中讀取命令,添加和讀取的過程是獨立的。這樣使得CPU和GPU可以相互獨立工作。當CPU需要渲染對象時,則向命令緩衝區中添加命令,而當GPU完成上一次渲染任務後,它就可以從命令隊列中取出一個命令並執行它。

2.爲什麼Draw Call多了會影響幀率?

在每次調用Draw Call之間,CPU需要向GPU發送很多內容,包括數據、狀態和命令。CPU需要完成很多工作,例如檢查渲染狀態等。而一旦CPU完成了這些準備工作,GPU就可以開始本次的渲染。GPU渲染的速度是比較CPU提交指令的速度要快很多的。所以性能的瓶頸會出現在CPU身上,如果Draw Call的數量太多,CPU就會把大量的時間花費在提交Draw Call上,造成CPU過載。

3.如何減少Draw Call?

主要的解決方案是批處理(Batch),把衆多小的合併Draw Call合併成一個Draw Call,當然不是所有情況都能合併的。我們可以對網格進行合併,但是合併的過程是比較消耗時間的,因此批處理技術更適合於靜態的網格。
合併需要注意的點:

  • 避免使用大量很小的網格,當不可避免的要使用這些這麼小的網格時,考慮是否可以合併他們。
  • 避免使用過多的材質,因爲相同的材質會方便我們進行合併

4.什麼是固定函數的流水線?

簡稱固定管線,通常是指在舊GPU上實現的渲染流水線。開發者沒有對流水線完全控制權,只有一些配置操作,配置操作只有開和關

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