Unity3D 渲染路徑

Unity渲染路徑: 

Unity支持不同的渲染路徑。您應具體取決於你的遊戲內容和目標平臺/硬件來選擇使用哪一個。不同的渲染路徑有不同的特點和性能特點,主要影響燈光和陰影

如果圖形卡不能處理選定的渲染路徑,Unity將自動使用一個較低保真度的設置。因此,在GPU上不能處理延遲照明(Deferred Lighting),將使用正向渲染(Forward Rendering )。如果不支持正向渲染(Forward Rendering ),將使用頂點光照(Vertex Lit)。

Deferred Lighting延遲光照

延時光照是有着最高保真度的光照和陰影的渲染路徑。如果你有很多實時燈光,最好是使用延時光照。它需要一定水平的硬件支持,僅在 Unity Pro可用,移動設備上不支持

Forward Rendering 正向渲染

正向渲染一個基於着色器的渲染路徑。它支持逐像素計算光照(包括法線貼圖和燈光Cookies)和來自一個平行光的實時陰影。在默認設置中,少數最亮的燈光在逐像素計算光照模式下渲染。其餘的燈光計算對象頂點的光照。

Vertex Lit頂點光照

頂點光照(Vertex Lit) 是最低保真度的光照、不支持實時陰影的渲染路徑。最好是用於舊機器或受限制的移動平臺上

頂點照明渲染路徑通常在一個通道中渲染物體,所有光源的照明都是在物體的頂點上進行計算的

頂點照明渲染路徑是最快的渲染路徑並且有最廣泛的硬件支持(然而,請記住:它無法工作在遊戲機上[看渲染路徑比較圖])。

由於所有的光照都是在頂點層級上計算的,此渲染路徑不支持大部分的逐像素渲染效果:如,陰影法線貼圖燈光遮罩高精度的高光

其他參考:http://blog.sina.com.cn/s/blog_471132920101dfvh.html


1. rendering path的技術基礎


在介紹各種光照渲染方式之前,首先必須介紹一下現代的圖形渲染管線。這是下面提到的幾種Rendering Path的技術基礎。


目前主流的遊戲和圖形渲染引擎,包括底層的API(如DirectX和OpenGL)都開始支持現代的圖形渲染管線。現代的渲染管線也稱爲可編程管線(Programmable Pipeline),簡單點說就是將以前固定管線寫死的部分(比如頂點的處理,像素顏色的處理等等)變成在GPU上可以進行用戶自定義編程的部分,好處就是用戶可以自由發揮的空間增大,缺點就是必須用戶自己實現很多功能。

下面簡單介紹下可編程管線的流程。以OpenGL繪製一個三角形舉例。首先用戶指定三個頂點傳給Vertex Shader。然後用戶可以選擇是否進行Tessellation Shader(曲面細分可能會用到)和Geometry Shader(可以在GPU上增刪幾何信息)。緊接着進行光柵化,再將光柵化後的結果傳給Fragment Shader進行pixel級別的處理。最後將處理的像素傳給FrameBuffer並顯示到屏幕上。

2. 幾種常用的Rendering Path


Rendering Path其實指的就是渲染場景中光照的方式。由於場景中的光源可能很多,甚至是動態的光源。所以怎麼在速度和效果上達到一個最好的結果確實很困難。以當今的顯卡發展爲契機,人們才衍生出了這麼多的Rendering Path來處理各種光照。

2.1 Forward Rendering


Forward Rendering是絕大數引擎都含有的一種渲染方式。要使用Forward Rendering,一般在Vertex Shader或Fragment Shader階段對每個頂點或每個像素進行光照計算,並且是對每個光源進行計算產生最終結果。下面是Forward Rendering的核心僞代碼[1]。

For each light:
    For each object affected by the light:
        framebuffer += object * light

比如在Unity3D 4.x引擎中,對於下圖中的圓圈(表示一個Geometry),進行Forward Rendering處理。


將得到下面的處理結果


也就是說,對於ABCD四個光源我們在Fragment Shader中我們對每個pixel處理光照,對於DEFG光源我們在Vertex Shader中對每個vertex處理光照,而對於GH光源,我們採用球調和(SH)函數進行處理。  什麼情況?

Forward Rendering優缺點

很明顯,對於Forward Rendering,光源數量對計算複雜度影響巨大,所以比較適合戶外這種光源較少的場景(一般只有太陽光)。

