【技術美術百人計劃】延遲渲染
渲染路徑
決定光照的實現(xiàn)方式,簡而言之,就是當前渲染目標使用光照的方式
渲染方式

前向渲染
流程

待渲染物體--頂點著色器--片元著色器--渲染目標
在渲染每一幀時,每個頂點/片元都要執(zhí)行一次片元著色器代碼,這時需要將所有的光照信息都傳遞到片元著色器中。
雖然大部分情況下的光源都趨向于小型化,距離很遠,效果較差,但在計算光源時還是會把所有的光源都計算進去。
規(guī)則(如何渲染每一幀的)和注意事項
發(fā)生在頂點處理階段,會計算所有頂點的光照。(全平臺支持)
規(guī)則1:最亮的幾個光源會被實現(xiàn)為像素光照
規(guī)則2:然后就是,最多四個光源會被實現(xiàn)為頂點光照
規(guī)則3:剩下的光源會實現(xiàn)為效率較高的球面調(diào)諧光照(Spherical Hamanic),這是一種模擬光照
補充說明
最亮的那盞光一定是像素光照
Light的Render Mode是important的光一定是像素光照
如果前面的兩條加起來的像素光照小于Quality Setting里的Pixel Light Count(最大像素光照數(shù)量),那么從剩下的光源中找出最亮的那幾盞光源,實現(xiàn)為像素光照。
最后剩下的光源,按照規(guī)則2或3。
在base pass里執(zhí)行一盞像素光、所有的頂點光和球面調(diào)諧光照,并且進行陰影計算。
其余的像素光每盞一個Additional Pass,并且這些pass里沒有陰影計算。
場景中看到的陰影,全是base pass里計算出最亮那盞像素光的陰影,其他像素光是不計算陰影的。
最多的光源數(shù)是可以更改的
以Unity中的為例,在project setting中

所以,如果一個物體受到n個光源影響,那么每個片元著色器執(zhí)行代碼時,都必須把n個光源傳遞給著色器中進行計算。

延遲渲染
一句話概括:先不計算光照,延遲到最后再一起計算
主要解決大量光照的渲染方案
延遲渲染實質上,是先不要做迭代三角形做光照計算,而是先找出你能看到的所有像素,再去迭代光照。直接迭代三角形,會造成極大的浪費。
流程

流程為:待渲染幾何體 → 頂點著色器 → MRT → 光照計算 → 渲染目標
過程可以拆分為兩個pass
第一個pass:幾何處理通路。
首先將場景渲染一次,獲取到的待渲染對象的各種幾何信息存儲到名為G-buffer的緩沖區(qū)中,這些緩沖區(qū)用來之后進行更復雜的光照計算。
由于有深度測試,所以最終寫入G-buffer中的,都是離攝像機最近的片元的集合屬性,這就意味著,在G-buffer中的片元必定要進行光照計算。
第二個pass:光照處理通路。
這個pass會遍歷所有G-buffer中的位置、顏色、法線等參數(shù),執(zhí)行一次光照計算。
一些注意事項
G-buffer的概念
G-Buffer,全稱Geometric Buffer ,譯作幾何緩沖區(qū),它主要用于存儲每個像素對應的位置(Position),法線(Normal),漫反射顏色(Diffuse Color)以及其他有用材質參數(shù)。
根據(jù)這些信息,就可以在像空間(二維空間)中對每個像素進行光照處理。
如圖為一個典型的G-buffer

UE4默認使用的是延遲管線
我們在視圖模式---緩沖顯示---總覽,就可以看到所有G-buffer的預覽
延遲渲染不支持透明物體的渲染
因為沒有深度信息
延遲渲染中的透明物體渲染方式和前向渲染相同
偽代碼?

RT(G-buffer)相當于把整個屏幕的信息繪制到一個圖中,每個RT都可以寫到一個G-buffer中
G-buffer中的數(shù)據(jù)都是2D的,所以我們的光照計算就相當于一個2D的光照后處理
渲染優(yōu)劣

優(yōu)點
1.支持半透明渲染
2.支持使用多個光照pass
3.支持自定義光照計算方式
(延遲渲染是渲染到Gbuffer,再一起計算光照,所以不支持每一個物體用單獨的光照方式計算)
缺點
1.光源數(shù)量對計算復雜度影響巨大
2.訪問深度等數(shù)據(jù)需要額外計算(需要再渲染一張深度圖)
2、延遲渲染的優(yōu)點、缺點
優(yōu)點
1.大量光照場景的情況下,優(yōu)勢明顯
2.只渲染可見像素,節(jié)省計算量
3.對后處理支持良好(例如深度信息:直接拿G-buffer中的就行)
4.用更少的shader(所有的物體光照模型都一樣,很多東西不用再定義了)
缺點
1.對MSAA支持不友好
2.透明物體渲染存在問題(深度問題,只渲染力物體最近的物體,渲染透明度時會出現(xiàn)問題)
3.占用大量的顯存帶寬
涉及一個clear的操作,如果不清理的話,后邊可以繼續(xù)獲取到
每一幀都需要幾張rt在顯存中傳輸、清理等,會更耗帶寬
4.只能使用同一個光照pass
其他部分
1、渲染路徑的設置
Project Setting中進行設置


