翻閱了不少資料,也作了很多筆記,決定仍是對渲染進行一個總結,以鞏固所學的東西。web
《Real-Time Rendering, Third Edition》 (PDF的配圖連接)將一個渲染流程分爲三個階段:編程
即 應用階段(PApplication Stage)、幾何階段(Geometry Stage)、光柵化階段(Rasterizer Stage)dom
我借用《Unity Shader入門精要》的網頁貼圖來講明ide
顏色表示了不一樣階段的可配置性或可編程性:綠色表示該流水線階段是徹底可編程控制的,黃色表示該流水線階段能夠配置但不是可編程的,藍色表示該流水線階段是由GPU固定實現的,開發者沒有任何控制權。實線表示該shader必須由開發者編程實現,虛線表示該Shader是可選的。測試
頂點着色器(Vertex Shader)、片元着色器(Fragment Shader)是咱們編寫 Shader最經常使用的二個。另外二個曲面細分着色器(Tessellation Shader)、幾何着色器(Geometry Shader)都是可選着色器。優化
既然提到了着色器(Shader),那什麼是Shader呢?this
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的模型頂點、紋理和其它信息,並獲取返回的像素顏色。3d
那Draw Call又是什麼呢?code
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
有興趣能夠觀看下面這個視頻,很形象和生動。
視頻地址:
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就會根據渲染狀態(例如材質、紋理、着色器等)和全部輸入的頂點數據來進行計算,最終輸出成屏幕上所顯示的那些像素。
只看文字難免過於抽象,我以前在網上看到一張圖,描述的挺不錯。
而實際的工做比這個要複雜不少,包括:座標轉換、透視、裁剪等一系列操做。
所上圖所示,須要歷經:座標轉換(模型空間 –> 齊次裁剪空間),逐頂點光照,透視除法(歸一化的設備座標 —— 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,以及如何優化。
先大致理解整個脈絡,中間每個點均可以單獨寫不少內容,可是總的流程是這樣的。對整個渲染流程,時刻要在腦海中有一個比較清晰的認識和了解,否則很容易被各類轉換和模型給搞暈,不知道當前處於什麼階段,輸入是什麼,輸出又是什麼,接下來要怎麼走。