但是對於多光源,我們使用Forward Rendering的效率會極其低下。因爲如果在vertex shader中計算光照,其複雜度將是 ,而如果在fragment shader中計算光照,其複雜度爲 。可見光源數目和複雜度是成線性增長的。

對此,我們需要進行必要的優化。比如

  • 1.多在vertex shader中進行光照處理,因爲有一個幾何體有10000個頂點,那麼對於n個光源,至少要在vertex shader中計算10000n次。而對於在fragment shader中進行處理,這種消耗會更多,因爲對於一個普通的1024x768屏幕,將近有8百萬的像素要處理。所以如果頂點數小於像素個數的話,儘量在vertex shader中進行光照。
  • 2.如果要在fragment shader中處理光照,我們大可不必對每個光源進行計算時,把所有像素都對該光源進行處理一次。因爲每個光源都有其自己的作用區域。比如點光源的作用區域是一個球體,而平行光的作用區域就是整個空間了。對於不在此光照作用區域的像素就不進行處理。但是這樣做的話,CPU端的負擔將加重,因爲要計算作用區域。 怎麼計算
  • 3.對於某個幾何體,光源對其作用的程度是不同,所以有些作用程度特別小的光源可以不進行考慮。典型的例子就是Unity中只考慮重要程度最大的4個光源。

2.2 Deferred Rendering

注意下具體渲染的3個步驟http://blog.csdn.net/heyuchang666/article/details/51564954 


Deferred Rendering(延遲渲染)顧名思義,就是將光照處理這一步驟延遲一段時間再處理。具體做法就是將光照處理這一步放在已經三維物體生成二維圖片之後進行處理。也就是說將物空間的光照處理放到了像空間進行處理。要做到這一步,需要一個重要的輔助工具——G-Buffer。G-Buffer主要是用來存儲每個像素對應的Position,Normal,Diffuse Color和其他Material parameters。根據這些信息,我們就可以在像空間中對每個像素進行光照處理[3]。下面是Deferred Rendering的核心僞代碼。

For each object: 
    Render to multiple targets 
For each light: 
    Apply light as a 2D postprocess

下面簡單舉個例子[1]。

首先我們用存儲各種信息的紋理圖。比如下面這張Depth Buffer,主要是用來確定該像素距離視點的遠近的。


圖. Depth Buffer

根據反射光的密度/強度分度圖來計算反射效果。


圖.Specular Intensity/Power

下圖表示法向數據,這個很關鍵。進行光照計算最重要的一組數據。


圖.Normal Buffer

下圖使用了Diffuse Color Buffer。


圖.Diffuse Color Buffer

這是使用Deferred Rendering最終的結果。


圖.Deferred Lighting Results

Deferred Rendering的最大的優勢就是將光源的數目和場景中物體的數目在複雜度層面上完全分開。也就是說場景中不管是一個三角形還是一百萬個三角形,最後的複雜度不會隨光源數目變化而產生巨大變化。從上面的僞代碼可以看出deferred rendering的複雜度爲 。

但是Deferred Rendering侷限性也是顯而易見。比如我在G-Buffer存儲以下數據

Depth

R32F

Normal + scattering

A2R10G10B10

Diffuse color + emissive

A8R8G8B8

Other material parameters

A8R8G8B8

這樣的話,對於一個普通的1024x768的屏幕分辨率。總共得使用1024x768x128bit=20MB,對於目前的動則上GB的顯卡內存,可能不算什麼。但是使用G-Buffer耗費的顯存還是很多的。一方面,對於低端顯卡,這麼大的顯卡內存確實很耗費資源。另一方面,如果要渲染更酷的特效,使用的G-Buffer大小將增加,並且其增加的幅度也是很可觀的。順帶說一句,存取G-Buffer耗費的帶寬也是一個不可忽視的缺陷。

對於Deferred Rendering的優化也是一個很有挑戰的問題。下面簡單介紹幾種降低Deferred Rendering存取帶寬的方式。最簡單也是最容易想到的就是將存取的G-Buffer數據結構最小化,這也就衍生出了light pre-pass方法。另一種方式是將多個光照組成一組,然後一起處理,這種方法衍生了Tile-based deferred Rendering。

2.2.1 Light Pre-Pass

