【《Real-Time Rendering 3rd》 提煉總結】(三) 第三章 · GPU渲染管線與可編程著色器
題圖來自《巫師3:狂獵》。
這篇文章是解析計算機圖形學界「九陰真經總綱」一般存在的《Real-Time Rendering 3rd》系列文章的第三篇。將帶來RTR3第三章內容「Chapter 3 The Graphics Processing Unit 圖形處理器」的總結、概括與提煉。
這章的主要內容是介紹GPU渲染管線的組成,以及可編程著色的進化史,頂點、幾何、像素三種可編程著色器。
本文總字數7.3k,分為全文內容圖示、原書核心內容分章節提煉、本章內容提煉總結三個部分來呈現。
一、全文內容圖示
1.章節框架圖示

2. GPU渲染管線流程圖

其中:
- 綠色的階段都是完全可編程的。
- 黃色的階段可配置,但不可編程。
- 藍色的階段完全固定。
二、原書核心內容分節提煉
3.1 GPU管線概述
- 第一個包含頂點處理,面向消費者的圖形晶元(NVIDIA GeForce256)發佈於1999年,且NVIDIA提出了圖形處理單元(Graphics Processing Unit ,GPU)這一術語,將GeForce256和之前的只能進行光柵化處理的圖形晶元相區分。在接下來的幾年中,GPU從可配置的固定功能管線演變到了支持高度可編程的管線。直到如今,各種可編程著色器依然是控制GPU的主要手段。為了提高效率,GPU管線的一部分仍然保持著可配置,但不是可編程的,但大趨勢依然是朝著可編程和更具靈活性的方向在發展。
- GPU實現了第二章中描述的幾何和光柵化概念管線階段。其被分為一些不同程度的可配置性和可編程性的硬體階段。如圖3.1 所示。

圖3.1 GPU實現的渲染管線
上圖中,不同顏色的階段表示了該階段不同屬性。其中:
- 綠色的階段都是完全可編程的。
- 黃色的階段可配置,但不可編程。
- 藍色的階段完全固定。
需要注意,GPU實現的渲染管線和第二章中描述的渲染管線的功能階段在結構上略有不同。以下是對GPU渲染管線的一個流程概覽:
- 頂點著色器(The Vertex Shader)是完全可編程的階段,頂點著色器可以對每個頂點進行諸如變換和變形在內的很多操作,提供了修改/創建/忽略頂點相關屬性的功能,這些頂點屬性包括顏色、法線、紋理坐標和位置。頂點著色器的必須完成的任務是將頂點從模型空間轉換到齊次裁剪空間。
- 幾何著色器(The Geometry Shader)位於頂點著色器之後,允許GPU高效地創建和銷毀幾何圖元。幾何著色器是可選的,完全可編程的階段,主要對圖元(點、線、三角形)的頂點進行操作。幾何著色器接收頂點著色器的輸出作為輸入,通過高效的幾何運算,將數據輸出,數據隨後經過幾何階段和光柵化階段的其他處理後,會發送給片段著色器。
- 裁剪(Clipping)屬於可配置的功能階段,在此階段可選運行的裁剪方式,以及添加自定義的裁剪面。
- 屏幕映射(Screen Mapping)、三角形設置(Triangle Setup)和三角形遍歷(Triangle Traversal)階段是固定功能階段。
- 像素著色器(Pixel Shader,Direct3D中的叫法)常常又稱為片斷著色器,片元著色器(Fragment Shader,OpenGL中的叫法),是完全可編程的階段,主要作用是進行像素的處理,讓複雜的著色方程在每一個像素上執行。
- 合併階段(The Merger Stage)處於完全可編程和固定功能之間,儘管不能編程,但是高度可配置,可以進行一系列的操作。其除了進行合併操作,還分管顏色修改(Color Modifying),Z緩衝(Z-buffer),混合(Blend),模板(Stencil)和相關緩存的處理。
隨著時間的推移,GPU管線已經遠離硬編碼的運算操作,而朝著提高靈活性和控制性改進。編程著色器的引入是這個進化的最重要的一步。
3.2 可編程著色模型
- 現代著色階段(比如支持shader model 4.0,DirectX 10以及之後)使用了通用著色核心(common-shader core),這就表明頂點,片段,幾何著色器共享一套編程模型。
- 早期的著色模型可以用彙編語言直接編程,但DX10之後,彙編就只在調試輸出階段可見,改用高級著色語言。
- 目前的著色語言都是C-like的著色語言,比如HLSL,CG和GLSL,其被編譯成獨立於機器的彙編語言語言,也稱為中間語言(IL)。這些彙編語言在單獨的階段,通常是在驅動中,被轉化成實際的機器語言。這樣的安排可以兼容不同的硬體實現。這些彙編語言可以被看做是定義一個作為著色語言編譯器的虛擬機。這個虛擬機是一個處理多種類型寄存器和數據源、預編了一系列指令的處理器。
- 著色語言虛擬機可以理解為一個處理多種類型寄存器和數據源、預編了一系列指令的處理器。考慮到很多圖形操作都使用短矢量(最高四位),處理器擁有4路SIMD(single-instruction multiple-data,單指令多數據)兼容性。每個寄存器包含四個獨立的值。32位單精度浮點的標量和矢量是其基本數據類型;也隨後支持32位整型。浮點矢量通常包含數據如位置(xyzw),法線,矩陣行 ,顏色(rgba),或者紋理坐標(uvwq)。而整型通常用來表示,計數器,索引,或者位掩碼。也支持綜合數據類型比如結構體,數組,和矩陣。而為了便於使用向量,向量操作如調和(swizzling,也就是向量分量的重新排序或複製),和屏蔽(masking,只使用指定的矢量元素),也能夠支持。虛擬機的輸入和輸出見圖3.2。

