??在微軟DirectX10.0規(guī)范的統(tǒng)一渲染架構(gòu)發(fā)布以前,渲染管線曾經(jīng)是選購顯卡的一項重要指標(biāo)。然而采用流處理器渲染架構(gòu),由于硬件工作效率更高,目前已經(jīng)逐漸取代了采用渲染管線的傳統(tǒng)架構(gòu),在消費領(lǐng)域渲染管線的概念慢慢被淡化了。但是在計算機(jī)圖形學(xué)領(lǐng)域,渲染管線卻依然有著舉足輕重的地位,因為它依然是實時渲染的基本原理。雖然在上一章提到了渲染管線這一概念,但并沒有給出詳細(xì)的解釋,在這一章我要對渲染管線進(jìn)行一個深入的分析。
??渲染就是把3D場景轉(zhuǎn)換到屏幕上2D圖像的一個處理過程。
3D渲染過程
??有了渲染概念,渲染管線的概念就更加清晰了,它是顯示芯片內(nèi)部處理圖形信號相互獨立的的并行處理單元。每個階段從上一個階段接收輸入,處理完成后輸出到下一個階段,整個過程就是為了完成一幀畫面渲染。在某種程度上可以把渲染管線比喻為工廠里面常見的各種生產(chǎn)流水線,工廠里的生產(chǎn)流水線是為了提高產(chǎn)品的生產(chǎn)能力和效率,而渲染管線則是提高顯卡的工作能力和效率。 ??在這里還得解釋一下渲染管線和GPU渲染管線的關(guān)系。在GPU問世的初期,處理能力非常有限,渲染管線很大程度上依賴于CPU,整個管線的任務(wù)是由CPU和GPU共同協(xié)作完成的。當(dāng)然隨著GPU日益強(qiáng)大,GPU承擔(dān)了管線中所有的任務(wù),所以現(xiàn)在渲染管線這一概念就等同于GPU渲染管線了。
??在Real-Time Rendering一書中,作者把渲染管線分成了三個階段:應(yīng)用程序階段、幾何階段以及光柵階段。 ??應(yīng)用程序階段,使用高級編程語言進(jìn)行開發(fā),主要和CPU 、內(nèi)存打交道,諸如碰撞檢測、場景圖建立、空間八叉樹更新、視錐裁剪等經(jīng)典算法都在此階段執(zhí)行。在該階段的末端,幾何體數(shù)據(jù)(頂點坐標(biāo)、法向量、紋理坐標(biāo)、紋理等)通過數(shù)據(jù)總線傳送到圖形硬件。 ??幾何階段,主要負(fù)責(zé)頂點坐標(biāo)變換、光照、裁剪、投影以及屏幕映射,該階段基于 GPU 進(jìn)行運算,在該階段的末端得到了經(jīng)過變換和投影之后的頂點坐標(biāo)、顏色、以及紋理坐標(biāo)。 ??光柵階段,基于幾何階段的輸出數(shù)據(jù),為像素正確配色,以便繪制完整圖像,該階段進(jìn)行的都是單個像素的操作,每個像素的信息存儲在幀緩存中。 ??但是我并不完全認(rèn)同Real-Time Rendering中的階段劃分,目前主流的階段劃分方式只有后面兩個階段。盡管應(yīng)用程序階段是圖形渲染的重要一環(huán),但是它處理的任務(wù)只是優(yōu)化渲染過程的輸入數(shù)據(jù)以及相關(guān)邏輯處理,并不是真正的核心的渲染過程,而且該過程主要在CPU中完成。所以在接下來我將詳細(xì)地闡述這兩個階段的過程。 ??渲染管線根據(jù)硬件架構(gòu)的不同分為固定渲染管線和可編程渲染管線,焦點在于GPU是否具有可編程性。
??在可編程渲染管線出現(xiàn)之前,渲染管線就是指固定渲染管線,后來為了便于區(qū)分才添加了“固定”二字。固定渲染管線具備了渲染管線流水線作業(yè)的優(yōu)勢,將復(fù)雜的過程分解成小的階段,幾何數(shù)據(jù)按工作流程依次經(jīng)過各個階段的處理,大大增強(qiáng)了渲染作業(yè)的處理效率。同時幾何階段和光柵階段算法都是固化在GPU中,編程人員在開發(fā)時無須關(guān)注幾何階段的各種變換算法以及光柵階段的著色算法,開發(fā)效率大幅提高。圖2- 2展示了固定渲染管線的整個流程。
固定渲染管線流程
??幾何階段的主要工作就是三維頂點坐標(biāo)變換和光照計算,由顯卡中的“T&L”硬件來完成。 ??什么是“T&L”硬件?“T&L”英文全稱是“Transform&Lingting”,中文意思幾何變換和光照?!癟&L”硬件其實一個硬件級別的幾何與光照轉(zhuǎn)換引擎,它在GPU中的出現(xiàn),使得CPU從復(fù)雜運算中解脫出來。這樣一來,一個場景中可以添加更多的物體和幾何細(xì)節(jié),最終渲染效果更加細(xì)膩,同時渲染效率也大幅提升。 ??那么為什么需要進(jìn)行三維頂點坐標(biāo)的變換?原因很簡單,我們的真實世界是一個三維的空間,而顯示屏幕是二維的平面,為了將三維的數(shù)據(jù)更加真實地繪制到屏幕上,并達(dá)到“躍然紙上”的效果,我們就需要頂點變換。通過一系列頂點變換,物體在世界空間被轉(zhuǎn)換到屏幕空間。 ??根據(jù)頂點坐標(biāo)變換的先后順序,主要有這樣幾個坐標(biāo)空間:物體空間、世界空間、觀視空間以及屏幕空間。
物體空間到世界空間
??物體空間的是一個相對獨立的局部空間,與其他物體沒有任何參照關(guān)系。物體空間中的坐標(biāo)都是在3DS MAX這類建模工具中生成的。 ??而世界空間與物體空間的關(guān)鍵區(qū)別就在于前者中所有的物體把需要坐標(biāo)原點作為參考點。物體導(dǎo)入場景中之后,需要為物體指定一個位置,這個位置就是物體在世界空間的一個全局坐標(biāo)。物體空間到世界空間的變換由一個一個四階矩陣來完成,稱作世界矩陣。 ??光照計算通常是在世界坐標(biāo)空間中進(jìn)行的,這也符合人類的生活常識。當(dāng)然,也可以在觀視空間中得到相同的光照效果,因為,在同一觀察空間中物體之間的相對關(guān)系是保存不變的。 在這里有一點非常重要。就是物體頂點的法相量是在物體空間中生成的,它也是需要變換到世界空間才能正常使用,和頂點坐標(biāo)類似。但是二者的轉(zhuǎn)換矩陣卻是不一樣的,法向量變換的矩陣是世界矩陣轉(zhuǎn)置的逆矩陣。在固定管線中,雖然這一變換不需要我們手動完成,但是理解清楚對于后面的可編程管線非常重要。
世界空間到觀視空間
??每個人都是從各自的視點出發(fā)觀察這個世界,無論是主觀世界還是客觀世界。同樣,在計算機(jī)中每次只能從唯一的視角出發(fā)渲染物體。在游戲中,都會提供視點漫游的功能,屏幕顯示的內(nèi)容隨著視點的變化而變化。這是因為GPU 將物體頂點坐標(biāo)從世界空間轉(zhuǎn)換到了觀視空間。 ??所謂觀視空間,即以視點為原點,由視線方向、視角和遠(yuǎn)近平面,共同組成一個梯形體的三維空間,稱之為視錐,如圖所示。近平面,是梯形體較小的矩形面,作為投影平面,遠(yuǎn)平面是梯形體較大的矩形,在這個梯形體中的所有頂點數(shù)據(jù)是可見的,而超出這個梯形體之外的場景數(shù)據(jù),會被視錐裁剪去掉。
視錐
觀視空間到投影空間
??理論上講視錐剔除應(yīng)該在投影之前完成,事實上這樣也是可以的,但是有一個問題就是視錐是一個不規(guī)則的幾何體,在其中完成多邊形剔除并不是一件很容易的事。所以目前采用的方式是先投影,后剔除。 ??但是這個投影并非簡簡單單投影到一個二維平面上,而是將觀視空間中的場景投影到一個單位立方體中,俗稱CCV(Canonical view volume)[5]。三維場景投影到三維空間,聽起來也許很詫異。其實本質(zhì)上還是投影到了二維平面上,因為CCV近平面的X、Y坐標(biāo)與屏幕坐標(biāo)相對應(yīng),Z坐標(biāo)表示像素的深度值。我們常用的投影方式有兩種:正投影和透視投影。從人眼觀察世界的物理原理上來看,透視投影更加符合人類的視覺習(xí)慣。 從數(shù)學(xué)上來講,這個投影過程就是觀視空間的頂點坐標(biāo)乘以投影矩陣,頂點就變換到了投影空間CVV中。 ??視錐剔除過程現(xiàn)在變得簡單一些了,因為視體由視錐變成了CVV,位于CVV外面的圖元全部被裁剔除,部分位于CVV之外的需要進(jìn)行裁剪。這一過程在下面的圖元裝配時完成。
圖元裝配
??頂點流經(jīng)過變換后,按照管線的順序,下一個流程就是圖元裝配。所謂圖元裝配,就是根據(jù)之前的圖元分類信息把頂點裝配成圖元。在這個過程中,將生成一些三角形、線段和點。之前是對頂點處理,現(xiàn)在就是對圖元就行裁剪,對于超出屏幕之外的圖元進(jìn)行裁剪。對于游戲來講,一般裝配的圖元都是三角形,如果三角形的一部分在屏幕外面,那么經(jīng)過裁剪之后就變成了四邊形,這個四邊形又需要分割成兩個小三角形。到現(xiàn)在這個階段,就只剩下基本的圖元,不存在復(fù)雜的幾何圖形。 ??到此為止,渲染管線的幾何階段就完成了,幾何階段的工作比較簡單明晰,就是頂點變換和光照。接下來就是非常重要的光柵階段。
光柵化
??圖元裝配后生成的圖元要顯示在屏幕上必須經(jīng)過光柵化。光柵化就是決定哪些像素被幾何圖元覆蓋的過程。每種圖元根據(jù)指定的規(guī)則分別被光柵化,涉及一些填充算法如掃描線多邊形填充算法、邊界填充算法等,這里不做討論。光柵化的結(jié)果就是片段位置的集合。當(dāng)光柵化之后,一個圖元擁有的頂點數(shù)目和產(chǎn)生的片段數(shù)目之間沒有任何關(guān)系。比如,一個由三個頂點組成的三角形,占據(jù)整個屏幕需要上百萬的片段。 ??這個有必要解釋一下片段的概念。說到片段,不得不提到像素。像素是圖像元素的簡稱。一個像素代表幀緩存中某個指定位置的信息,比如顏色、深度和其他與這個位置關(guān)聯(lián)的值。而片段就是像素形成之前的一個狀態(tài),可以算作潛在的像素。在完成最后的光柵操作,更新片段信息至幀緩存后,片段就成了名副其實的像素。
插值、貼圖以及著色
??當(dāng)圖元被光柵化成一個或者多個片段后,就會根據(jù)需要對片段進(jìn)行插值、執(zhí)行一系列的貼圖以及數(shù)學(xué)操作,然后為每個片段確定最終顏色。除了確定最終的顏色意外,還需要確定最終的深度值,或者丟棄這個片段以避免更新幀緩存中對應(yīng)像素。
光柵操作
??光柵操作是在更新幀緩存之前,執(zhí)行的最后的一系列片段操作。這些操作是Direct3D和OpenGL的一個標(biāo)準(zhǔn)組成部分。 光柵操作會根據(jù)許多測試來檢查每一個片段,這些測試包括裁剪測試、alpha測試、模板測試以及深度測試。如果其中一項測試不通過,片段就會被丟棄。如果所有測試都通過,執(zhí)行完后續(xù)操作后,就會更新幀緩存中的對應(yīng)像素值。
標(biāo)準(zhǔn)OpenGL和Direct3D的光柵操作流程
??固定渲染管線看似很完美,預(yù)制好的算法,操作簡單,整個流程各環(huán)節(jié)分工明確,配合緊密。但是這樣經(jīng)典的模型同樣存在著諸多短板,導(dǎo)致整個渲染的效率和質(zhì)量很難再大幅提升。曾經(jīng)的優(yōu)勢隨著科技的進(jìn)步反倒成了劣勢,算法都固化在硬件內(nèi)部,所有開發(fā)人員只能采用同一種固定的渲染方式,這樣極大的束縛了開發(fā)人員的常造型思維。固定渲染管線已經(jīng)完全不能滿足時代對于渲染的要求,所以一種新的渲染模型——可編程渲染管線誕生了。 可編程渲染管線顧名思義,它的關(guān)鍵就在于GPU內(nèi)部提供了可編程性。這種可編程性的出現(xiàn),對開發(fā)人員來說是一種思想的解放。
??對比一下可編程渲染管線與固定渲染管線的流程圖,可以很明顯的發(fā)現(xiàn)他們之間的共性和差異。 ??關(guān)于共性,整個渲染流程大體上都是一致的,都是分為幾何階段和光柵階段,其中流水線中頂點變換、圖元裝配、光柵化、插值、光柵操作等關(guān)鍵操作都是一致的。只是在具體操作中采用的方式不同而已。 ??重點就在于二者的差異??梢钥吹綀D中的可編程渲染管線多了兩條分支——可編程頂點處理器和可編程片段處理器。在幾何階段沒有了一系列坐標(biāo)變換的過程,這緣于“T&L”硬件被移除了,取而代之的是可編程頂點處理器,頂點變換和光照計算都由它來完成。在光柵階段,顏色計算過程由可編程片段處理器取代了。 ??接下來將要詳細(xì)介紹可編程處理器。
??可編程處理器是內(nèi)嵌在GPU上的一種可編程單元,擁有非常強(qiáng)大的并行計算能力,并且擅長不高于4階的矩陣運算。而且隨著技術(shù)的發(fā)展,對浮點運算能力越來越強(qiáng)大。 ??可編程處理器還有個別名——著色器,使用的比較廣泛。所以上面的兩種可編程處理器也叫做頂點著色器和片段著色器(或者像素著色器)。 ??可編程處理器只是一個單純的硬件,還需要有運行在處理器上的程序來驅(qū)動,而這種程序叫做著色器程序,也叫shader程序。程序和處理器之間有一一對應(yīng)關(guān)系,頂點shader程序運行在頂點著色器上,片段shader程序運行片段著色器上。前面已經(jīng)提到頂點著色器接管了“T&L”硬件的任務(wù),片段著色器負(fù)責(zé)顏色計算。那么shader程序也有各自分工,頂點shader程序負(fù)責(zé)頂點變換和光照計算,片段shader程序負(fù)責(zé)顏色計算,而且前者的輸出是后者的輸入。 ??由于每一代可編程處理器的架構(gòu)有一定差異,沒有一個絕對統(tǒng)一的標(biāo)準(zhǔn)。本文將以遵循Shader Model 2.0規(guī)范的可編程處理器為例來介紹。
可編程頂點處理器
??在渲染3D場景的時候,頂點信息是通過Direct3D或者OpenGL這類3D渲染API傳遞給GPU。當(dāng)GPU接收到頂點信息,它會為應(yīng)用程序提交的每一個頂點調(diào)用一次頂點著色器程序。圖2- 8就是可編程頂點處理器的架構(gòu)圖。
可編程頂點處理器架構(gòu)
??從圖中我們可以看到頂點信息是以流的形式傳遞進(jìn)來,這些信息包括頂點坐標(biāo)、紋理坐標(biāo)、顏色等等。信息傳遞完畢后,會被存放頂點數(shù)據(jù)寄存器(也叫輸入寄存器)中。輸入寄存器是一種只讀寄存器,從圖中可以看到輸入寄存器有16個,v0到v15,說明最多可以存儲16個頂點屬性。然后,頂點處理器會通過訪問其他的一些寄存器來完成幾何階段的任務(wù)。 ??常量寄存器也是一種只讀寄存器,存儲一些預(yù)先設(shè)定的靜態(tài)參數(shù)。 ??臨時寄存器可以進(jìn)行讀寫操作,存放計算的中間結(jié)果。注意其中的a0和aL寄存器,他們主要用于循環(huán)計數(shù)和按索引尋址。 ??由于頂點處理器可以隨意訪問以上三種寄存器,所以編程人員可以按照自己的方式處理操作和處理寄存器中的數(shù)據(jù)。這在固定渲染管線時代是不可想象的事情。計算完畢,定點處理器要把計算結(jié)果存入輸出寄存器。這里必須把屏幕空間的頂點坐標(biāo)放入oPos寄存器中,當(dāng)然還可以傳遞其他的信息比如顏色、紋理坐標(biāo)等等到其他寄存器中。結(jié)果存放完畢后,接下來就進(jìn)入了圖元裝配階段和光柵化階段,這個與固定渲染管線一致,不具備可編程性。
可編程片段處理器
??光柵化操作完后,生成了一系列片段,這時候片段處理器會為每個片段調(diào)用片段著色程序。
可編程片段著色器架構(gòu)圖
??片段處理器內(nèi)部架構(gòu)和頂點處理器基本框架是差不多的,只是有些細(xì)節(jié)上的差異。這里是輸入寄存器變成了顏色寄存器和紋理寄存器,因為現(xiàn)在操作的對象是片段。寄存器v0和v1分別表示插值過以后的漫反射和鏡面反射分量。t0到tN這一系列寄存是用于存儲紋理查找坐標(biāo)。在片段處理階段,采樣寄存器指向需要使用的紋理。常量寄存器和臨時寄存器基本一致。 ??計算完畢后,輸出寄存器要把計算出的最終顏色存入寄存器。這里的寄存器除了oC0以外,還有oDepth,它里面的深度值是在光柵操作中做深度測試用的。 ??兩種可編程處理器介紹完了,在這里有兩點需要說明一下。第一點就是在本章最開始提到的統(tǒng)一渲染架構(gòu),已經(jīng)取消了專門的可編程處理器,shader程序與處理器之間沒有對應(yīng)關(guān)系。取而代之的是統(tǒng)一的流處理器,能運行所有類型的shader程序,這樣的好處是在各類場景渲染時能保持負(fù)載均衡。第二點是邏輯上的可編程處理器還有一類可編程幾何處理器,目前只有較新的硬件支持,應(yīng)用不廣泛,本文不做介紹。
??本章介紹了GPU 圖形渲染管線,并對相關(guān)的圖形硬件進(jìn)行了闡述。圖形渲染管線是GPU 編程的基礎(chǔ),事實上頂點著色程序和片段著色程序正是按照圖渲染制管線而劃分的。
新聞熱點
疑難解答