GPU 怎麼把 3D 模型變成螢幕上的 2D 畫面?本文用最白話的方式,拆解從座標轉換、光柵化、紋理貼圖到深度測試的十四個步驟,並標註每個步驟由頂點著色器、紋理單元、渲染輸出單元等硬體單元負責,適合想徹底理解 GPU 如何運作的讀者。
第一階段:3D 變 2D 的座標轉換,也就是定位
在開始畫畫之前,GPU 必須先透過一套叫做 MVP 矩陣的數學運算,把 3D 世界壓扁到玩家的螢幕上。這個過程會經歷四個關鍵的空間轉換。你可以把這四個轉換想像成拍電影的四個步驟。
步驟 1:從物件空間到世界空間
GPU 利用模型矩陣,把原本以自身為原點的 3D 模型,放置到統一的遊戲大世界座標中。
白話解釋:想像你有一個模型師做好的「樹」模型。在模型師的工作桌上,這棵樹的座標原點是樹的正中心,所有樹枝、樹葉的位置都是相對於這個中心點算出來的。現在你要把這棵樹放進一個森林遊戲場景裡,你需要告訴 GPU:把這棵樹放到森林的哪個位置,是東邊的草地上還是西邊的河邊。這個「把模型放到世界裡」的動作,就是從物件空間轉換到世界空間。
執行單元:這個步驟由 GPU 的頂點著色器(Vertex Shader)執行。頂點著色器是 GPU 內部的可程式化單元,負責處理每一個頂點的座標轉換和基本光照計算。在 NVIDIA 的架構中,頂點著色器執行在 CUDA 核心上;在 AMD 的架構中,執行在串流處理器(Stream Processor)上。兩者本質上都是處理簡單數學運算的大量小型核心。
步驟 2:從世界空間到視圖空間
GPU 利用視圖矩陣,把所有物體的座標轉換成相對於玩家攝影機的距離與位置。
白話解釋:現在森林裡所有東西都有了自己的世界座標,但玩家看出去的畫面,是從攝影機的角度出發的。這個步驟就是在問:從攝影機的位置看出去,這棵樹在我的左邊還是右邊?離我多遠?是在我前面還是後面?簡單來說,就是把「世界的座標」轉換成「從我眼睛看出去的座標」。
執行單元:同樣由頂點著色器(Vertex Shader)負責。這一步本質上還是座標轉換,所以和第一步使用的是同一組執行單元。頂點著色器會對每個頂點執行同樣的矩陣乘法運算。
步驟 3:從視圖空間到裁剪空間
GPU 利用投影矩陣,創造出近大遠小的真實透視感,同時界定出攝影機的視野範圍,也就是視錐體。
白話解釋:攝影機不是什麼都拍,它有一個拍攝範圍。太遠的東西拍不到,太旁邊的東西也拍不到。這個範圍就像一個從攝影機向前延伸的錐形區域,叫做視錐體。在這個範圍裡面的東西才會被留下來,範圍外面的就直接丟掉。同時,投影矩陣還會讓物體符合近大遠小的透視規則,讓畫面看起來自然。
執行單元:由頂點著色器(Vertex Shader)繼續執行。這一步是 MVP 矩陣轉換的最後一個階段,同樣由頂點著色器完成。完成這一步之後,頂點著色器的工作就告一段落了。
步驟 4:從裁剪空間到螢幕空間
GPU 把 3D 座標轉換成螢幕上確切的 2D 像素座標。
白話解釋:經過前面三個轉換,我們已經知道攝影機拍到了哪些東西、它們的相對位置和大小。但最後要顯示在螢幕上,還需要知道這些東西要落在螢幕的哪個像素上。假設你的螢幕是 1920×1080,這個步驟就是在算出某個點應該落在 (960, 324) 這個位置。到這裡,3D 的座標已經完全轉換成 2D 螢幕上的位置了。
執行單元:這個步驟由 GPU 內部的視角轉換單元(Viewport Transform Unit)負責。這是一個固定功能的硬體單元,不是可程式化的著色器。它的工作是單純的數學轉換,把裁剪空間的座標映射到螢幕像素座標。
第二階段:核心渲染流水線,也就是塑形與品管
確定了位置之後,GPU 內部啟動流水線,開始處理畫面,這就像一個工廠的組裝線,每個站做不同的事情,同時進行。
步驟 5:頂點與幾何處理
GPU 處理 3D 模型的每一個頂點,計算光照,並把它們組裝成數百萬個基本的三角形。
白話解釋:還記得嗎?所有 3D 模型都是由三角形組成的。一個角色可能由幾萬個三角形組成,一個場景可能有幾百萬個三角形。這個步驟會處理每個三角形的三個頂點,計算每個頂點受到光源影響後的顏色,然後把這些頂點串起來,形成一個一個的三角形。
這個階段還有一個進階功能叫做細分著色器(Tessellation Shader)。它的作用是:當你離一個物體很近的時候,把大三角形切成更多更小的三角形,讓表面變得更平滑。比如一個圓球,如果只用幾個大三角形去拼,看起來會像骰子一樣有稜有角。但如果把每個大三角形再切成幾百個小三角形,看起來就會很圓滑。如果想知道更多Tessellation Shader 的資訊,可以參考我這篇《產品經理軟體轉硬體的痛|在硬體產業是怎麼補齊相關知識的?》文章底部的內容。
執行單元:這一步涉及多個執行單元。頂點著色器(Vertex Shader)負責處理每個頂點的資料,包括座標轉換和頂點級別的光照計算。幾何著色器(Geometry Shader)(如果有使用的話)負責處理整個基本圖形,也就是三角形,可以生成新的頂點或刪除現有的頂點。
在現代 GPU 架構中,細分控制著色器(Tessellation Control Shader)和細分評估著色器(Tessellation Evaluation Shader)專門負責細分曲面,把一個粗糙的模型變得更平滑。在 NVIDIA 的 Turing 及後續架構中,這些著色器都執行在 CUDA 核心上;在 AMD 的 RDNA 架構中,執行在串流處理器上。
步驟 6:裁剪
這是一道硬體自動執行的品管關卡。完全在視野外,或者是背對鏡頭的三角形,會被直接丟棄。
白話解釋:這個步驟很簡單,但效果非常驚人。假設你站在森林裡看向前方,你背後的那些樹木和石頭,因為你根本看不到,所以完全不需要浪費時間去處理它們的細節。GPU 會檢查每個三角形:它是不是在攝影機的視野範圍內?它的正面是不是朝著攝影機?如果兩個答案都是否定的,就直接丟掉。這個篩選可以幫 GPU 省下多達七成的運算量。
執行單元:由 GPU 內部的三角形組裝單元(Primitive Assembly Unit)和裁剪單元(Clipping Unit)負責。這兩個都是固定功能的硬體單元,三角形組裝單元負責把頂點串成三角形,裁剪單元則檢查每個三角形是否在視錐體內,並將部分在視野內的三角形切開,保留可見的部分。
步驟 7:光柵化
這是 3D 轉 2D 像素的關鍵轉折點,GPU 會掃描這些三角形覆蓋了螢幕上的哪些像素,並生成對應的片段。
白話解釋:現在我們有一堆三角形,也知道它們在螢幕上的位置了。但螢幕是由一格一格的像素組成的,三角形是一個連續的形狀,它會覆蓋到哪些像素?光柵化就是在做這件事:把每個三角形轉換成它覆蓋到的像素集合,每個被覆蓋到的像素會產生一個「片段」,這個片段包含了這個像素的顏色、深度、透明度等資訊。
光柵化還有一個重要的功能叫做插值(Interpolation)。假設一個三角形的三個頂點分別是紅色、綠色和藍色,那三角形中間的像素應該是什麼顏色?插值會根據每個像素到三個頂點的距離,計算出一個漸變的顏色。比如靠近紅色頂點的地方偏紅,靠近藍色頂點的地方偏藍。這樣整個三角形看起來就會是平滑的漸層,而不是三個色塊拼在一起。
執行單元:由 GPU 內部的光柵化器(Rasterizer)負責,這是一個固定功能的硬體單元,是 GPU 架構中非常關鍵的部分。光柵化器接收來自三角形組裝單元的三角形,計算出三角形覆蓋了哪些像素,並為每個像素生成一個片段。同時,光柵化器內建的漸變計算單元會計算每個片段相對應的屬性值,如顏色、紋理座標、深度等,讓顏色和紋理能夠平滑地過渡。
第三階段:賦予細節與真實感,也就是上色與貼圖
生成像素之後,就進入了整個流程中最耗時、也最重要的片段著色階段,因為,這個階段決定了一個像素最終看起來是什麼樣子。
步驟 8:紋理映射
GPU 就是透過 UV 座標,把 2D 的紋理貼圖精準地貼在 3D 模型表面。
白話解釋:如果只用三角形,物體會毫無細節,就像一個白色的石膏模型,看起來很假,紋理映射就是拿一張 2D 的圖片,像是磚牆的照片、木紋的照片、角色的衣服花紋,把它貼到 3D 模型上。UV 座標就是這張 2D 圖片的座標系統,U 是水平方向,V 是垂直方向。模型上的每個三角形頂點都有一個對應的 UV 座標,告訴 GPU 這個頂點應該對應到貼圖上的哪個位置。貼圖的時候,三角形內部的每個像素就會根據頂點的 UV 座標去貼圖上取顏色。這樣一來,一個簡單的平面模型貼上磚牆貼圖,看起來就是一面有細節的磚牆。
執行單元:這個步驟由 GPU 內部的紋理單元(Texture Mapping Unit,TMU)負責。紋理單元是專門用來讀取紋理貼圖的硬體單元,它與著色器核心緊密配合。當片段著色器需要取樣一個紋理時,它會向紋理單元發出請求,紋理單元負責從記憶體中讀取貼圖資料、執行紋理過濾,然後把結果返回給片段著色器。
步驟 9:紋理過濾與 Mipmap
為了避免紋理在放大縮小或傾斜觀看時產生鋸齒或模糊,GPU 的紋理單元會執行各種過濾來平滑畫面。同時,GPU 會採用 Mipmap 技術,預先準備一系列逐漸縮小的紋理圖片。
白話解釋:先說過濾。當你斜著看一面牆的時候,牆上的磚頭貼圖會被拉長,如果直接用原始的貼圖,會產生嚴重的鋸齒或模糊。紋理過濾就是一種數學方法,讓 GPU 在取樣貼圖顏色的時候,不只是取一個點,而是取周圍幾個點的平均值,讓顏色過渡更平滑。常見的有雙線性過濾(取周圍 4 個點平均)、三線性過濾(在兩層 Mipmap 之間取平均)、各向異性過濾(針對傾斜角度做更精準的取樣)。
再來說 Mipmap。Mipmap 的概念很簡單:預先做好一組尺寸逐漸縮小的貼圖。原始貼圖是 1024×1024,然後縮成 512×512、256×256,一直縮到 1×1。當一個物體離你很近的時候,它在螢幕上占的像素很多,GPU 就用最大的貼圖;當物體離你很遠的時候,它在螢幕上可能只占幾個像素,這時候用最大的貼圖是浪費,因為細節根本看不出來,GPU 會自動選用較小的 Mipmap 層級。
執行單元:這個步驟由紋理單元(Texture Mapping Unit,TMU)執行,紋理單元內建了專門的硬體電路來執行雙線性、三線性和各向異性過濾。Mipmap 的層級選擇也是由紋理單元自動計算的,它會根據紋理座標的變化率來決定要用哪一層 Mipmap,然後執行對應的讀取和過濾運算。
步驟 10:光照與材質計算
著色器會結合多種貼圖,並計算光源強度與環境光,得出該像素最精確的最終顏色。
白話解釋:這一步是最複雜的,因為它要模擬現實世界的光學現象。一個物體表面的最終顏色,不是單靠一張貼圖就能決定的,它還需要考慮光線怎麼打上去、材質怎麼反射光線。常見的貼圖有這幾種:
漫反射貼圖(也叫基礎顏色貼圖),決定物體本身的顏色。木頭就是木頭色,金屬就是金屬色。
法線貼圖,決定表面的凹凸感。法線貼圖不是真的改變模型形狀,而是騙過光線計算,讓光線在照射的時候以為表面有凹凸,從而產生陰影和高光。這樣一個平滑的平面看起來就會有磚頭的凹凸紋理。
高光貼圖,決定哪些地方會反光、反光強度多高。金屬的部分反光強,粗糙的木頭部分反光弱。
有了這些貼圖之後,著色器還會計算光源。遊戲場景裡可能有太陽光(方向光)、燈泡光(點光源)、手電筒光(聚光燈),還有周圍環境的反射光(環境光)。著色器會把這些光源的強度、方向,加上材質的反光特性,還有法線貼圖提供的凹凸資訊,全部算在一起,得出這個像素最終的顏色。
執行單元:這個步驟由片段著色器(Fragment Shader)執行。片段著色器是 GPU 內部數量最多的可程式化單元。在 NVIDIA 的架構中,片段著色器執行在 CUDA 核心上;在 AMD 的架構中,執行在串流處理器(Stream Processor)上。一個高階 GPU 有幾千個這樣的執行單元,它們可以同時處理數千個片段的著色計算。片段著色器會呼叫紋理單元來讀取貼圖,然後自己執行光照模型和材質計算的數學運算。
第四階段:空間關係與最終輸出,也就是整合
最後,要把這些處理好的像素搬上螢幕之前,必須經過渲染輸出單元的嚴格把關。這個階段負責處理物體之間的遮擋關係、透明物體的混合、以及邊緣的平滑。
步驟 11:深度測試與 Z-Buffer
GPU 會查閱一張叫做 Z-Buffer 的深度記憶體,確保近的物體會遮擋遠的物體。
白話解釋:Z-Buffer 是一張跟螢幕大小相同的表格,每個格子記錄著「這個像素目前為止,最靠近鏡頭的物體距離是多少」。當 GPU 要畫一個新的像素時,它會先問 Z-Buffer:這個位置已經有東西了嗎?那個東西離鏡頭多遠?如果新像素的距離比記錄的還要遠,就代表它會被前面的東西擋住,不需要畫出來,直接丟棄。如果新像素的距離比較近,就代表它會擋住後面的東西,那就把新像素畫上去,同時更新 Z-Buffer 的記錄。
現代 GPU 還有一個優化叫做 Early-Z。原本的流程是先做片段著色(上色、貼圖、光照),再做深度測試。但問題是,如果一個像素後來被深度測試淘汰了,那前面做的那些複雜的著色運算就白費了。Early-Z 把深度測試提前到片段著色之前,先確定這個像素不會被擋住,才去做那些昂貴的著色運算。
執行單元:這個步驟由 GPU 內部的渲染輸出單元(Render Output Unit,ROP)負責。ROP 是專門處理像素輸出相關操作的固定功能硬體單元。它負責執行深度測試、模板測試,並將通過測試的像素寫入幀緩衝區。Early-Z 優化也是在 ROP 中實現的,它在片段著色器執行之前就先進行深度測試。
步驟 12:Alpha 混合
處理半透明物體的透明度,如玻璃、水面、煙霧。
白話解釋:不透明的物體很簡單,誰在前面就顯示誰。但半透明的物體不一樣,我們要看到它背後的東西。假設你有一塊藍色的半透明玻璃,它後面有一個紅色的箱子。你看到的不是純藍色,也不是純紅色,而是藍色和紅色混合在一起的顏色。Alpha 混合就是做這件事。每個像素有一個 Alpha 值,代表透明度。0 是完全透明,1 是完全不透明。ROP 會根據前景像素的顏色和 Alpha 值,以及背景像素的顏色,用一個公式算出混合後的顏色。
執行單元:這個步驟同樣由渲染輸出單元(Render Output Unit,ROP)負責。ROP 內建了 Alpha 混合的硬體電路,可以在一組像素上同時執行混合運算。不同的應用程式可以設定不同的混合公式,例如加法混合、減法混合、最小值混合等,來達到不同的視覺效果。
步驟 13:抗鋸齒
因為螢幕是由方形像素組成的,物體邊緣容易出現階梯狀的鋸齒,ROP 透過各種抗鋸齒技術讓邊緣變平滑。
白話解釋:想像你要在方格紙上畫一條斜線。因為每個格子只能塗滿一個顏色,你畫出來的斜線會像樓梯一樣一格一格的,這就是鋸齒。抗鋸齒的解決方法很簡單:在邊緣的地方,不要只用純色,而是用一些半透明的像素來填補。
例如,一個黑色三角形覆蓋了一個像素的一半,傳統的做法是把整個像素塗黑,看起來就會很多鋸齒狀,而抗鋸齒的做法是把這個像素塗成灰色,讓它看起來像是半黑半白,這樣從遠處看,那條邊就會顯得平滑。
最常見的抗鋸齒技術是 MSAA,也就是多重採樣抗鋸齒。它在每個像素內部取多個點,計算這個三角形覆蓋了其中幾個點,然後根據覆蓋的比例來決定這個像素的顏色。
執行單元:抗鋸齒主要由渲染輸出單元(Render Output Unit,ROP)負責執行。在 MSAA 模式下,ROP 會管理每個像素內部的多個採樣點,執行多重採樣的深度測試和顏色混合,最後再將多個採樣點合併成一個最終的像素顏色。這也是為什麼 MSAA 對效能的影響相對較小,因為它只增加 ROP 的工作量,不增加片段著色器的工作量。
步驟 14:寫入與輸出
最終完美的像素顏色會被寫入影像緩衝區,也就是 Frame Buffer,然後輸出顯示在你的螢幕上。
白話解釋:影像緩衝區是一塊記憶體,專門存放已經處理好的完整畫面。當 GPU 把整個畫面的所有像素都處理完、寫入影像緩衝區之後,這個畫面就準備好了。然後顯示控制器會從影像緩衝區讀取資料,透過 HDMI 或 DisplayPort 線,一個像素一個像素地送到你的螢幕上,你就看到了最終的畫面。
執行單元:這個步驟的寫入動作由渲染輸出單元(Render Output Unit,ROP)負責,ROP 將最終的像素顏色寫入影像緩衝區所在的視訊記憶體。而將畫面從影像緩衝區傳送到螢幕的工作,則由 GPU 內部的顯示控制器(Display Controller)負責,這是一個獨立於 3D 渲染引擎的固定功能單元,專門負責時序控制和畫面輸出。
總結:執行單元的協同工作
上面講的這十四個步驟,每一個都是由 GPU 內部不同的事用硬體單元分工完成的,我們用一張簡化的對照表來總結:
| 流程步驟 | 執行單元 | 類型 |
|---|---|---|
| 步驟 1 到 3 的座標轉換 | 頂點著色器(Vertex Shader) | 可程式化 |
| 步驟 4 螢幕座標轉換 | 視口轉換單元(Viewport Transform Unit) | 固定功能 |
| 步驟 5 頂點與幾何處理 | 頂點著色器、幾何著色器、細分著色器(Vertex Shader、Geometry Shader、Tessellation Shader) | 可程式化 |
| 步驟 6 裁剪 | 圖元組裝單元、裁剪單元(Primitive Assembly Unit、Clipping Unit) | 固定功能 |
| 步驟 7 光柵化 | 光柵化器(Rasterizer) | 固定功能 |
| 步驟 8 與 9 紋理相關 | 紋理單元(Texture Mapping Unit,TMU) | 固定功能 |
| 步驟 10 光照與材質 | 片段著色器(Fragment Shader) | 可程式化 |
| 步驟 11 到 13 像素輸出 | 渲染輸出單元(Render Output Unit,ROP) | 固定功能 |
| 步驟 14 畫面輸出 | 渲染輸出單元、顯示控制器(ROP、Display Controller) | 固定功能 |
這些執行單元之所以能夠協同工作,靠的是 GPU 內部的管線化架構,每個單元都像工廠生產線上的一個工作站,當第一個單元處理完第 100 個三角形時,第二個單元可能正在處理第 80 個三角形,第三個單元在處理第 60 個三角形。這種設計讓 GPU 可以同時塞滿所有硬體單元,不會有人閒在那邊等,這就是為什麼 GPU 可以用那麼驚人的速度處理這麼複雜的畫面。
整個流程,從座標轉換到最終輸出,GPU 可以在短短不到 1 毫秒之內全部完成,可能在一秒鐘之內,這個流程會重複執行六十到一百多次,就產生了我們眼前極度流暢、充滿光影細節與真實質感的遊戲世界。
要深入了解 GPU ,請參考我們的詳細指南:GPU 是什麼?是專為計算 3D 圖形的晶片