2、TBDR
有兩個TBDR,名字一樣,內(nèi)容不同
第一個:
是SIGGRAPH2010提出的,作為傳統(tǒng)Defferred Rendering的另一種主要改進,分塊延遲渲染(Tile-Based Deferred Rendering,TBDR)旨在合理分攤開銷(amortize overhead),自SIGGRAPH 2010上提出以來逐漸為業(yè)界所了解?;谘舆t渲染的優(yōu)化方式,通過分塊來降低帶寬內(nèi)存用量(解決帶寬和內(nèi)存問題)

延遲渲染的分塊,把整個圖像分為很多塊,再一塊一塊的渲染。
第二個:
PowerVR基于手機GPU的TBR框架提出的改進,通過HSR減少Overdraw
TBDR這個架構是PowerVR提出來的對TBR的一次改進,在TBR的基礎上再加了一個Deferred。
通過做一些可見性測試來減少Overdraw
涉及手機GPU架構,和延遲渲染沒什么關系
這里提到的關于TBR 、PowerVR提出的TBRD,作業(yè)部分有整理,詳細的也可以看知乎的這篇文章https://zhuanlan.zhihu.com/p/259760974

3、其他渲染路徑
延遲光照(Light Pre-Pass / Deferred Lighting)
減少G-buffer占用的過多開銷,支持多種光照模型
和延遲渲染的區(qū)別:
用更少的buffe信息,著色計算的時候用的是forward,所以第三步開始都是前向渲染(可以對不同的物體進行不同的光照模型)
Forward+(即Tiled Forward Rendering,分塊正向渲染)
減少帶寬,支持多光源,強制需要一個preZ
通過分塊索引的方式,以及深度和法線信息來到需要進行光照計算的片元進行光照計算。
需要法線和深度的后處理需要單獨渲染一個rt出來
強制使用了一個preZ(如果沒涉及過這個概念的話,可以理解為進行了一個深度預計算)
群組渲染(Clustered Rendering)
帶寬相對減少,多光源下效率提升
分為forward和deferred兩種
詳細補充拓展:https://zhuanlan.zhihu.com/p/54694743
延遲渲染中MSAA存在的問題
前邊補充的圖里有提到“NO MSAA Possible”,也就是說延遲渲染管線不支持MSAA
MSAA在延遲渲染中存在的問題:像素已經(jīng)被光柵化了,所以沒法用更大的像素來渲染。
不同path下光源shader的編寫
詳情請看官方文檔
PreZ(Zprepass)
實際上就是一個深度計算
和深度圖的區(qū)別
都是深度信息
PreZ是用一個pass,只算深度
深度圖是算成了一張RT(RenderTexture),把深度信息繪制到了一張RT上。
具體用途:
大規(guī)模草、透明排序會用到PreZ
early-z 和 PreZ的區(qū)別
early-z,自動的,對面數(shù)有要求(硬件自動)
PreZ,當early-z失效的時候,或者需要深度圖的時候,一種手動代替的方案
后邊課程還會細講
7、一些補充
Unity的urp是不支持延遲渲染的,老管線支持。
UE默認管線就是延遲渲染管線
一般延遲渲染用于主機/大項目
【抄作業(yè)】
總結延遲渲染管線的優(yōu)缺點
優(yōu)點:
1.大量光照場景優(yōu)勢明顯
2.只渲染可見像素,節(jié)省計算量
3.對后處理支持良好(例如深度信息:直接拿G-buffer中的就行)
缺點:
1.不支持MSAA
2.透明物體渲染不行
3.占用大量的顯存帶寬
涉及一個clear的操作,如果不清理的話,后邊可以繼續(xù)獲取到
每一幀都需要幾張rt在顯存中傳輸、清理等,會更耗帶寬
4.延遲渲染只能用同一套lighting pass(光照是一起計算的)
二、延遲渲染管線的優(yōu)化(移動端)
1、出發(fā)點
既然提到優(yōu)化,那么我們可以從缺點理解:在P1部分may佬提到,延遲渲染在移動端的一個很大的缺點:帶寬占用過高。
這里我們就要提到PC端構架IMR和移動端構架TBR了
2、IMR
IMR的全稱是Immediate Mode Rendering,我們通常說PC端GPU渲染用的就是下面這種架構