圖3.2 DX 10下的通用Shader核心虛擬機架構以及寄存器布局。每個資源旁邊顯示最大可用編號。其中,用兩個斜杠分開的三個數值,分別是頂點,幾何、像素著色器對應的可用最大值。
- 一個繪製調用(也就是喜聞樂見的Draw Call)會調用圖形API來繪製一系列的圖元,會驅使圖形管線的運行。
- 每個可編程著色階段擁有兩種類型的輸入:
- uniform輸入,在一個draw call中保持不變的值(但在不同draw call 之間可以更改);
- varying 輸入,shader里對每個頂點和像素的處理都不同的值。紋理是特殊類型的uniform輸入,曾經一直是一張應用到表面的彩色圖片,但現在可以認為是存儲著大量數據的數組。
- 在現代GPU上 ,圖形運算中常見的運算操作執行速度非常快。通常情況下,最快的操作是標量和向量的乘法和加法,以及他們的組合,如乘加運算(multiply- add)和點乘 (dot-product)運算。其他操作,比如倒數(reciprocal), 平方根(square root),正弦(sine),餘弦(cosine),指數(exponentiation)、對數(logarithm)運算,往往會稍微更加昂貴,但依然相當快捷。紋理操作非常高效,但他們的性能可能受到諸如等待檢索結果的時間等因素的限制。
- 著色語言表示出了大多數場常見的操作(比如加法和乘法通過運算符+和*來表示)。其餘的操作用固有的函數,比如atan() , dot() , log(),等。更複雜的操作也存在內建函數,比如矢量歸一化(vector normalization)、反射(reflection)、叉乘(cross products)、矩陣的轉置(matrix transpose)和行列式(determinant)等。
- 流控制(flow control)是指使用分支指令來改變代碼執行流程的操作。這些指令用於實現高級語言結構,如「if」和「case」語句,以及各種類型的循環。Shader支持兩種類型的流控制。靜態流控制(Static flow control)是基於統一輸入的值的。這意味著代碼的流在調用時是恆定的。靜態流控制的主要好處是允許在不同的情況下使用相同的著色器(例如,不同數量的光源)。動態流控制(Dynamic flow control)基於不同的輸入值。但動態流控制 遠比靜態流量控制更強大但同時也需更高的開銷,特別是在調用shader之間,代碼流不規律改變的時候。正如18.4.2節中討論的,評估一個shader的性能,是評估其在一段時間內處理頂點或像素的個數。如果流對某些元素選擇「if」分支,而對其他元素選擇「else」分支,這兩個分支必須對所有元素進行評估(並且每個元素的未使用分支將被丟棄)。
- Shader程序可以在程序載入或運行時離線編譯。和任何編譯器一樣,有生成不同輸出文件和使用不同優化級別的選項。一個編譯過的Shader作為字元串或者文本來存儲,並通過驅動程序傳遞給GPU。
3.3 可編程著色的進化史 ThenEvolution of Programmable Shading
可編程著色的框架的思想可以追溯到1984年Cook的 shade trees。一個簡單的Shader以及其對應的shade tree如圖3.3。

