Unity3D 渲染管线全流程解析
目錄
渲染管線(流水線,流程)
一、渲染任務(wù)
二、三個階段
1、應(yīng)用階段
? ? ? ? 1-1:數(shù)據(jù)的準備
? ? ? ? 1-2:設(shè)置渲染狀態(tài)
? ? ? ??1-3:發(fā)送DrawCall
2、幾何階段
? ? ? ? 2-1:頂點著色器
? ? ? ? 2-2:裁剪
? ? ? ? 2-3:屏幕映射
3、光柵化階段
? ? ? ? 3-1:三角形設(shè)置
? ? ? ? 3-2:三角形遍歷
? ? ? ? 3-3:片元著色器
? ? ? ? 3-4:逐片元操作
? ? ? ??
渲染管線(流水線,流程)
聲明:本文章參考自《Real-Time Rendering》書籍加以匯總并引用了其中部分圖片
一、渲染任務(wù)
? ? ? ? 渲染的任務(wù)其實就是從一個三維場景出發(fā),將其進行渲染生成一個二維圖像供人眼觀察。詳細點說,就是CPU和GPU配合,將3D場景中各個對象的坐標,紋理,材質(zhì)等信息經(jīng)一系列轉(zhuǎn)換生成人眼可以看見的圖像映射到屏幕上。
二、三個階段
? ? ? ? 渲染的三個階段一般分為,應(yīng)用階段,幾何階段和光柵化階段,其大致的流程參考下方圖1
?圖1 渲染管線流程圖
三個階段的操作對象的流程圖大致可以參考一下 圖2
?圖2 渲染管線操作對象流程圖
1、應(yīng)用階段
? ? ? ? 這一階段由CPU處理,主要任務(wù)是為接下來GPU的渲染操作提供所需要的幾何信息,即輸出渲染圖元(rending primitives)以供后續(xù)階段的使用。渲染圖元就是由若干個頂點構(gòu)成的幾何形狀,點,線,三角形,多邊形面都可以是一個圖元。
? ? ? ? 1-1:數(shù)據(jù)的準備
? ? ? ? ?第一步應(yīng)先將不需要的數(shù)據(jù)剔除出去,如以包圍盒為單位的視錐體(粗粒度)剔除,遮擋剔除,層級剔除等等。
?????????第二步根據(jù)UI對象在Herachy面板深度值的順序(DFS深度優(yōu)先搜索)設(shè)置渲染的順序,其余物體大體可以按照離攝像機先近后遠的規(guī)則為后續(xù)循環(huán)繪制所有對象制定排隊順序。
?????????第三步先將所有需要的渲染數(shù)據(jù)從硬盤讀取到主存中,再把GPU渲染需要用到的數(shù)據(jù)打包發(fā)給顯存(GPU一般沒有對主存的訪問權(quán)限,且與顯存進行交換速度較快)。
打包的數(shù)據(jù)詳細信息見圖3
?圖3 打包的數(shù)據(jù)信息
? ? ? ? 1-2:設(shè)置渲染狀態(tài)
? ? ? ? ?渲染狀態(tài)包括著色器(Shader),紋理,材質(zhì),燈光等等。
? ? ? ? ?設(shè)置渲染狀態(tài)實質(zhì)上就是,告訴GPU該使用哪個Shader,紋理,材質(zhì)等去渲染模型網(wǎng)格體,這個過程也就是SetPassCall。當使用不同的材質(zhì)或者相同材質(zhì)下不同的Pass時就需要設(shè)置切換多個渲染狀態(tài),就會增加SetPassCall?所以SetPassCall的次數(shù)也能反映性能的優(yōu)劣。
? ? ? ??1-3:發(fā)送DrawCall
? ? ? ? ?當收到一個DrawCall時,GPU會按照命令,根據(jù)渲染狀態(tài)和輸入的頂點信息對指定的模(網(wǎng)格)進行計算渲染。
? ? ? ? ?CPU通過調(diào)用圖形API接口(?glDrawElements (OpenGl中的圖元渲染函數(shù)) 或者 DrawIndexedPrimitive (DirectX中的頂點繪制方法)?)命令GPU對指定物體進行一次渲染的操作即為DrawCall。此過程實質(zhì)上就是在告訴GPU該使用哪個模型的數(shù)據(jù)(圖形API函數(shù)的功能就是將CPU計算出的頂點數(shù)據(jù)渲染出來)。
在應(yīng)用階段有三個衡量性能指標非常重要的名詞 下面我將再次敘述一下
DrawCall:CPU每次調(diào)用圖形API接口命令GPU進行渲染的操作稱為一次DrawCall。
SetPassCall:設(shè)置/切換一次渲染狀態(tài)。
Batch:把數(shù)據(jù)加載到顯存,設(shè)置渲染狀態(tài),CPU調(diào)用GPU渲染的過程稱之為一個Batch。
注:一個Batch包含至少一個DrawCall
??
2、幾何階段
? ? ? ? 幾何階段由GPU進行處理,其幾乎要處理所有和幾何相關(guān)的繪制事情。如繪制的對象,位置,形狀。幾何階段處理的對象時渲染圖元,進行逐頂點和逐多邊形的操作。主要任務(wù)是把頂點坐標變換到屏幕空間中,以供給接下來的光柵器進行處理。具體輸出的信息有,變換后的屏幕二位頂點坐標,頂點的深度值,著色,法線等等信息。
接下來對幾何階段的主要流水線階段進行一下解釋:
? ? ? ? 2-1:頂點著色器
? ? ? ? 流水線的第一個階段,其可以通過編程進行控制。輸入來自CPU發(fā)送的頂點信息,每個頂點都會調(diào)用一次頂點著色器。其主要工作為:坐標轉(zhuǎn)換和逐頂點光照(可選,計算輸出頂點的顏色值)。坐標轉(zhuǎn)換是必須完成的一個任務(wù)。其把頂點坐標從模型空間轉(zhuǎn)換到齊次裁剪空間。(齊次裁剪空間不是屏幕空間,是xyz均放縮到-1到1的空間),具體過程可以參考圖4
(提一下:此時GPU處理的頂點并不清楚頂點之間的關(guān)系,只是無差別的對待每個頂點,能? ? ? ? ? ? ?很好的體現(xiàn)各個部件的分離,降低耦合性)
圖4?坐標轉(zhuǎn)換
? ? ? ? 2-2:裁剪
? ? ? ? ?顧名思義,就是將不需要的數(shù)據(jù)對象剔除出去的過程。由于場景一般很大,攝像機的視野范圍可能不會覆蓋所有的場景物體,裁剪就是為了將那些在攝像機視野范圍外的物體剔除出去而被提出來的。
? ? ? ? 一個圖元和攝像機的關(guān)系有三種:完全在視野內(nèi)、部分在視野內(nèi)、完全在視野外。完全在視野內(nèi)的就傳遞給下一個流水線階段,完全在視野外的就不會向下傳遞,而部分在視野內(nèi)的就需要進行一次處理,就是裁剪。
下圖(圖5)展示了一個裁剪的過程:
圖5?裁剪過程
????????由圖5可清楚的看出,除完全在空間內(nèi)外的圖元被保留和舍棄以外,部分在空間內(nèi)的圖元(黃色三角形)會被裁剪,新的頂點將在空間的邊界處生成,原來在外部的頂點會被舍棄 。
? ? ? ? 2-3:屏幕映射
? ? ? ? ? ? ? ? 通過計算將實際場景的對象映射到屏幕上,實質(zhì)上就是對坐標的放縮,參考圖6
圖6 屏幕映射
3、光柵化階段
? ? ? ? 此階段仍然由GPU進行處理。這一階段將會使用上個階段傳遞的數(shù)據(jù)(屏幕坐標系下的頂點位置以及和它們相關(guān)的額外信息,如深度值(z坐標)、法線方向、視角方向等。)來產(chǎn)生屏幕上的像素,并渲染出最終的圖像。光柵化的主要任務(wù)是決定渲染圖元中的哪些像素應(yīng)該被繪制在屏幕上,然后對其顏色進行合并混合。
? ? ? ? 3-1:三角形設(shè)置
? ? ? ? ?其主要任務(wù)是為后續(xù)光柵化提供所需要計算的信息。例如,后續(xù)階段需要判斷像素點是否被三角形網(wǎng)格覆蓋,只靠上個階段得到的頂點信息無法確定邊界的覆蓋情況,還需要三角形網(wǎng)格邊的信息,所以在這個階段需要計算出邊的表達式以供后續(xù)判斷的使用。其輸出都是為了給下一階段做相應(yīng)的準備。
? ? ? ? 3-2:三角形遍歷
? ? ? ? ?三角形遍歷階段會根據(jù)上一個階段的計算結(jié)果判斷一個三角形網(wǎng)格覆蓋了哪些像素,并使用三角網(wǎng)格三個頂點的頂點信息對整個覆蓋區(qū)域進行插值。詳細過程見下方介紹:
? ? ? ? ?此階段會遍歷所有的像素點,判斷其是否被三角網(wǎng)格所覆蓋 (用3-1計算的結(jié)果) ,如果被覆蓋,則在此像素點上生成一個片元。片元不是單純的像素點,其還包含很多狀態(tài)的集合,這些狀態(tài)用來最終計算檢測篩選每個像素點最終的顏色。(部分狀態(tài)包括:屏幕坐標,深度值,從幾何階段繼承來的法線,紋理等等)。
? ? ? ? ? 片元狀態(tài)的信息是由其所在三角形網(wǎng)格的三個頂點的信息的插值得到的,例如計算三角形網(wǎng)格重心位置片元的深度 如下圖(圖7)
?圖7 片元狀態(tài)信息插值
?????????最終輸出的是包含多個片元的片元序列
? ? ? ? 3-3:片元著色器
? ? ? ? ?非常重要的可編程著色器階段。片元著色器的輸入是上一個階段對頂點信息插值得到的結(jié)果,輸出為每個片元的顏色值。這一階段可以按需完成很多重要的渲染技術(shù),最重要的技術(shù)之一就是紋理采樣。
??????????紋理采樣
? ? ? ? ? 為了在片元著色器中進行紋理采樣,先在頂點著色器階段輸出每個頂點對應(yīng)的紋理坐標,然后經(jīng)過光柵化階段對三角形網(wǎng)格的三個頂點對應(yīng)的紋理坐標進行插值后,就可以得到其覆蓋的片元的紋理坐標了。其局限在于僅可以影響單個片元。即執(zhí)行片元著色器時,不能將結(jié)果直接發(fā)給旁邊的鄰居。片段著色器輸出顏色的具體過程如下圖(圖8)
圖8 計算輸出顏色?
? ? ? ? 3-4:逐片元操作
? ? ? ? ?這是OpenGL中的說法,在DirectX中,這階段被稱為輸出合并階段(Output-Merger)。
????????該階段是對每一片 片元 進行操作,主要任務(wù)有:
????????①決定每個片元的可見性,如深度測試、模板測試。
????????②如果一個片元通過了所有測試,就把這個片元的顏色值和已經(jīng)存儲在顏色緩沖區(qū)的顏色進? ? ? ? ? ? ?行合并,混合。
????????????????該階段是高度可配置的,我們可以設(shè)置每一步的操作細節(jié)。該階段首先解決的是,每個 ????????片元的可見性問題。這需要進行一系列測試,只有通過了才能和顏色緩沖區(qū)進行合并。沒通? ? ? ? ? ? 過任何一 個測試,片元都會被丟棄。見圖(9)
圖9 片元測試及合并
? ? ? ? ?測試過程是很復雜的,不同接口實現(xiàn)細節(jié)也不同,下面筆者將講述一些常用的測試:
? ? ? ? ?模板測試
? ? ? ? ? ? ? ? 開啟了模板測試,GPU就會使用讀取掩碼讀取模板緩沖區(qū)中該片元的模板值,將該值和讀取到的參考值進行比較。這個比較函數(shù)可以是開發(fā)者指定的,例如小于模板值時則舍棄該片元或者大于模板值時舍棄該片元。片元無論有沒有通過模板測試都可以根據(jù)模板測試和下面的深度測試結(jié)果來修改模板緩沖區(qū)。這個修改操作也是由開發(fā)者指定的。模板測試通常用于限制渲染的區(qū)域。
????????深度測試
? ? ? ? 通過模板測試后,片元就會進行深度測試。其同樣是高度可配置的。
????????開啟后,GPU會把該片元深度值和已存在與深度緩沖區(qū)的深度值進行比較,這個比較函數(shù)也是開發(fā)者設(shè)置的。例如小于緩沖區(qū)深度值時舍棄該片元,或者大于緩沖區(qū)深度值等于時舍棄該片元。通常人們更希望顯示離攝像機最近的物體,所以一般比較函數(shù)設(shè)置為當前片元深度值要小于緩沖區(qū)深度值,深度值大無法通過測試。如果片元沒有通過測試,則會被丟棄掉。
與模板測試不同,只有通過之后開發(fā)者才能指定是否用該片元的深度值覆蓋原有緩沖區(qū)的深度值。這是通過開啟/關(guān)閉深度寫入做到的。
????????合并操作
????????通過了所有測試后,片元就來到了合并操作。
每個像素的信息被存儲在一個名為顏色緩沖區(qū)的地方,因此執(zhí)行此次渲染時,顏色緩沖區(qū)中往往已經(jīng)有了上次渲染之后的結(jié)果。所以需要合并的方式使其達到一種均衡狀態(tài)。
對于不透明物體,開發(fā)者可以選擇關(guān)閉混合操作。這樣片元著色器計算得到的顏色值就會直接覆蓋原來顏色緩沖區(qū)中的像素值。
對于半透明物體,需要使用混合操作來讓這個物體看起來是透明的。
混合操作也是可以高度配置的。開啟了混合,GPU會取出源顏色和目標顏色將兩者混合。
源顏色是片元著色器得到的顏色,目標顏色是已經(jīng)存在于顏色緩沖區(qū)中的顏色值。
????????提前測試
? ? ? ??提前測試的目的主要是為了提高性能
雖然邏輯上這些測試是在片元著色器之后進行的,但對于大多數(shù)GPU來說,他們會盡可能在執(zhí)行片元著色器之前進行這些測試。
盡可能早知道哪些片元會被舍棄可以提高性能,比如unity的渲染流水線中的深度測試就在片元著色器之前。這種將深度測試提前的技術(shù)被稱為Early-Z技術(shù)。
但將這些測試提前其檢驗結(jié)果可能會與片元著色器中一些操作產(chǎn)生沖突。
????????至此Unity的渲染管線的大致過程就已經(jīng)介紹完畢啦~,這是筆者第一次攥寫博客部分繪制的流程圖和解釋還較為粗糙,后續(xù)還會繼續(xù)編寫有關(guān)Unity的內(nèi)容,希望能對大家有所幫助(.^?^.)
? ? ? ??
總結(jié)
以上是生活随笔為你收集整理的Unity3D 渲染管线全流程解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 时间复杂度和空间复杂度,一看就懂,面试前
- 下一篇: KNN 算法实现 Iris 数据集分类