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