上圖中,藍色部分就是正常的渲染管線,灰色部分對應GPU中的顯存(包括幾何信息,紋理信息,深度Buffer還有FrameBuffer等等。)
這種渲染架構下信息傳出的特點:
對于每一個具體的繪制,渲染管線里的讀寫操作都是直接在顯存和GPU中傳輸數(shù)據(jù)的,比如說上圖中灰色和藍色之間交互的箭頭,向上的箭頭表示讀取,向下的箭頭表示寫回。
缺點:
在這種架構下,每一次渲染完的Color和Depth數(shù)據(jù)寫回到Frame Buffer和 Depth Buffer都會產(chǎn)生很大的帶寬消耗。
3、TBR
TBR的由來:
移動端的瓶頸-功耗:因為移動端的硬件在設計最開始想到的最重要的問題就是功耗(功耗意味著發(fā)熱量,意味著耗電量,意味著芯片大小,所以手機GPU的設計也是把功耗擺在第一位。)
而在GPU渲染過程中,對功耗影響最大的就是帶寬,所以為了從設計層面減少帶寬消耗,就延伸出了移動端的構架TBR
TBR是什么
TBR全稱是Tile-Based Rendering,

上圖中,可以看到兩個灰色部分,因為手機端是沒有顯存的,所有最下邊一層灰色部分是系統(tǒng)內(nèi)存。
選擇TBR的原因
在移動端,如果是一個1920*1080的屏幕上,每渲染一幀圖像,對FrameBuffer的訪問量是驚人的(各種test,blend,再算上MSAA, overdraw等等)
通常gpu的onchip memory(也就是SRAM,或者L1 L2 cache)很小,這么大的FrameBuffer要存儲在離gpu相對較遠的DRAM(顯存)上,可以這樣理解:
把gpu想象成你家,SRAM想象成小區(qū)便利店,DRAM想象成市中心超市,從gpu對framebuffer的訪問就相當于一輛貨車大量的在你家和市中心之間往返運輸,帶寬和發(fā)熱量之巨大是手機上無法接受的。
所以移動端的gpu想到了一種化整為零的方法
把巨大的FrameBuffer分解成很多小塊,使得每個小塊可以被離gpu更近的那個SRAM可以容納,塊的多少取決于你的硬件的SRAM的大小。這樣gpu可以分批的一塊塊的在SRAM上訪問framebuffer,一整塊都訪問好了后整體轉移回DRAM上,這樣問題就解決了
使用TBR可以減少帶寬的原因
對FrameBuffer現(xiàn)在幾乎全部的訪問在渲染這一塊的時候(test,read,write,blend…)都完全在SRAM上解決,只在最后把這一塊整體渲染完了才整體搬回DRAM。這種模式就叫做TBR(tile-based-rendering)
也就是說:對于上面圖中TBR和系統(tǒng)內(nèi)存數(shù)據(jù)傳輸部分的箭頭,讀取只發(fā)生在需要幾何以及紋理信息的時候,寫回也只發(fā)生在整批繪制畫完的時候,具體的繪制都是在On_Chip Memory上完成的,也就是帶寬消耗最大的DepthBuffer 和 ColorBuffer的讀寫都發(fā)生在On_Chip Memory上
TBR和IMR的對比如下圖

TBR的過程/內(nèi)容
對于TBR來講,整個光柵化和像素處理會被分為一個個Tile進行處理(通常為16×16大小的Tile),先執(zhí)行所有draw call的vertex shading,拼裝成相應的圖元后,接著對于每一個tile,各用一張鏈表(polygon list)來記錄位于該tile中的圖元;然后對于每一個tile中的每一個圖元進行光柵化和fragment shading。
簡要概括過程:
1.緩存所有繪制指令直到最后才開始進行真正繪制
2.首先對所有圖元執(zhí)行VS,把相關的所有繪制數(shù)據(jù)保存起來
3.計算每個tile都包含哪些圖元
4.開始繪制一個tile
5.依次繪制tile中的每個圖元
6.把繪制好的tile拷貝到framebuffer對應的位置上
TBR的結構通過On-Chip Buffers來儲存Tiling后的Depth Buffer和Color buffer。
實際上TBR的實現(xiàn)策略
實際上,在TBR的架構里,并不是來一個繪制就執(zhí)行一個的,因為任何一個繪制都可能影響到到整個FrameBuffer,如果來一個畫一個,那么GPU可能會在每一個繪制上都來回運輸所有的Tile,這太慢了。
所以,TBR一般的實現(xiàn)策略是對于Cpu過來的繪制,只對他們做頂點處理,也就是上圖中的Geometry Processor部分,產(chǎn)生的結果(Frame Data【2】)暫時寫回到物理內(nèi)存,等到非得刷新整個FrameBuffer的時候【1】,GPU才知道拖不了了,就會將這批繪制做光柵化,做tile-based-rendering。
注【1】:對于非得刷新整個FrameBuffere的時候的解釋:比如Swap Back and Front Buffer,glflush,glfinish,glreadpixels,glcopytexiamge,glbitframebuffer,queryingocclusion,unbind the framebuffer.總之是所有gpu覺得不得不把這塊fb繪制好的時候
注【2】:FrameData是TBRr特有的在gpu繪制時所需的存儲數(shù)據(jù)。