原書圖3.3 一個簡單的銅著色器(Cook的 shade trees)和其對應的著色語言代碼
- RenderMan 著色語言(RenderManShading Language)是從80年代中後期根據這個可編程著色的框架思想開發出來的,目前仍然廣泛運用於電影製作的渲染中。在GPU原生支持可編程著色之前,有一些通過多個渲染通道實現實時可編程著色的嘗試。
- 在1999年,《雷神III:競技場》腳本語言是在這個領域上第一個成功廣泛商用的語言。
- 在2000年,Peery等人,描繪了一個將RnederMan Shader翻譯成在多通道上並且運行在圖形硬體上的系統。
- 在2001年,NVIDIAsGeForce 3 發布,它是第一個支持可編程頂點著色器的GPU,向DirectX 8.0開放,並以擴展的形式提供給OpenGL。
- 2002年, DirectX 9.0發布,裡面包括了Shader Model 2.0(以及其擴展版本2.X),Shader Model 2.0 支持真正的可編程頂點和像素著色。著色編程語言 HLSL也是隨著DirectX 9.0的發布而問世。
- 2004年,Shader Model 3.0推出。SM3.0是一個增量改進,將之前的可選功能進行了實現,進一步增加資源上限,並在頂點著色器中添加了有限的紋理讀取支持。新一代主機的問世,2005 (Microsofts Xbox 360),2006 (Sony Play Station),它們配備配備了Shader Model 3.0級別的GPU。
- 2006年底 ,任天堂發布了搭載固定功能管線GPU的Wii主機 ,固定功能管線,並沒有像預想中的會完全死亡(其實一直活得很好)。
- 2007年,Shader Model4.0發布,包含於DirectX 10.0中,OpenGL以擴展的方式支持。SM 4.0新增了幾個著色器和流輸出等新特性。SM 4.0包括支持所有Shader類型(頂點、幾何、像素著色器)的一套統一著色模型(uniform programming model)

圖3.4 一個名為「mental mill」的可視化著色器設計系統。各種操作都封裝在功能窗中,位於左側。每個功能窗都有可調參數,每個功能框的輸入和輸出彼此連接,形成最終結果。
3.3.1 著色模型對比 Comparison of Shader Models
下表3.1比較了各種著色模型的能力。在這個表格中,VS」代表頂點著色器和「PS」代表像素著色器(而著色模型4.0 引入了幾何著色器,其能力與頂點著色相似)。如果沒有出現「VS」和「PS」,那麼該行適用於頂點和像素著色器。

表3.1 不同版本的著色模型能力對比,按DirectX著色模型版本列出
3.4 頂點著色器 Vertext Shader
- 頂點著色器(The Vertex Shader)的功能於2001年首次在DirectX 8中引入。由於它是流水線上的第一個階段,可選是在GPU還是CPU上實現。而在CPU上實現的話,需將CPU中的輸出數據發送到GPU進行光柵化。目前幾乎所有的GPU都支持頂點著色。
- 頂點著色器是完全可編程的階段,是專門處理傳入的頂點信息的著色器,頂點著色器可以對每個頂點進行諸如變換和變形在內的很多操作。頂點著色器一般不處理附加信息,也就是說,頂點著色器提供了修改,創建,或者忽略與每個多邊形頂點相關的值的方式,例如其顏色,法線,紋理坐標和位置。通常,頂點著色器程序將頂點從模型空間(Model Space)變換到齊次裁剪空間(Homogeneous Clip Space),並且,一個頂點著色器至少且必須輸出此變換位置。
- 值得注意的是,在這個頂點著色階段之前發生了一些數據操作。比如在DirectX中叫做輸入裝配(Input Assembler)的階段,會將一些數據流組織在一起,以形成頂點和基元的集合,發送到管線。
- 頂點著色器本身與前面3.2節所述的通用核心虛擬機(Common-Shader Core Virtual Machine)非常相似。傳入的每個頂點由頂點著色器程序處理,然後輸出一些在三角形或直線上進行插值後獲得的值。頂點著色器既不能創建也不能消除頂點,並且由一個頂點生成的結果不能傳遞到另一個頂點。由於每個頂點都被獨立處理,所以GPU上的任何數量的著色器處理器都可以並行地應用到傳入的頂點流上。
- 頂點著色器的輸出可以以許多不同的方式來使用,通常是隨後用於每個實例三角形的生成和光柵化,然後各個像素片段被發送到像素著色器,以便繼續處理。而在Shader Model 4.0中,數據也可以發送到幾何著色器(Geometry Shader)或輸出流(Streamed Output)或同時發動到像素著色器和幾何著色器兩者中。
如圖3.5 使用頂點著色器的一些示例。

