1.3:Render Pipeline and GPU Pipeline
文章著作權(quán)歸作者所有。轉(zhuǎn)載請聯(lián)系作者,并在文中注明出處,給出原文鏈接。
本系列原更新于作者的github博客,這里給出鏈接。
在學習SubShader之前,我們有必要對 Render Pipeline (渲染流水線)和 GPU Pipeline (圖形硬件流水線)有一個比較細致的了解。這是一篇干貨,內(nèi)容主要參考了《Unity Shader入門精要》、《Real-Time Rendering》以及眾多博客,其中加入了一些個人的見解,里面涉及到的知識能夠為我們以后的Shader編寫提供指導。有錯誤的地方歡迎聯(lián)系指正。
什么是流水線
在第0章我們簡單地提到了渲染流水線的大概過程。但是,只知道大概過程會給我們以后的學習帶來疑惑,所以我們還是要熟悉整個渲染的流程。在這之前,我們首先要清楚Pipeline(流水線)是什么。我們都知道,工廠的生產(chǎn)都是基于流水線的,那為什么我們會選擇使用這種模式呢?我們先回到傳統(tǒng)模式,假設(shè)一件商品需要經(jīng)過四道工序完成,而這四道工序都由同一個工人去完成。顯然,工序是拓撲有序的,也就是說,我們必須嚴格按照1-2-3-4的順序進行,在計算機上,這對應(yīng)著串行計算,這也就意味著,上一道工序如果沒有完成,我們便永遠無法開展下一步的工作,這無疑是低效的。在注重效率的現(xiàn)代社會,最重要的協(xié)作方式肯定是各司其職,所有人在同一個時間段完成不同的工作,再把各自的階段產(chǎn)物遞交給下一道工序的執(zhí)行者,這對應(yīng)著計算機的并行計算。因為各自負責了自己擅長的工作,在時間上又是同時開展的,效率顯然遠遠高過傳統(tǒng)方式。值得慶幸的是,GPU(圖形顯卡)的特長正是并行計算。
渲染流水線是怎樣運作的
了解了流水線模式的好處之后,可能會有這樣的疑問:為什么渲染也需要用到流水線呢?這是因為渲染工作也是由若干階段組成的。接下來我們將深入流水線中看看渲染的實質(zhì)。在《Real-Time Rendering》一書中,作者把渲染流程分為了三個概念階段,分別是Application Stage(應(yīng)用階段)、Geometry Stage(幾何階段)、Rasterizer Stage(光柵化階段),這也是目前被廣泛認可的一種描述。
應(yīng)用階段
在應(yīng)用階段,我們需要準備好場景數(shù)據(jù),如視角位置,光照設(shè)置,但最重要的輸出是Render Primitives(渲染圖元),這一階段在CPU中完成,對應(yīng)到Unity中就是我們需要在場景中擺放Light,設(shè)置Main Camera,擺放游戲物體,設(shè)置好所有的參數(shù)。
幾何階段
從上一階段獲取到圖元信息后,幾何階段會進行所有和幾何相關(guān)的工作,決定哪些圖元需要被繪制,需要怎樣繪制,在哪里繪制。處理之后,我們會得到每個頂點在二維屏幕空間的坐標,以及各頂點的深度、顏色信息,這些會被傳輸?shù)焦鈻呕A段。這一階段通常在GPU上進行。
純CPU的渲染流水線通常稱為軟渲染,即用軟件模擬硬件進行渲染操作。
光柵化階段
這一階段通常也在GPU進行,這個時候,渲染已經(jīng)接近尾聲。利用上一階段得到的數(shù)據(jù),我們可以在GPU的插值寄存器中進行插值運算得到足夠數(shù)量的像素信息,并最終確定逐像素確認,哪些像素應(yīng)該顯示在屏幕上。
CPU和GPU之間的通信
我們可以看到,應(yīng)用階段的數(shù)據(jù)在CPU中,這些數(shù)據(jù)是怎樣傳輸給GPU進行幾何階段的操作呢?
在CPU中,所有和渲染有關(guān)的數(shù)據(jù)都會進入顯存中,這是因為顯卡對于顯存的訪問速度更快,隨后,CPU會設(shè)置一些渲染狀態(tài),最后,CPU會調(diào)用Draw Call。Draw Call是一個CPU調(diào)度命令,它會指定那些需要被渲染的圖元并通知GPU,這些被指定的數(shù)據(jù)會通過數(shù)據(jù)總線傳輸?shù)紾PU中。Draw Call其實就是調(diào)用圖形編程語言(如DX,GL,Cg)的接口,通過這一層抽象與硬件層打交道。
數(shù)據(jù)總線是計算機內(nèi)部各設(shè)備之間交換設(shè)備的一個通道,既然是通道,那么它肯定有傳輸速度的上限,頻繁地提交Draw Call會導致CPU過載,這也是一個常見的性能瓶頸。
GPU流水線
應(yīng)用階段進行的計算都是為硬件層的渲染做準備,這個階段結(jié)束后,就正式進入了GPU的流水線中。在一些比較老的GPU中采用的是固定渲染流水線,這也就意味著所有的操作都是受限的,我們只能做一些簡單的配置。隨著硬件設(shè)備的發(fā)展,現(xiàn)代的圖形顯卡幾乎都支持可編程渲染流水線,定制化程度得到了提高。固定渲染流水線已經(jīng)逐漸被淘汰了,這里不對其展開說明,下面主要了解一下可編程渲染管線的各個階段:
Vertex Shader(頂點著色器)
頂點著色器是完全可編程的。輸入其中的每一個頂點都會調(diào)用一次頂點著色器,它無法創(chuàng)建和銷毀頂點,也無法獲取頂點之間的關(guān)系,但這一特性適合用來進行高速的并行計算。輸入頂點著色器的有頂點的位置信息,法線信息,切線信息等。它的主要工作是進行坐標變換和頂點光照計算最終得到Normalized Device Coordinates(NDC,歸一化的設(shè)備坐標)。這個坐標通常會在光柵化后傳遞給片元著色器進行處理。但是頂點著色器的作用遠不止于此,輸入頂點的法線、切線等信息也會在這一步進行處理,比如生成副切線,把頂點轉(zhuǎn)換到切線空間進行計算,或者進行法線外擴,實現(xiàn)描邊效果。
涉及到坐標變換,就繞不開矩陣和線性代數(shù),數(shù)學部分的內(nèi)容可以參考《3D數(shù)學基礎(chǔ):圖形與游戲開發(fā)》等圖書,或者參考3D數(shù)學概要。
Tessellation Shader(曲面細分著色器)& Geometry Shader(幾何著色器)
頂點著色器為了追求速度不得不舍棄一些操作,但這些舍棄的操作會在一定程度上影響畫面的美感。在硬件的支持下,便誕生了具有特異功能的曲面細分著色器和幾何著色器。這兩個著色器不可編程,但可以配置。
由于計算機的數(shù)據(jù)離散性,我們只能使用折線表示曲線,使用多平面表示曲面,如果粒度不夠,曲線和曲面就顯得沒那么平滑。而曲面細分著色器的作用就是解決這個問題:生成新的頂點,“插入”到直線上或平面內(nèi),讓曲線和曲面顯得更圓滑。
而幾何著色器的優(yōu)勢在于它可以創(chuàng)建和銷毀頂點。但這些創(chuàng)建出來的頂點不是用于細分,而是用于擴展;同時,它也可以銷毀那些我們不想輸出到下一階段的頂點。
由于Shaderlab的高度封裝性,Unity對這兩種著色器的支持度比較低。
經(jīng)過若干著色器的計算篩選,頂點規(guī)模已經(jīng)基本確定了,接下來對頂點做最后的處理。
Clipping(裁剪)
由于我們輸出的不可能是整個空間,出于性能考慮,我們自然會想到,舍去那些不會出現(xiàn)在屏幕上的頂點,這也就是裁剪。在這一階段,我們會把頂點變換到裁剪空間,裁剪空間是一個單位立方體空間,因此我們只需要判斷哪些線、面在立方體內(nèi),哪些在立方體外,即可知道我們真正需要處理的是哪些。特殊情況是,如果有一條直線或者一個平面部分可見,那么裁剪操作會在立方體邊界生成新的頂點,取代那些不會出現(xiàn)的頂點。裁剪操作雖然不可編程,但是我們可以定制裁剪視錐,遠近平面,視角大小等信息控制裁剪范圍,這一部分內(nèi)容也會在3D數(shù)學概要中有所體現(xiàn)。
Screen Mapping(屏幕映射)
現(xiàn)在我們已經(jīng)得到了屏幕內(nèi)的所有頂點信息,但它仍位于裁剪空間中,因此我們有必要把這些頂點映射到屏幕坐標系。映射過程中使用了兩個維度的坐標信息,而我們知道空間坐標是一個三維信息,丟失的那一維我們并沒有真正地舍棄,而是將他作為頂點的深度信息,為以后的片元操作提供依據(jù)。
至此,概念流水線的幾何階段工作就結(jié)束了,我們回顧一下,上述階段,我們接收了頂點的原始信息,最終得到的是渲染所需的屏幕坐標,頂點深度值等信息。需要注意的是,在Shaderlab中編寫的頂點著色器包含了裁剪部分,這是因為接下來這些頂點數(shù)據(jù)會被提交給光柵化階段,這一階段接受的輸入是裁剪空間下的信息。接下來是光柵化階段的工作了。
首先是光柵化以及插值過程,它包含了三角形設(shè)置和三角形遍歷,目的是計算圖元覆蓋的像素。
Triangle Setup(三角形設(shè)置)
計算三角形網(wǎng)格表示的數(shù)據(jù)。
Triangle Traversal(三角形遍歷)
這個階段接收的數(shù)據(jù)仍是頂點級別。這里是真正柵格化數(shù)據(jù)的階段,這個階段會逐像素檢查其是否有被三角形網(wǎng)格覆蓋,如果有,就生成一個片元。因此,它也被稱為掃描變換過程,覆蓋信息計算完成后,整個覆蓋區(qū)域會使用頂點信息進行插值,生成像素級別的數(shù)據(jù),這些數(shù)據(jù)會傳遞到片元著色器中。
這些像素級別的數(shù)據(jù)仍然是以片元為載體的,并不真正對應(yīng)屏幕上的像素。
接下來是片元著色器環(huán)節(jié),也是第二個和最后一個完全可編程環(huán)節(jié)。
Fragment Shader(片元著色器)
在DirectX中,它也被稱為Pixel Shader(像素著色器),但個人感覺片元是更適合的名稱,因為這個階段的輸出并不會真正影響屏幕的像素顏色,接下來還有逐片元操作,對這些片元進行篩選,以確定最終顯示的顏色。這一階段最重要的技術(shù)是紋理采樣。為了得到采樣結(jié)果,我們通常會在頂點著色器中計算每個頂點的紋理坐標,采樣器會根據(jù)這個坐標采樣紋理數(shù)據(jù)。由于我們已經(jīng)在頂點著色器中計算好了每個頂點的顏色信息,也在上一階段得到了像素級別的插值顏色,因此現(xiàn)在我們只需要根據(jù)我們想實現(xiàn)的效果,做相應(yīng)的顏色計算即可。
Per-Fragment Operations(逐片元操作)
在DirectX中也稱為Output Merger(輸出合并)。這一階段具有高度的配置性。進行到這里,渲染工作也基本完成了。經(jīng)過上一階段,我們得到了許多色彩斑斕的片元,是時候進行最后的篩選了。在這一階段,我們會對每一個片元都進行一系列的測試操作以及最終的混合操作,目的是確定這個片元是否可見,以及可見時它的顏色對應(yīng)的權(quán)重。測試主要有Stencil Test(模板測試)和Depth Test(深度測試),在測試前,首先要判斷這個片元是否開啟了對應(yīng)的測試操作。在測試中,我們會利用對應(yīng)的緩沖和片元進行比較,對應(yīng)的有Stencil Buffer(模板緩沖)和Depth Buffer(深度緩沖)。通過了模板測試的片元會被保留,同時更改模板緩沖區(qū)的值,隨后進行深度測試(假設(shè)這個片元同時開啟了兩種測試)。深度測試具有更高的配置性。即使這個片元通過了深度測試,我們也可以關(guān)閉深度寫入,讓這個片元的深度值不影響深度緩沖區(qū)。透明效果的實現(xiàn)離不開深度測試的高配置性。
經(jīng)過深度測試后我們會發(fā)現(xiàn),我們舍棄了許多片元,這樣也就意味著這些片元對應(yīng)的片元著色環(huán)節(jié)所做的一切都是徒勞。自然我們會想,能否將深度測試提前?答案是肯定的,這項技術(shù)被稱為Early-Z,它會在片元著色器之前進行深度測試,但這并不意味著我們可以舍棄真正的深度測試環(huán)節(jié)。如果我們把深度測試提前,這些檢驗結(jié)果可能會和片元著色器的某些操作發(fā)生沖突,這個時候我們就不得不放棄Early-Z,選擇傳統(tǒng)的深度測試。
通過測試的片元會來到最后一個環(huán)節(jié),Blend(混合),與之對應(yīng)的是顏色緩沖區(qū)。我們可以選擇開啟/關(guān)閉混合操作。對于不透明的物體,我們會關(guān)閉混合操作,這樣,這個片元的顏色會完全覆蓋掉顏色緩沖區(qū)的像素值;對于半透明物體,我們需要開啟混合選項,以達到透明效果。這一階段也是高度配置的,我們可以自定義混合的比例。Photoshop的圖層混合模式的實現(xiàn)也是類似的操作。
GPU流水線之后
當所有片元都經(jīng)過逐片元操作后,我們得到的顏色緩沖區(qū)就可以作為最終呈現(xiàn)在屏幕上的圖像了。但一般來說我們會把這個顏色緩沖區(qū)的內(nèi)容輸出到Frame Buffer(幀緩沖)。這是因為光柵化過程的進度是不可知的,也就是說有可能我們讀取的屏幕圖像還包含了部分上一幀的內(nèi)容(這個轉(zhuǎn)場很炫酷,但并不是我們希望看到的)。對應(yīng)的解決方案是,使用Double Buffer(雙緩沖)技術(shù),我們準備兩個緩沖區(qū),一個用于當前屏幕圖像顯示,一個用于幕后渲染下一幀的圖像。不僅如此,我們得到了幀緩沖后,還可以進行Post-Processing(屏幕后處理),實現(xiàn)更豐富的視覺效果。
最后
我們已經(jīng)梳理了一遍GPU渲染的詳細過程,但由于抽象層(Shader Language)提供給我們的接口的區(qū)別,以及GPU為我們做的額外優(yōu)化,許多細節(jié)或者順序可能不盡相同,但也足以讓我們對計算機圖形學和渲染有比較清晰的認識了,至少可以知道計算機,或者說硬件,在背后為我們做了些什么。
下面給出一張流水線對照圖以供參考(打開圖片可查看原圖)。
在Shaderlab中,頂點和片元著色器可以通過插入CG/GLSL代碼塊實現(xiàn),剔除階段和逐片元操作則可以通過Tags和CommonState實現(xiàn)高度配置。
轉(zhuǎn)載于:https://www.cnblogs.com/Li-F/p/10635587.html
總結(jié)
以上是生活随笔為你收集整理的1.3:Render Pipeline and GPU Pipeline的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Istio 1.1.1 发布,修复漏洞并
- 下一篇: redhat6.下安装配置hadoop环