如圖,上面是TBR,下面是IMR? ??,這里可以看到TBR的管線上在PixelShader之前增多了一個步驟,即vs和gs處理后的數(shù)據(jù)(這里叫做FrameData)被暫時保存下來排好隊,然后后面再對framebuffer分塊,然后對每一塊,繪制所有影響這個塊的pixel。
所以TBR在本質上就是DeferredRendering,因為它對于IMR來說,將Rasterize/PS的處理延后到了處理完所有VS之后,而不是像IMR一樣VS后馬上Rasterize/PS。
4、TBDR
什么是TBDR
雖然TBR也有一次Defer,但是實際上只有只有PowerVR的GPU是TBDR架構,別的移動GPU是TBR架構。
TBDR這個架構是PowerVR提出來的對TBR的一次改進,在TBR的基礎上再加了一個Deferred。
PowerVR的渲染架構中還多了一步Rasterize到PS的延遲,PowerVR為處理這一步延遲的組件起名叫HSR(Hidden Surface Removal)并申請了專利
TBR和TBDR的區(qū)別
TBR:VS - Defer - RS - PS
TBDR:VS - Defer1 - RS - Defer2 - PS
通過這第二個Defer,PowerVR的渲染架構真正最大程度上實現(xiàn)了“延后(Defer)PS的執(zhí)行”,以避免執(zhí)行不必要的PS計算與相關資源調(diào)用的帶寬開銷,以達到最少的性能消耗和最高的渲染效率。
注意:有些文章會將TBR叫作TBDR,所以看資料的時候,要搞清楚它說的"D"是指第一個Defer還是第二個。
HSR技術
首先回顧一下之前深度測試有學到的Early-Z
具體的Early-Z內(nèi)容不做整理了,可以回顧之前的筆記,簡單的來說就是:繪制前先做深度檢測,減少不必要的繪制。
但是Early-Z是不能完全避免OverDraw的,這是因為我們在真正對一個復雜場景去渲染的時候是不可能進行嚴格的由近到遠的繪制的,例如下圖

EarlyZ特性對渲染物體進行排序優(yōu)化對于移動GPU也是同樣適用,除了PowerVR(TBRD做的比Early-Z要更好)
PowerVR提出的HSR技術,不需要在軟件層面對物體進行排序,直接提供硬件級別的支持來解決OverDraw。
簡述原理:
當一個像素通過了EarlyZ準備執(zhí)行PS進行繪制前,先不畫,只記錄標記這個像素歸哪個圖元來畫。等到這個Tile上所有的圖元都處理完了,最后再真正的開始繪制每個圖元中被標記上能繪制的像素點。
這樣每個像素上實際只執(zhí)行了最后通過EarlyZ的那個PS,而且由于TBR的機制,Tile塊中所有圖元的相關信息都在片上,可以極小代價去獲得,最終零Overdraw
至于為什么能夠做到,這是利用了TBR的特點,我們知道來了一批Drawcall,在TBR架構下是不會立刻去畫的,而是把幾何階段處理后的結果存在FrameData中,等到必須要畫整個FrameBuffer的繪制的時候才會去著色,所以HSR發(fā)生在著色前,擁有這批繪制需要的所有幾何信息,自然能夠做到像素級別的深度測試支持,而不需要關心EarlyZ技術里面需要的繪制順序問題。

5、優(yōu)化建議
//以下摘自參考資料,詳情可點開參考資料細看
記得不使用Framebuffer的時候clear或者discard
在每幀渲染之前盡量clear
不要在一幀里面頻繁的切換framebuffer
貼圖格式能壓縮就壓縮
能開mipmap就開mipmap
隨機紋理尋址相對于相鄰紋理尋址有顯著開銷
Trilinear/Anisotropic相對于Bilinear有顯著的開銷
使用LUT(look up texture)很可能是負優(yōu)化
通道圖能合并就合并,減少Shader中貼圖采樣次數(shù)
控制Framebuffer大小
避免大量的drawcall和頂點量
避免gpu上的copy-on-write
【技術美術百人計劃】延遲渲染的評論 (共 條)