原書圖3.5 左圖,一個普通茶壺。中圖,經過頂點著色程序執行的簡單剪切(shear)操作產生的茶壺。右圖,通過雜訊(noise)產生的發生形變的茶壺。
3.5 幾何著色器 The Geometry Shader
- 幾何著色器(Geometry Shader)是頂點和片段著色器之間一個可選的著色器,隨著2006年底 DirectX10的發布被加入到硬體加速圖形管線中。幾何著色器作為Shader Model 4.0的一部分,不能早期著色模型(<=SM 3.0)中使用。
- 幾何著色器的輸入是單個對象及對象相關的頂點,而對象通常是網格中的三角形,線段或簡單的點。另外,擴展的圖元可以由幾何著色器定義和處理。如圖3.6。

- 幾何著色器可以改變新傳遞進來的圖元的拓撲結構,且幾何著色器可以接收任何拓撲類型的圖元,但是只能輸出點、折線(line strip)和三角形條(triangle strips)。
- 幾何著色器需要圖元作為輸入,在處理過程中他可以將這個圖元整個丟棄或者輸出一個或更多的圖元(也就是說它可以產生比它得到的更多或更少的頂點)。這個能力被叫做幾何增長(growing geometry)。如上所述,幾何著色器輸出的形式只能是點,折線和三角形條。
- 當我們未添加幾何著色器時,默認的行為是將輸入的三角形直接輸出。我們添加了幾何著色器之後,可以在幾何著色器中修改輸出的圖形,我們可以輸出我們想要輸出的任何圖形。

圖 3.7.一些幾何著色器的應用。左圖,使用幾何著色器實現元球的等值面曲面細分(metaball isosurface tessellation)。中圖,使用了幾何著色器和流輸出進行線段細分的分形(fractal subdivision of line segments)。右圖,使用頂點和幾何著色器的流輸出進行布料模擬。(圖片來自NVIDIA SDK 10的示例)
3.5.1 流輸出 Stream Output
- GPU的管線的標準使用方式是發送數據到頂點著色器,然後對所得到的三角形進行光柵化處理,並在像素著色器中處理它們。 數據總是通過管線傳遞,無法訪問中間結果。流輸出的想法在Shader Model 4.0中被引入。在頂點著色器(以及可選的幾何著色器中)處理頂點之後,除了將數據發送到光柵化階段之外,也可以輸出到流,也就是一個有序數組中進行處理。事實上,可以完全關掉光柵化,然後管線純粹作為非圖形流處理器來使用。以這種方式處理的數據可以通過管線回傳,從而允許迭代處理。如原書的第10.7節所述,這種操作特別適用於模擬流動的水或其他粒子特效。
3.6 像素著色器 Pixel Shader
- 像素著色器(Pixel Shader,Direct3D中的叫法),常常又稱為片斷著色器,片元著色器(Fragment Shader, OpenGL中的叫法),用於進行逐像素計算顏色的操作,讓複雜的著色方程在每一個像素上執行。如圖3.1 GPU渲染管線所示,像素著色器是光柵化階段的主要步驟之一。在頂點和幾何著色器執行完其操作之後,圖元會被裁剪、屏幕映射,結束幾何階段,到達光柵化階段,在光柵化階段中先經歷三角形設定和三角形遍歷,之後來到像素著色階段。
- 像素著色器常用來處理場景光照和與之相關的效果,如凸凹紋理映射和調色。名稱片斷著色器似乎更為準確,因為對於著色器的調用和屏幕上像素的顯示並非一一對應。舉個例子,對於一個像素,片斷著色器可能會被調用若干次來決定它最終的顏色,那些被遮擋的物體也會被計算,直到最後的深度緩衝才將各物體前後排序。
- 需要注意,像素著色程序通常在最終合併階段設置片段顏色以進行合併,而深度值也可以由像素著色器修改。模板緩衝( stencil bu?er )值是不可修改的,而是將其傳遞到合併階段(merge stage)。在SM 2.0以及以上版本,像素著色器也可以丟棄(discard )傳入的片段數據,即不產生輸出。這樣的操作會消耗性能,因為通常在這種情況下不能使用由GPU執行的優化 。詳見第18.3.7節。 諸如霧計算和alpha測試的操作已經從合併操作轉移到SM 4.0 中的像素著色器里計算。
- 可以發現,頂點著色程序的輸出,在經歷裁剪、屏幕映射、三角形設定、三角形遍歷後,實際上變成了像素著色程序的輸入。在Shader Model 4.0中,共有16個向量(每個向量含4個值)可以從頂點著色器傳到像素著色器。當使用幾何著色器時,可以輸出32個向量到像素著色器中。像素著色器的追加輸入是在Shader Model 3.0中引入的。例如,三角形的哪一面是可見的是通過輸入標誌來加入的。這個值對於在單個通道中的正面和背面渲染不同材質十分重要。而且像素著色器也可以獲得片段的屏幕位置。
3.7 合併階段 The Merging Stage
作為光柵化階段名義上的最後一個階段,合併階段(The Merging Stage)是將像素著色器中生成的各個片段的深度和顏色與幀緩衝結合在一起的地方。這個階段也就是進行模板緩衝(Stencil-Buffer)和Z緩衝(Z-buffer)操作的地方。最常用於透明處理(Transparency)和合成操作(Compositing)的顏色混合(Color Blending)操作也是在這個階段進行的。
雖然合併階段不可編程,但卻是高度可配置的。在合併階段可以設置顏色混合來執行大量不同的操作。最常見的是涉及顏色和Alpha值的乘法,加法,和減法的組合。其他操作也是可能的,比如最大值,最小值以及按位邏輯運算。
3.8 效果 Effect
- GPU渲染管線中的可編程階段有頂點、幾何和像素著色器三個部分,他們需要相互結合在一起使用。正因如此,不同的團隊研發出了不同的特效語言,例如HLSL FX,CgFX,以及COLLADA FX,來將他們更好的結合在一起。
- 一個效果文件通常會包含所有執行一種特定圖形演算法的所有相關信息,而且通常定義一些可被應用程序賦值的全局參數。例如,一個單獨的effect file可能定義渲染塑料材質需要的vs(頂點著色器)和ps(像素著色器),它可能暴露一些參數例如塑料顏色和粗糙度,這樣渲染每個模型的時候可以改變效果而僅僅使用同一個特效文件。一個效果文件中能存儲很多techniques。這些techniques通常是一個相同effect的變體,每種對應於一個不同的Shader Model(SM2.0,SM3.0等等)。如圖3.9,使用Shader實現的效果。

