渲染流水線的工作任務
根據一個三維場景,生成一張二維圖像
從一系列的頂點數據、紋理等信息出發(fā),將這些信息轉換為一張人眼可見的圖像
該工作由CPU和GPU共同完成
渲染流程可從概念上分為3個階段
應用階段
在CPU中進行(該階段由開發(fā)者控制)
任務
準備好場景數據
- 攝像機的位置
- 視錐體
- 場景中包含的模型
- 場景中使用的光源
粗粒度剔除
- 剔除不可見的物體(避免在下一階段時再處理)
- 為了提高渲染性能
設置各模型的渲染狀態(tài)
- 使用的材質(漫反射顏色、高光反射顏色)
- 使用的紋理
- 使用的Shader
輸出
渲染所需的幾何信息(渲染圖元)
- 點、線、三角面
幾何階段
在GPU中進行
任務
對各渲染圖元進行處理,將頂點坐標變換到屏幕空間中
輸出
屏幕空間的二維頂點坐標、各頂點的深度值和著色等信息
光柵化階段
在GPU中進行
任務
決定各渲染圖元中哪些像素應被繪制到屏幕上(對各頂點數據進行插值后,逐像素處理)
輸出
最終的二維圖像
真正用于實現的渲染流水線
應用階段
將數據加載到顯存
所有渲染所需的數據都需從硬盤加載到內存中,然后,網格和紋理等數據需加載到顯存(顯卡的存儲空間)中
因為
- 顯卡訪問顯存比訪問內存快
- 大多數顯卡無權直接訪問內存
需要加載到顯存的數據
- 頂點的位置信息、法線方向、顏色
- 紋理坐標
對于加載到顯存的數據,如果CPU無需訪問(有時CPU需要訪問網格數據進行碰撞檢測,這種情況不能刪除內存中的該數據,因為將數據從硬盤加載到內存很耗時),則可以刪掉內存中的數據
設置渲染狀態(tài)
渲染狀態(tài)定義了場景中的網格如何(使用哪個著色器,光源屬性,材質)被渲染
CPU設置渲染狀態(tài)來指導GPU渲染
CPU通過調用渲染命令DrawCall來通知GPU
調用Draw Call
該命令由CPU發(fā)起,由GPU接收,指向一個需被渲染的圖元列表
GPU根據渲染狀態(tài)和頂點數據進行計算,輸出成在屏幕上顯示的像素
該計算過程就是GPU流水線(幾何階段和光柵化階段)
幾何階段
頂點著色器
完全可編程
輸入進來的每個頂點都會調用一次頂點著色器
頂點著色器無法創(chuàng)建或銷毀頂點,無法得到頂點間關系(因頂點相互獨立,所以可并行處理多個頂點)
可修改頂點坐標和顏色,模擬水面或布料等
頂點著色器必須要將頂點坐標從模型空間轉換到齊次裁剪空間(最基本工作)
頂點著色器可將數據經光柵化后交給片元著色器處理,在現代Shader Model中,可將數據發(fā)送給曲面細分著色器或幾何著色器
曲面細分著色器
非必須執(zhí)行
細分圖元
幾何著色器
非必須執(zhí)行
逐圖元地著色或產生更多圖元
裁剪
不可編程(是硬件上的固定操作),可配置(自定義裁剪平面來配置裁剪區(qū)域,控制裁剪三角圖元的正或反面)
將不在攝像機視野內的頂點裁剪掉并剔除某些三角圖元的面片
圖元與攝像機視野的關系有3種
- 完全在視野內
傳遞給下一階段 - 部分在視野內
進行裁剪操作 - 完全在視野外
無需傳遞給下一階段(無需被渲染)
屏幕映射
不可配置,不可編程
將各圖元坐標(三維坐標)轉換到屏幕坐標系(二維坐標)
OpenGL的最小窗口坐標為左下角
DirectX的最小窗口坐標為左上角
光柵化階段
計算各圖元覆蓋了哪些像素,計算這些像素的顏色
三角形設置
固定函數階段
計算三角網格表示數據的過程
根據頂點數據算出邊界各像素的數據,使用算得的數據表示三角形邊界
判斷一個三角網格覆蓋了哪些像素,根據頂點信息對覆蓋區(qū)域的像素進行插值,得到一個片元序列(片元不是像素,包含了許多用于計算像素最終顏色的狀態(tài),如坐標、深度、紋理、法線等)
三角形遍歷(掃描變換)
固定函數階段
計算三角網格表示數據的過程
根據頂點數據算出邊界各像素的數據,使用算得的數據表示三角形邊界
判斷一個三角網格覆蓋了哪些像素,根據頂點信息對覆蓋區(qū)域的像素進行插值,得到一個片元序列(片元不是像素,包含了許多用于計算像素最終顏色的狀態(tài),如坐標、深度、紋理、法線等)
片元著色器(在DirectX中叫 像素著色器)
完全可編程
逐片元地著色,僅影響單個片元,但可訪問導數信息
根據輸入的頂點信息進行插值,得到各片元的信息
逐片元操作
不可編程,高度可配置(設置每一步的操作細節(jié))
通過進行深度測試和模板測試等來決定片元的可見性(是否允許其與顏色緩沖區(qū)合并)
模板測試
用于限制渲染區(qū)域、渲染陰影、輪廓渲染等
讀取模板緩沖區(qū)中該片元位置的模板值,與讀取到的參考值進行比較,根據比較結果修改模板緩沖區(qū),并決定是否舍棄該片元
深度測試
將該片元的深度值與深度緩沖區(qū)中的深度值進行比較,決定是否舍棄該片元
未通過測試的片元無權修改深度緩沖區(qū)的值,可使用通過測試的片元的深度值覆蓋原深度值(也可不覆蓋)
可通過Early-Z技術將深度測試提前到片元著色器之前(但有時需在片元著色器階段判斷是否要舍棄片元)
合并
對通過了所有測試的片元,將其顏色值與已存儲在顏色緩沖區(qū)中的顏色進行合并(混合)
決定是使用本次渲染得到的顏色完全覆蓋掉之前的結果還是進行其他處理
屏幕圖像
概念
OpenGL/DirectX
直接訪問GPU很麻煩,麻煩體現在需要與各種寄存器和顯存打交道
為了消除麻煩,在硬件基礎上實現一層抽象,即圖像編程接口(OpenGL和DirectX)
應用程序向接口發(fā)送渲染命令,接口向顯卡驅動發(fā)送渲染命令,顯卡驅動將命令翻譯成GPU能理解的代碼,進行繪制
應用程序 -> 圖像編程接口 -> 顯卡驅動 -> 顯存和GPU
HLSL/GLSL/CG
GLSL:OpenGL,OpenGL Shading Language,可在多平臺工作(原因是OpenGL未提供著色器編譯器,由顯卡驅動來完成著色器編譯工作,因此只要顯卡驅動支持該語言的編譯即可),依賴硬件,導致編譯結果不一致
HLSL:DirectX,High Level Shading Language,微軟控制著色器編譯,與硬件無關,僅支持微軟平臺
CG:NVIDIA,C for Graphic,與HLSL極相像,根據平臺不同可編譯成對應的中間語言(跨平臺)
Draw Call
CPU調用圖像編程接口,命令GPU進行渲染
CPU和GPU如何并行工作
使用命令緩沖區(qū),使二者并行工作
緩沖區(qū)中包含一個命令隊列
- CPU添加指令
- GPU讀取指令
二者相互獨立工作
Draw Call多了影響幀率
渲染速度快于提交命令的速度,過多的Draw Call導致CPU花費大量時間用在提交Draw Call上,造成CPU過載
如何減少Draw Call
批處理(將許多小Draw Call合并為一個大Draw Call)
適用于靜態(tài)物體
- 避免使用大量小的網格(考慮合并)
- 避免使用過多材質(共用材質)
固定管線渲染
開發(fā)者只能進行配置操作(無法完全控制)
將該管線想象成一個控制電路,開發(fā)者只能設置電路中各開關的狀態(tài),無法控制電路排布
已退出歷史舞臺(被可編程渲染管線取代)
Shader(著色器)
GPU流水線中一些可高度編程的階段,由著色器編譯出來的最終代碼在GPU上運行
有特定類型:頂點著色器,片元著色器
可通過著色器控制流水線的渲染細節(jié)
- 頂點著色器 -> 頂點變換和傳遞數據
- 片元著色器 -> 逐像素渲染