圖形繪制流水線梳理


  作為一個游戲開發從業者,雖然現在主要在做服務器端了,但對客戶端開發依然充滿興趣。個人一直認為游戲開發,應該前后端都學,都做。只是公司總是區分服務器和客戶端開發。。。好吧,那就有機會都學一學,做一做。以前做客戶端開發的時候,由於需求繁忙,很難擠出時間細細研究,只能是現學現用,很多東西還是沒有完全搞明白。現在終於有一點個人時間,可以更系統的學習了。先從回顧一下圖形學的相關知識開始吧。

  現代計算機的圖形繪制,都交由gpu完成並輸出到顯示設備。從而減輕cpu的計算壓力,獲得更好的繪制效率。為了讓cpu和gpu很好的協作,gpu都需要單獨的驅動程序,為了能更通用的使用顯卡驅動,便有了通用的圖形API,常見的即DirectX與OpenGL,DirectX只能在Windows平台使用,OpenGL則可以跨很多平台,他們的繪制過程大體相似。本文不是要介紹相應的圖形API,而是梳理普遍的三維圖形繪制過程。繪制過程可以分為以下幾個常見階段,又叫繪制流水線(Graphics pipeline)。

  圖形輸入

  要把三維模型繪制到屏幕上,首先需要定義模型,並將定義的相關數據作為輸入,存放到顯存,再在繪制提交執行的時候直接從顯存讀取數據。模型由一個個圖元拼接而成,常見的圖元有點、線、三角形及四邊形等。比如一個正方形就可以由兩個三角形組成。為了描述一個三角形,我們需要三個頂點數據,每個頂點不僅僅包含該點在三維空間中的位置,還可以包含該頂點的顏色、頂點的法線方向以及頂點對應的紋理坐標等等,根據需要加入相關的頂點屬性。

  為了繪制一個模型,除了頂點外,我們還需要定義頂點的拓撲結構,從而用頂點組成圖元,再由一個個圖元組成整個模型。常見的拓撲類型有TriangleStrip,TriangleList等,當然還有PointList,LineList等,這里我們主要討論三角形面片。TriangleStrip是按約定的頂點順序組建三角形,除了前三個頂點,后續的頂點總是和前面兩個頂點組成一個三角形。TriangleList(頂點列表)類型每三個頂點描述一個三角形,顯然前者更省空間,但是后者的描述更為通用。

  大多數模型都有很多相鄰的三角形,利用上述TriangleList描述,很多頂點需要重復多遍。於是有了索引描述方式,即定義頂點列表后,按照順序用一個uint16或uint32數組從0開始索引頂點列表中的頂點。每三個索引所對應的頂點組成一個三角形。一個正方形,用頂點列表描述的話,兩個三角形總共6個頂點,但用索引的方式,則只需要4個頂點加一個長度為6的short int數組。注意一個頂點數據結構的大小,一般都比額外索引半個int要大多了。實際應用中一般都是用索引描述的方式,對應到繪制的時候,則需要調相應的Indexed接口。

  頂點着色器

  對每一個輸入的頂點,都會執行一次頂點着色器。頂點着色器主要處理頂點坐標系的變換,當然也可以進行逐個頂點的光照計算、蒙皮計算等。一般而言,我們獲得的模型都是在它自己的坐標系中的,就是以該模型自己為中心(這樣美術在制作模型時才能更好的合作,模型自身旋轉縮放時也不會遇到問題)。模型位於場景中某個位置,我們通過一個世界變換矩陣(WorldMatrix)將模型變換到世界坐標系下。然后再從某個點用相機看整個場景,將世界坐標系中的物體變換到相機坐標系中(ViewMatrix)。最后由於最終顯示的屏幕是是個平面,為了表現眼睛看物體近大遠小,通過一個視錐體來表示相機坐標系中看到的區域,然后通過投影變換(ProjectionMatrix)把相機坐標系中的視錐體變換成立方體區域,也稱為齊次裁剪空間,注意這里的立方體還不是最終屏幕上視口的大小,而是一個xy(-1,1),z(0,1)的區域。

  於是一個頂點的變換可以表示為 ouput = input * WorldMatrix * ViewMatrix * ProjectionMatrix。根據矩陣的結合律,這三個矩陣也可以合並為一個矩陣。關於為何可以使用矩陣對頂點進行變換以及三個變換矩陣的計算,需要一些線性代數基礎,可以參考DX龍書或OpenGL紅寶書,都有基本講解。現在普遍使用的旋轉表示方法,四元數,卻是很少有講解的很清楚的,我們只需要知道通過四元數的兩次乘法,能夠實現三維空間的旋轉即可。

  對於逐個頂點的光照計算,我們在頂點着色階段就可以根據該頂點的法向和光源方向計算出一個光照顏色,並記錄在頂點輸出屬性中,向后傳輸插值后給片段着色器使用。

  對於基於gpu的蒙皮動畫,我們也可以在這個階段根據骨骼的當前動作和頂點的綁定矩陣計算頂點的最終動作位置。

  圖元組裝

  該階段根據描述的圖元類型,傳入的頂點及索引緩存,生成圖元數據結構,為后續的幾何着色器及裁剪做准備。

  細分曲面着色器、幾何着色器

  在組裝好圖元后,我們還有機會對圖元進行修改,進行曲面細分、圖元修改等從而實現一些幾何變化效果。

  裁剪

  頂點變換最終變換到一個xy軸(-1,1),z軸(0,1)的一個立方體區域,區域外的三角形不可見,可以直接剔除,而對於部分在立方體內的三角形,則需要切割,並把外面的剔除,注意此時坐標還在齊次空間以保留線性關系。此階段還會做的工作是背面消隱。由於一般物體我們都只能看到外面,看不到內部,因此會指定三角形的順時針或逆時針為正向,對於反向面向相機的面片則可以直接剔除。由於我們已經變換到立方體區域,此時只要判斷法向的z值大於0或小於0即可。

  視口變換

  投影變換后,繪制的模型區域是在一個立方體區域內,與最終繪制的屏幕視口還有平移和縮放。此階段將立方體映射到屏幕的視口中鋪滿。

  柵格化

  圖形輸入是描述性的幾何定義,而屏幕則是散列點陣。簡單來說,將幾何圖形離散為點陣表示,就是柵格化的過程。通過前面的變換,我們要繪制的三角形已經限制在視口以內,視口以外的三角形被剔除。對視口以內的每個三角形,判斷該三角形所覆蓋的離散像素點,對每個像素點利用三角形的頂點進行雙線性插值生成一個片段屬性數據,注意由於三角形可能互相覆蓋穿插,實際上同一個像素點可能有多個片段,可能在前可能在后,在后續的z-test中可以選擇性的得到該點最終的顏色。在柵格化階段,也會指定后續使用的像素(片段)着色器。對生成的每個片段屬性,都會執行一遍片段着色器,獲得一個顏色輸出。

  柵格化階段也可以處理反鋸齒,上面說到屏幕上顯示的是散列點陣,那么如果三角形的邊緣正好穿過某個像素點,就產生了歧義。此時該像素點實際上只有部分被三角形覆蓋,但片段着色器是對整個像素計算的。因此邊緣穿過的像素覆蓋多的計算顏色,覆蓋少的不計算顏色,就產生了鋸齒。在理論層面,由於采樣的頻率必須大於信號最大頻率的兩倍才能保持原有的信息(奈奎斯特定理)。對於三角形邊緣,是一個不連續的跳變,離散的采樣需要無限大的采樣頻率才能保持邊緣信息。由於屏幕是離散的點,我們只能通過提高采樣頻率來減少鋸齒的視覺影響。最簡單的方法是超采樣(SSAA),就是原先的一個像素,采樣兩個、四個像素,最后做個平均或者其他濾波得到屏幕的顏色值。由於SSAA采樣數翻倍,計算量也是翻倍的增長,顯然對性能消耗是巨大的。於是誕生了多重采樣(MSAA),對原先的一個像素,柵格化時同樣采樣多次,並且每個采樣點單獨進行深度測試,但是對於三角形覆蓋的區域,只執行一次片段着色器計算,並把該結果復制給其他采樣點。最后根據覆蓋率計算最終的顏色值。如果一個像素采樣了4個點,有3個點被該三角形覆蓋,那么最終該片段貢獻3/4的顏色值。這樣得到的結果也能大大改善邊緣鋸齒,但是省去了為每個采樣點執行片段着色。

  像素(片段)着色器

  柵格化輸出的片段數據,就是該着色器的輸入,對於每一個片段數據,片段着色器都執行一遍代碼計算該片段的顏色值。逐像素的光照計算、紋理采樣、一些風格化的處理都可以在片段着色器中完成。在柵格化的過程中我們提到,屏幕上的每個點可能有多個片段數據。因此這里計算的結果還不是最終屏幕上看到的顏色,實際上很多結果還會丟掉。

  屏幕剪裁

  在視口中,我們可以指定一個剪裁區域,超出區域的部分不繪制。

  深度和模板測試

  這里的深度測試就是上文說到的z-test,由於每個位置可能有多個片段計算的結果,一般來說只有位於最前方靠近相機的面片才能被看到,所以每個片段在寫入結果時,同時寫入自己的深度到z-buffer,后面再有片段對應到這個位置時,先用深度屬性和z-buffer中的深度值比較,小於z-buffer中的深度才計算並更新顏色值。模板測試與深度測試類似,只有stencil-buffer的值滿足特定條件才計算並寫入結果。

  混合

  有時物體表面帶有半透明的特性,在寫入顏色值時需要與已有的顏色進行混合,注意此時需要把深度檢測關掉,否則位於半透明表面后面的面片可能通不過深度測試而不繪制出來。另外如果采用了MSAA等抗鋸齒技術,這里也要把不同采樣的結果做一個混個,得到最終的顏色。

  繪制目標

  繪制目標不一定是屏幕,即使是屏幕,在這里一般也不是直接寫入用於顯示的內存塊,而是寫入一個交換鏈的准備緩存,等這一次繪制全部完成后再用一次交換操作整塊拷貝或翻轉用於顯示。很多情況下,繪制的結果不用於直接的屏幕顯示,而是寫入一個幀緩存,該緩存可以被應用程序讀取並用於后續的操作。

  

  以上是常見的圖形繪制流水線的簡單梳理,可能有的階段在某些文檔里是合並到一起的,並不影響理解。圖形繪制是一個很復雜的過程,每一個階段展開都有很多相關的專題可以討論,這里只做一個概念性的總結,以幫助理解引擎繪制過程以及學習相關shader語言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM