各Rendering Path技术以及其在Unity中的实现
Rendering Path其實指的就是渲染場景中光照的方式。由于場景中的光源可能很多,甚至是動態的光源。所以怎么在速度和效果上達到一個最好的結果確實很困難。以當今的顯卡發展為契機,人們才衍生出了這么多的Rendering Path來處理各種光照。
一. 正向渲染Forward Rendering
正向渲染(Forward Rendering),或稱正向著色(Forward Shading),是渲染物體的一種非常直接的方式,在場景中我們根據所有光源照亮一個物體,之后再渲染下一個物體,以此類推。要使用Forward Rendering,一般在Vertex Shader或Fragment Shader階段對每個頂點或每個像素進行光照計算,并且是對每個光源進行計算產生最終結果。
正向渲染的渲染管線流程?
下面是Forward Rendering的核心偽代碼。
For each light:For each object affected by the light:framebuffer += object * light傳統的正向渲染思路是,先進行著色,再進行深度測試。其的主要缺點就是光照計算跟場景復雜度和光源個數有很大關系。假設有n個物體,m個光源,且每個每個物體受所有光源的影響,那么復雜度就是O(m*n),所以比較適合戶外這種光源較少的場景(一般只有太陽光)。因為如果在 vertex shader 中計算光照,其復雜度將是O(num_geometry_vertexes ? num_lights),而如果在 fragment shader 中計算光照,其復雜度為O(num_geometry_fragments ? num_lights) 。可見 光源數目和復雜度是成線性增長的。
正向渲染簡單直接,也很容易實現,但是同時它對程序性能的影響也很大,因為對每一個需要渲染的物體,程序都要對每個光源下每一個需要渲染的片段進行迭代,如果舊的片段完全被一些新的片段覆蓋,最終無需顯示出來,那么其著色計算花費的時間就完全浪費掉了。
Unity中的正向渲染
在Unity中,前向渲染規則如下:一個場景中多光照情形,按光源的重要程度排序,ABCD四盞燈用效果最好的逐像素方式渲染光照,DEFG用逐頂點方式渲染光照,GH則用球諧光照方式計算光照。(關于球諧光的一些知識,可以參考我的這篇文章https://blog.csdn.net/yinfourever/article/details/90205890)
?
Unity中正向渲染分為一個Base Pass和多個Additional Passes.
Base Pass: 用逐像素的方式渲染一個directional light, 所有球諧光和逐頂點光。在這個pass里也會計算lightmap等,directional light可以計算陰影,要注意如果使用了lightmap照亮的物體不會被球諧光照亮。
Additional Passes:每個其他需要用逐像素渲染的光源將會用一個Addtional pass渲染,默認情況下這些燈光不會有陰影除非使用了?multi_compile_fwdadd_fullshadows?variant shortcut
更詳細的信息可以參考Unity官方文檔中關于Forward Rendering的介紹https://docs.unity3d.com/2019.2/Documentation/Manual/RenderTech-ForwardRendering.html
?
二. 延遲渲染 Deferred Rendering
延遲渲染( Deferred Rendering),即延遲著色(Deferred Shading),顧名思義,是將著色計算延后進行處理的一種渲染方法,在2004年的GDC上被正式提出 http://www.tenacioussoftware.com/gdc_2004_deferred_shading.ppt。
延遲著色給我們優化擁有大量光源的場景提供了很多可能性,因為它能夠在渲染擁有成百上千光源的場景的同時還能夠保持能讓人接受的幀率。下面這張圖展示了一個基于延遲著色渲染出的場景,這個場景中包含了1000個點光源,對于目前的硬件設備而言,用傳統的正向渲染來實現幾乎是不可能的。
基于Deferred Rendering 渲染的含1000個點光源的場景 [J. Andersson, SIGGRAPH 2009 Beyond Programmable shading course talk] @ Frostbite 2引擎可以將延遲渲染( Deferred Rendering)理解為先將所有物體都先繪制到屏幕空間的緩沖(即G-buffer,Geometric Buffer,幾何緩沖區)中,再逐光源對該緩沖進行著色的過程,從而避免了因計算被深度測試丟棄的?元的著色而產?的不必要的開銷。也就是說延遲渲染基本思想是,先執行深度測試,再進行著色計算,將本來在物空 間(三維空間)進行光照計算放到了像空間(二維空間)進行處理。
可以將延遲渲染理解為兩個Pass的過程:
1、幾何處理階段(Geometry Pass)。這個階段中,我們獲取對象的各種幾何信息,并將第二步所需的各種數據儲存(也就是渲染)到多個G-buffer中;
2、光照處理階段(Lighting Pass)。在這個pass中,我們只需渲染出一個屏幕大小的二維矩形,使用第一步在G-buffer中存儲的數據對此矩陣的每一個片段計算場景的光照;光照計算的過程還是和正向渲染以前一樣,只是現在我們需要從對應的G-buffer而不是頂點著色器(和一些uniform變量)那里獲取輸入變量了。
?
延遲著色中一個非常重要的概念就是G-Buffer,下面先聊一下G-Buffer。
G-Buffer,全稱Geometric Buffer ,譯作幾何緩沖區,它主要用于存儲每個像素對應的位置(Position),法線(Normal),漫反射顏色(Diffuse Color)以及其他有用材質參數。根據這些信息,就可以在像空間(二維空間)中對每個像素進行光照處理。G-Buffer根據需求可以存儲不同的內容,以下為一個典型的G-Buffer layout,對于Unity中的Deferred Rendering,G-Buffer layout就與之下的不同,關于Unity的部分之后會再細說。
一個典型的G-buffer layout。Source: W. Engel, “Light-Prepass Renderer Mark III” @SIGGRAPH 2009Talks下面是一幀中G-buffer中存儲的內容:
G-buffer存儲的信息?
延遲渲染方法一個很大的好處就是能保證在G-buffer中的片段和在屏幕上呈現的像素所包含的片段信息是一樣的,因為深度測試已經最終將這里的片段信息作為最頂層的片段。這樣保證了對于在光照處理階段中處理的每一個像素都只處理一次,所以我們能夠省下很多無用的渲染調用。除此之外,延遲渲染還允許我們做更多的優化,從而渲染更多的光源。
在幾何處理階段中填充G-buffer非常高效,因為我們直接儲存位置,顏色,法線等對象信息到幀緩沖中,這個過程幾乎不消耗處理時間。而在此基礎上使用多渲染目標(Multiple Render Targets, MRT)技術,我們可以在一個Pass之內完成所有渲染工作。
總結一下,典型的Deferred Rendering 的渲染流程有兩步:
- 1. 幾何處理階段:渲染所有的幾何/顏色數據到G-buffer
- 2. 光照處理階段:使用G-buffer計算場景的光照。
?
通用版本的延遲著色算法偽代碼:?
For each object:Render to multiple targets For each light:Apply light as a 2D postprocess 延遲渲染( Deferred Rendering)管線流程?
初步總結對比下延遲渲染和正向渲染
? ? ?正向渲染:
- 正向渲染(Forward Rendering),先執行著色計算,再執行深度測試。
- 正向渲染渲染n個物體在m個光源下的著色,復雜度為O(n*m)次。
- Forward Rendering,光源數量對計算復雜度影響巨大,所以比較適合戶外這種光源較少的場景。
? ? ?延遲渲染:
- 延遲渲染( Deferred Rendering),先執行深度測試,再執行著色計算。
- 延遲渲染渲染n個物體在m個光源下的著色,復雜度為O(n+m)次(幾何處理階段n次渲染到G-buffer,著色階段m個光源渲染m次)。
- Deferred Rendering 的最大的優勢就是將光源的數目和場景中物體的數目在復雜度層面上完全分開。也就是說場景中不管是一個三角形還是一百萬個三角形,最后的復雜度不會隨 光源數目變化而產生巨大變化。
?
總結下延遲渲染優缺點
延遲渲染的優點
Deferred Rendering 的最大的優勢就是將光源的數目和場景中物體的數目在復雜度層面上完全分開。也就是說場景中不管是一個三角形還是一百萬個三角形,最后的復雜度不會隨光源數目變化而產生巨大變化。
- 復雜度僅O(n+m)。
- 只渲染可見的像素,節省計算量。
- 用更少的shader。
- 對后處理支持良好。
- 在大量光源的場景優勢尤其明顯。
?
延遲渲染的缺點
- 內存開銷較大。
- 讀寫G-buffer的內存帶寬用量是性能瓶頸。
- 對透明物體的渲染存在問題。在這點上需要結合正向渲染進行渲染。
- 對多重采樣抗鋸齒(MultiSampling Anti-Aliasing, MSAA)的支持不友好,主要因為需開啟MRT。
- 由于Deferred Shading的Deferred階段是在完全基于G-Buffer的屏幕空間進行,這也導致了物體材質信息的缺失,這樣在處理多變的渲染風格時就需要額外的操作。
?
Unity中的延遲渲染
在Unity5之后,使用了延遲渲染(deferred rendering)代替了之前版本的延遲光照(Deferred Lighting? ?light prepass)關于延遲光照,在下一小節會簡要介紹。
在Unity中G-buffer layout:
G-Buffer pass
The g-buffer pass renders each GameObject once. Diffuse and specular colors, surface smoothness, world space normal, and emission+ambient+reflections+lightmaps are rendered into g-buffer textures. The g-buffer textures are setup as global shader properties for later access by shaders (CameraGBufferTexture0 ..CameraGBufferTexture3 names).
Lighting pass
The lighting pass computes lighting based on g-buffer and depth. Lighting is computed in screen space, so the time it takes to process is independent of Scene complexity. Lighting is added to the emission buffer.
延遲渲染只支持standard?lighting model,如果想替換自己的shader效果,可以修改lighting pass shader。
更詳細的信息可以參考Unity官方文檔??https://docs.unity3d.com/Manual/RenderTech-DeferredShading.html
三. 延遲光照 LightPre-Pass / Deferred Lighting
Light Pre-Pass即Deferred Lighting(延遲光照),旨在減少傳統Defferred Rendering使用G-buffer 時占用的過多開銷(reduce G-buffer overhead),最早由 Wolfgang Engel于2008年在他的博客(http://diaryofagraphicsprogrammer.blogspot.com/2008/03/light-pre-pass-renderer.html)中提到的。
延遲光照的具體的思路是:?
- 1、渲染場景中不透明(opaque )的幾何體。將法線向量n和鏡面擴展因子(specular spread factor)m 寫入緩沖區。這個n/m-buffer 緩沖區是一個類似 G-Buffer的緩沖區,但包含的信息更少,更輕量,適合于單個輸出顏色緩沖區,因此不需要MRT支持。
- 2、渲染光照。計算漫反射和鏡面著色方程,并將結果寫入不同的漫反射和鏡面反射累積緩沖區。這個過程可以在一個單獨的pass中完成(使用MRT),或者用兩個單獨的pass。環境光照明可以在這個階段使用一個 full-screen pass進行計算。
- 3、對場景中的不透明幾何體進行第二次渲染。從紋理中讀取漫反射和鏡面反射值,對前面步驟中漫反射和鏡面反射累積緩沖區的值進行調制,并將最終結果寫入最終的顏色緩沖區。若在上一階段沒有處理環境光照明,則在此階段應用環境光照明。
- 4、使用非延遲著色方法渲染半透明幾何體。
關于延遲著色和延遲光照,經常會被弄混,這邊簡單區分一下。
- 延遲著色(Deferred Rendering)又稱延遲著色(Deferred Shading),在2004年的GDC上被提出。
- 延遲光照(Deferred Lighting)又稱Light Pre-Pass,是延遲著色的一種改進,在2008年被提出。
Deferred Rendering與Deferred Lighting在思想上的主要異同:
- Deferred Shading需要更大的G-Buffer來完成對Deferred階段的前期準備,而且一般需要硬件有MRT的支持,可以說是硬件要求更高。
- Deferred Lighting需要兩個幾何體元的繪制過程來來完成整個渲染操作:G-Pass與Shading pass。這個既是劣勢也是優勢:由于Deferred Shading中的Deferred階段是在完全基于G-Buffer的屏幕空間進行,這也導致了物體材質信息的缺失,這樣在處理多變的渲染風格時就需要額外的操作;而Deferred Lighting卻可以在Shading階段得到物體的材質信息進而使這一問題的處理變得較簡單。
兩種方法的上述操作均是只能完成對不透明物體的渲染,而透明或半透明的物體則需額外的傳統Pass來完成。
兩者流程圖的對比:
Deferred Lighting流程圖?
Deferred Shading流程圖?
Unity中的Deferred Lighting
在Unity5版本之前,其實使用的就是Deferred Lighting技術,現在版本的Unity依然保留了該渲染模式,稱為Legacy Deferrred。
?Lighting Rendering Path. 分為Base pass,Lighting Pass,Final Pass在這里就不細分析了,主要技術原理和之前講的基本一致,? 詳細信息可以查看Unity的官方文檔
https://docs.unity3d.com/Manual/RenderTech-DeferredLighting.html
明明淺墨大神的文章說Deferred Lighting是Deferred Shading的優化,那為什么Unity要從Deferred Lighting轉換成Deferred Shading?
我個人的理解是:
- 隨著硬件的發展,帶寬越來越不是瓶頸
- Deferred Shading比Deferred Lighting少一個pass,假設場景有M個物體,N個參與計算的燈光,Deferred Lighting是 DS渲染最終執行的次數是2M+N, 而Deferred Shading執行的次數是M+N,所以效率更高,其實就是用空間換時間的概念
個人理解有可能不太正確,不負任何責任,還望大佬們指教。
?
四. 分塊延遲渲染 Tile-BasedDeferred Rendering
作為傳統Defferred Rendering的另一種主要改進,分塊延遲渲染(Tile-Based Deferred Rendering,TBDR)旨在合理分攤開銷(amortize overhead),自SIGGRAPH 2010上提出以來逐漸為業界所了解。實驗數據表明TBDR在大量光源存在的情況下明顯優于上文提到的Light Pre-Pass。
我們知道,延遲渲染的瓶頸在于讀寫 G-buffer,在大量光源下,具體瓶頸將位于每個光源對 G-buffer的讀取及與顏色緩沖區(color buffer)混合。這里的問題是,每個光源,即使它們的影響范圍在屏幕空間上有重疉,因為每個光源是在不同的繪制中進行,所以會重復讀取G-buffer中相同位置的數據,計算后以相加混合方式寫入顏色緩沖。光源越多,內存帶寬用量越大。
而分塊延遲渲染的主要思想則是把屏幕分拆成細小的柵格,例如每 32 × 32 象素作為一個分塊(tile)。然后,計算每個分塊會受到哪些光源影響,把那些光源的索引儲存在分塊的光源列表里。最后,逐個分塊進行著色,對每像素讀取 G-buffer 和光源列表及相關的光源信息。因此,G-buffer的數據只會被讀取1次且僅1次,寫入 color buffer也是1次且僅1次,大幅降低內存帶寬用量。不過,這種方法需要計算光源會影響哪些分塊,這個計算又稱為光源剔除(light culling),可以在 CPU 或 GPU(通常以 compute shader 實現)中進行。用GPU計算的好處是,GPU 計算這類工作比 CPU 更快,也減少 CPU/GPU 數據傳輸。而且,可以計算每個分塊的深度范圍(depth range),作更有效的剔除。
?
也就是說,TBDR 主要思想就是將屏幕分成一個個小塊 tile。然后根據這些 Depth 求得每個 tile 的 bounding box。對每個 tile 的 bounding box 和 light 進行求交,這樣就得到了對該 tile 有作用 的 light 的序列。最后根據得到的序列計算所在 tile 的光照效果。
對比 Deferred Rendering,之前是對每個光源求取其作用區域 light volume,然后決定其作用的的 pixel,也就是說每個光源要求取一次。而使用 TBDR,只要遍歷每個 pixel,讓其所屬 tile 與光線求交,來計算作用其上的 light,并利用 G-Buffer 進行 Shading。一方面這樣做減少 了所需考慮的光源個數,另一方面與傳統的 Deferred Rendering 相比,減少了存取的帶寬。
?
五. 分塊正向渲染 Tiled Forward Rendering
Forward+ == Forward + Light Culling[6]。Forward+很類似Tiled-based Deferred Rendering。其具體做法就是先對輸入的場景進行z-prepass,也就是說關閉寫入color,只向z-buffer寫入z值。注意此步驟是Forward+必須的,而其他渲染方式是可選的。接下來來的步驟和TBDR很類似,都是劃分tiles,并計算bounding box。只不過TBDR是在G-Buffer中完成這一步驟的,而Forward+是根據Z-Buffer。最后一步其實使用的是forward方式,即在FS階段對每個pixel根據其所在tile的light序列計算光照效果。而TBDR使用的是基于G-Buffer的deferred rendering。
實際上,forward+比deferred運行的更快。我們可以看出由于Forward+只要寫深度緩存就可以,而Deferred Render除了深度緩存,還要寫入法向緩存。而在Light Culling步驟,Forward+只需要計算出哪些light對該tile有影響即可。而Deferred Render還在這一部分把光照處理給做了。而這一部分,Forward+是放在Shading階段做的。所以Shading階段Forward+耗費更多時間。但是對目前硬件來說,Shading耗費的時間沒有那么多。
注意下這張圖是forward+與deferred lighting對比,不是shading?
六. 總結下目前Unity傳統渲染管線中支持RenderPath的情況
?
七. Unite2019中的部分相關內容
周末去上海參加了Unite大會,講道理確實干貨滿滿。其中有一場技術演講就是關于Unity最新Render path的,Unity最新的SRP渲染管線中,加入了Fine Pruned Tiled Light Lists和Clustered Shading技術,提供Tiled Forward Rendering和Tile-BasedDeferred Rendering,Fine Pruned Tiled Light Lists和Clustered Shading技術原理可以參照Gpu Pro7中的文章,我暫時也還沒有去讀,之后會抽時間去學習下。
- 《Gpu Pro7》Clustered Shading: Assigning?Lights Using Conservative?Rasterization in DirectX 12:使用Compute Shader以及DX12保守光柵化,實現優化的分簇渲染,三維空間劃分光,存儲到Light-Linklist中,降低光與物體映射的消耗。
- 《Gpu Pro7》Fine Pruned Tiled Light Lists:一種優化的Tiled Shading,與傳統的不同,通過兩個pass,首先計算light在全屏的AABB進行粗略判斷,然后逐Tiled精確判斷,可以實現不規則光源的Tiled Shading,而且利用Compute Shader生成,再利用AMD的GCN架構上,將這個計算和ShadowMap并行,降低計算時間。用于《古墓麗影·崛起》。
Unity中通過Fine Pruned Tiled Light Lists技術為deferred和forward兩種管線提供更優化的剔除功能,相關的一些知識可以參考我下邊拍攝的照片,等Unity更新版本提供這些功能后以及我個人學習更多相關知識后,我會再更新這篇文章。
?
?
?
八. 總結
總結下本文目前已經提到的Rendering Path有:
- 正向渲染 (Forward Rendering) Unity中一直有
- 延遲渲染 (Deferred Rendering) Unity5之后
- 延遲光照 (Light Pre-Pass / Deferred Lighting)Unity5之前
- 分塊延遲渲染(Tile-Based Deferred Rendering)Unity SRP渲染管線
- Forward+(即Tiled Forward Rendering,分塊正向渲染)Unity SRP渲染管線
- 群組渲染 Clustered Rendering??Unity SRP渲染管線
等有時間和精力,隨著新技術的發展會更加完善本篇文章。
本文參考以下幾篇大神的文章:
https://blog.csdn.net/poem_qianmo/article/details/77142101
https://www.cnblogs.com/polobymulberry/p/5126892.html
?
?
?
總結
以上是生活随笔為你收集整理的各Rendering Path技术以及其在Unity中的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ash, bash, ksh, csh,
- 下一篇: GPU Gems2 - 3 几何体实例化