Shader、Draw Call和渲染管線(Rendering Pipeline)


翻閱了很多資料,也做了不少筆記,決定還是對渲染進行一個總結,以鞏固所學的東西。

 

《Real-Time Rendering, Third Edition》   (PDF的配圖鏈接)將一個渲染流程分為三個階段:

應用階段(PApplication Stage)、幾何階段(Geometry Stage)、光柵化階段(Rasterizer Stage)

 

我借用《Unity Shader入門精要》的網頁貼圖來說明

概念流水線.png-16.9kB

GPU流水線.png-82.2kB

顏色表示了不同階段的可配置性或可編程性:綠色表示該流水線階段是完全可編程控制的,黃色表示該流水線階段可以配置但不是可編程的,藍色表示該流水線階段是由GPU固定實現的,開發者沒有任何控制權。實線表示該shader必須由開發者編程實現,虛線表示該Shader是可選的。

 

頂點着色器(Vertex Shader)、片元着色器(Fragment Shader)是我們編寫 Shader最常用的二個。另外二個曲面細分着色器(Tessellation Shader)、幾何着色器(Geometry Shader)都是可選着色器。

 

既然提到了着色器(Shader),那什么是Shader呢

A shader is a piece of code, that is executed on the GPU. The engine feeds it with 3d model vertices, textures and other information, and gets back from it pixel colours.
-- from What is a Shader?

翻譯過來:Shader 就是運行在GPU上的一段代碼,引擎提供給它3D的模型頂點、紋理和其它信息,並獲取返回的像素顏色。

 

Draw Call又是什么呢

A draw call is a command to render one mesh. It is given by the CPU. It is received by the GPU. The command only points to a mesh which shall be rendered and doesn’t contain any material information since these are already defined via the render state. The mesh resides at this point in the memory of your graphic card (VRAM).

-- from renderhell-book1

image

有興趣可以觀看下面這個視頻,很形象和生動。

視頻地址:

https://data.simonschreibt.de/gat049/cpu_calls_gpu.webm

https://data.simonschreibt.de/gat049/commandbuffer_communication_chunk.webm

 

實際上,Draw Call就是一個命令,它的發起方是CPU,接收方是GPU。這個命令僅僅會指向一個需要被渲染的圖元(primitives)列表,而不會再包含任何材質信息(這些信息已經在渲染狀態中被定義了),此時網格是駐留在顯存(Video Random Access Memory)中的。

 

當給定了一個Draw Call時,GPU就會根據渲染狀態(例如材質、紋理、着色器等)和所有輸入的頂點數據來進行計算,最終輸出成屏幕上所顯示的那些像素。

 

只看文字不免過於抽象,我之前在網上看到一張圖,描述的挺不錯。

各着色器

 

而實際的工作比這個要復雜很多,包括:坐標轉換、透視、裁剪等一系列操作。

RTR3.02.03

所上圖所示,需要歷經:坐標轉換(模型空間 –> 齊次裁剪空間),逐頂點光照,透視除法(歸一化的設備坐標 —— Normalized Device Coordinates, NDC),裁剪,屏幕映射。

 

這里再多解釋一下,何為齊次裁剪空間。齊次裁剪空間是一個中心點是坐標原點的立方體,xyz取值范圍是[-1, 1]。使用一個4x4的齊次變換矩陣將點從攝像機坐標空間變換到齊次裁剪空間,將頂點的深度值z保存在頂點經過變換得到的齊次坐標的w分量中。最后,把頂點在齊次空間中的坐標通過將x,y,z分量除以w分量的方式,將齊次坐標轉為NDC。

 

為什么需要使用NDC呢?為了適配屏幕的多分辨率問題。

歸一化坐標中,兩個軸其中一個軸的范圍是由0至1(但不能兩個都是0~1),而且能輕易縮放至不同分辨率下的像素單位。假設把y軸的范圍設置為0.0 ~ 1.0,當使用4:3長寬比時,x軸的范圍就是0.0至1.333(=4/3),而16:9時x軸的范圍則是0.0 ~ 1.777(=16/9),這樣就不會出現拉伸了。

 

 

 

 

 

接下來就進入光柵化階段了。

接收頂點信息,進行適當的轉換后,對頂點進行插值處理,然后對三角形進行遍歷,檢查每個網格是否被三角形覆蓋,如果被覆蓋就會生成一個片元。之后交給片元着色器(在DirectX中,也稱為像素着色器 Pixel Shader)。

 

經過片元着色器(Fragment Shader)處理后,得到一個或者多個顏色值(如下圖所示)。

 

 

之后進入逐片元操作(Per-Fragment Operations),會經過模板測試(Stencil Test)、深度測試(Depth Test)、混合(Blend)等一系列操作,最后得到的結果(一系列顏色值)存放到幀緩沖區,供GPU進行屏幕的更新。

 

 

大體的渲染過程就如上所述。中間忽略了不少信息,如坐標轉換(主要使用矩陣、四元數,矩陣就是映射),還有投影(正交/平行投影、透視投影),以及光照模型(各種貼圖和法線、切線等)。整個過程中裁剪不僅僅只發生在應用階段,背面剔除、遮擋剔除(光柵化階段)都使用了裁剪。也沒有提及為什么是減少Draw Call,以及如何優化。

 

先大體理解整個脈絡,中間每一個點都可以單獨寫很多內容,但是總的流程是這樣的。對整個渲染流程,時刻要在腦海中有一個比較清晰的認識和了解,不然很容易被各種轉換和模型給搞暈,不知道當前處於什么階段,輸入是什么,輸出又是什么,接下來要怎么走。


免責聲明!

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



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