原書圖3.9 可編程著色器可以實現各種材料和後處理效果
三、本章內容提煉總結
以下是對《Real Time Rendering 3rd》第三章「ThenGraphics Processing Unit 圖形處理器」內容概括總結,有必要再次放出這張圖:

圖3.1 GPU實現的渲染管線
上圖中,不同顏色的階段表示了該階段不同屬性。其中:
- 綠色的階段都是完全可編程的。
- 黃色的階段可配置,但不可編程。
- 藍色的階段完全固定。
對每個階段的分別概述:
- 頂點著色器(The Vertex Shader)是完全可編程的階段,頂點著色器可以對每個頂點進行諸如變換和變形在內的很多操作,提供了修改/創建/忽略頂點相關屬性的功能,這些頂點屬性包括顏色、法線、紋理坐標和位置。頂點著色器的必須完成的任務是將頂點從模型空間轉換到齊次裁剪空間。
- 幾何著色器(The Geometry Shader)位於頂點著色器之後,允許GPU高效地創建和銷毀幾何圖元。幾何著色器是可選的,完全可編程的階段,主要對圖元(點、線、三角形)的頂點進行操作。幾何著色器接收頂點著色器的輸出作為輸入,通過高效的幾何運算,將數據輸出,數據隨後經過幾何階段和光柵化階段的其他處理後,會發送給片段著色器。
- 裁剪(Clipping)屬於可配置的功能階段,在此階段可選運行的裁剪方式,以及添加自定義的裁剪面。
- 屏幕映射(Screen Mapping)、三角形設置(Triangle Setup)和三角形遍歷(Triangle Traversal)階段是固定功能階段。
- 像素著色器(Pixel Shader,Direct3D中的叫法)常常又稱為片斷著色器,片元著色器(Fragment Shader,OpenGL中的叫法),是完全可編程的階段,主要作用是進行像素的處理,讓複雜的著色方程在每一個像素上執行。
- 合併階段(The Merger Stage)處於完全可編程和固定功能之間,儘管不能編程,但是高度可配置,可以進行一系列的操作。其除了進行合併操作,還分管顏色修改(Color Modifying),Z緩衝(Z-buffer),混合(Blend),模板(Stencil)和相關緩存的處理。
The end.
推薦閱讀:
※6.向量
※極簡的 PNG 編碼函數 svpng()
※幻影坦克架構指南(一)
※計算機輔助設計與製造CAE/CAM目前有哪些技術問題需要解決?
※【《Real-Time Rendering 3rd》 提煉總結】(八) 第九章 · 全局光照:光線追蹤、路徑追蹤與GI技術進化編年史