Light Pre-Pass最早是由Wolfgang Engel在他的博客[2]中提到的。具體的做法是

  • (1)只在G-Buffer中存儲Z值和Normal值。對比Deferred Render,少了Diffuse Color, Specular Color以及對應位置的材質索引值。
  • (2)在FS階段利用上面的G-Buffer計算出所必須的light properties,比如Normal*LightDir,LightColor,Specular等light properties。將這些計算出的光照進行alpha-blend並存入LightBuffer(就是用來存儲light properties的buffer)。
  • (3)最後將結果送到forward rendering渲染方式計算最後的光照效果。

相對於傳統的Deferred Render,使用Light Pre-Pass可以對每個不同的幾何體使用不同的shader進行渲染,所以每個物體的material properties將有更多變化。這裏我們可以看出相對於傳統的Deferred Render,它的第二步(見僞代碼)是遍歷每個光源,這樣就增加了光源設置的靈活性,而Light Pre-Pass第三步使用的其實是forward rendering,所以可以對每個mesh設置其材質,這兩者是相輔相成的,有利有弊。另一個Light Pre-Pass的優點是在使用MSAA上很有利。雖然並不是100%使用上了MSAA(除非使用DX10/11的特性),但是由於使用了Z值和Normal值,就可以很容易找到邊緣,並進行採樣。

下面這兩張圖,左邊是使用傳統Deferred Render繪製的,右邊是使用Light Pre-Pass繪製的。這兩張圖在效果上不應該有太大區別。

imageimage

2.2.2 Tile-Based Deferred Rendering

TBDR主要思想就是將屏幕分成一個個小塊tile。然後根據這些Depth求得每個tile的bounding box。對每個tile的bounding box和light進行求交,這樣就得到了對該tile有作用的light的序列。最後根據得到的序列計算所在tile的光照效果。[4][5]

對比Deferred Render,之前是對每個光源求取其作用區域light volume,然後決定其作用的的pixel,也就是說每個光源要求取一次。而使用TBDR,只要遍歷每個pixel,讓其所屬tile與光線求交,來計算作用其上的light,並利用G-Buffer進行Shading。一方面這樣做減少了所需考慮的光源個數,另一方面與傳統的Deferred Rendering相比,減少了存取的帶寬。

2.3 Forward+

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+的優勢還有很多,其實大多就是傳統Forward Rendering本身的優勢,所以Forward+更像一個集各種Rendering Path優勢於一體的Rendering Path。

3. 總結


首先我們列出Rendering Equation,然後對比Forward Rendering,Deferred Rendering和Forward+ Rendering[6]。

3.1 Rendering Equation

其中點 處有一入射光,其光強爲 ,入射角度爲 。根據函數 和 來計算出射角爲 處的出射光強度。最後在輔以出射光的相對於視點可見性 。注意此處的 爲場景中總共有 個光源。


3.2 Forward Renderng

由於Forward本身對多光源支持力度不高,所以此處對於每個點 的處理不再考慮所有的 個光源,僅僅考慮少量的或者說經過挑選的 個光源。可以看出這樣的光照效果並不完美。另外,每個光線的 是計算不了的。


3.3 Deferred Rendering

由於Deferred Rendering使用了light culling,所以不用遍歷場景中的所有光源,只需遍歷經過light culling後的 個光源即可。並且Deferred Rendering將計算BxDF的部分單獨分出來了。


3.4 Forward+ Rendering

可以看出Forward+和Forward最大區別就是光源的挑選上有了很到改進。


參考文獻

[1] Shawn Hargreaves. (2004) “Deferred Shading”. [Online] Available:

http://hall.org.ua/halls/wizzard/books/articles-cg/DeferredShading.pdf (April 15,2015)

[2] Wolfgang Engel. (March 16, 2008) “Light Pre-Pass Renderer”. [Online] Available:

http://diaryofagraphicsprogrammer.blogspot.com/2008/03/light-pre-pass-renderer.html(April 14,2015)

[3] Klint J. Deferred Rendering in Leadwerks Engine[J]. Copyright Leadwerks Corporation, 2008.

[4] 龔敏敏.(April 22, 2012) “Forward框架的逆襲:解析Forward+渲染”. [Online] Available:

http://www.cnblogs.com/gongminmin/archive/2012/04/22/2464982.html(April 13,2015)

[5] Lauritzen A. Deferred rendering for current and future rendering pipelines[J]. SIGGRAPH Course: Beyond Programmable Shading, 2010: 1-34.

[6] Harada T, McKee J, Yang J C. Forward+: Bringing deferred lighting to the next level[J]. 2012.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章