1. rendering path 的技術(shù)基礎(chǔ)
在介紹各種光照渲染方式之前,首先必須介紹一下現(xiàn)代的圖形渲染管線。這是下面提到的幾種 Rendering Path 的技術(shù)基礎(chǔ)。
目前主流的游戲和圖形渲染引擎,包括底層的 API(如 DirectX 和 OpenGL)都開始支持現(xiàn)代的圖形渲染管線?,F(xiàn)代的渲染管線也稱為可編程管線(Programmable Pipeline),簡單點(diǎn)說就是將以前固定管線寫死的部分(比如頂點(diǎn)的處理,像素顏色的處理等等)變成在 GPU上可以進(jìn)行用戶自定義編程的部分,好處就是用戶可以自由發(fā)揮的空間增大,缺點(diǎn)就是必須用戶自己實(shí)現(xiàn)很多功能。
下面簡單介紹下可編程管線的流程。以 OpenGL 繪制一個三角形舉例。首先用戶指定三個頂點(diǎn)傳給 Vertex Shader。然后用戶可以選擇是否進(jìn)行 Tessellation Shader(曲面細(xì)分可能會用到)和 Geometry Shader(可以在 GPU 上增刪幾何信息)。緊接著進(jìn)行光柵化,再將光柵化后的結(jié)果傳給 Fragment Shader 進(jìn)行 pixel 級別的處理。最后將處理的像素傳給FrameBuffer 并顯示到屏幕上。
2. 幾種常用的 Rendering Path
Rendering Path 其實(shí)指的就是渲染場景中光照的方式。由于場景中的光源可能很多,甚至是動態(tài)的光源。所以怎么在速度和效果上達(dá)到一個最好的結(jié)果確實(shí)很困難。以當(dāng)今的顯卡發(fā)展為契機(jī),人們才衍生出了這么多的 Rendering Path 來處理各種光照。
2.1 Forward Rendering
Forward Rendering 是絕大數(shù)引擎都含有的一種渲染方式。要使用 Forward Rendering,一般在 Vertex Shader 或 Fragment Shader 階段對每個頂點(diǎn)或每個像素進(jìn)行光照計算,并且是對每個光源進(jìn)行計算產(chǎn)生最終結(jié)果。下面是 Forward Rendering 的核心偽代碼。
比如在 Unity3D 4.x 引擎中,對于下圖中的圓圈(表示一個 Geometry),進(jìn)行 Forward Rendering 處理。
將得到下面的處理結(jié)果
也就是說,對于 ABCD 四個光源我們在 Fragment Shader 中我們對每個 pixel 處理光照,對于 DEFG 光源我們在 Vertex Shader 中對每個 vertex 處理光照,而對于 GH 光源,我們采用球調(diào)和(SH)函數(shù)進(jìn)行處理。
Forward Rendering 優(yōu)缺點(diǎn)
很明顯,對于 Forward Rendering,光源數(shù)量對計算復(fù)雜度影響巨大,所以比較適合戶外這種光源較少的場景(一般只有太陽光)。
但是對于多光源,我們使用 Forward Rendering 的效率會極其低下。因?yàn)槿绻?vertex shader 中計算光照,其復(fù)雜度將是O(num_geometry_vertexes ? num_lights),而如果在fragment shader 中計算光照,其復(fù)雜度為O(num_geometry_fragments ? num_lights) ??梢姽庠磾?shù)目和復(fù)雜度是成線性增長的。
對此,我們需要進(jìn)行必要的優(yōu)化。比如
1、多在 vertex shader 中進(jìn)行光照處理,因?yàn)橛幸粋€幾何體有 10000 個頂點(diǎn),那么對于 n 個光源,至少要在 vertex shader 中計算 10000n 次。而對于在 fragment shader 中進(jìn)行處理,這種消耗會更多,因?yàn)閷τ谝粋€普通的 1024x768 屏幕,將近有 8 百萬的像素要處理。所以如果頂點(diǎn)數(shù)小于像素個數(shù)的話,盡量在 vertex shader 中進(jìn)行光照。
2、如果要在 fragment shader 中處理光照,我們大可不必對每個光源進(jìn)行計算時,把所有像素都對該光源進(jìn)行處理一次。因?yàn)槊總€光源都有其自己的作用區(qū)域。比如點(diǎn)光源的作用區(qū)域是一個球體,而平行光的作用區(qū)域就是整個空間了。對于不在此光照作用區(qū)域的像素就不進(jìn)行處理。但是這樣做的話,CPU 端的負(fù)擔(dān)將加重。
3、對于某個幾何體,光源對其作用的程度是不同,所以有些作用程度特別小的光源可以不進(jìn)行考慮。典型的例子就是 Unity 中只考慮重要程度最大的 4 個光源。
2.2 Deferred Rendering
Deferred Rendering(延遲渲染)顧名思義,就是將光照處理這一步驟延遲一段時間再處理。具體做法就是將光照放在已經(jīng)將三維物體生成二維圖片之后進(jìn)行處理。也就是說將物空間的光照處理放到了像空間進(jìn)行處理。要做到這一步,需要一個重要的輔助工具——G-Buffer。G-Buffer 主要是用來存儲每個像素對應(yīng)的 Position,Normal,Diffuse Color 和其他 Material parameters。根據(jù)這些信息,我們就可以在像空間中對每個像素進(jìn)行光照處理[3]。下面是Deferred Rendering 的核心偽代碼。
下面簡單舉個例子。
首先我們用存儲各種信息的紋理圖。比如下面這張 Depth Buffer,主要是用來確定該像素距離視點(diǎn)的遠(yuǎn)近的。
根據(jù)反射光的密度/強(qiáng)度分度圖來計算反射效果。
下圖表示法向數(shù)據(jù),這個很關(guān)鍵。進(jìn)行光照計算最重要的一組數(shù)據(jù)。
下圖使用了 Diffuse Color Buffer。
這是使用 Deferred Rendering 最終的結(jié)果。
Deferred Rendering 的最大的優(yōu)勢就是將光源的數(shù)目和場景中物體的數(shù)目在復(fù)雜度層面上完全分開。也就是說場景中不管是一個三角形還是一百萬個三角形,最后的復(fù)雜度不會隨光源數(shù)目變化而產(chǎn)生巨大變化。從上面的偽代碼可以看出 deferred rendering 的復(fù)雜度為O(screen_resolution + num_lights)。
但是 Deferred Rendering 局限性也是顯而易見。比如我在 G-Buffer 存儲以下數(shù)據(jù)
這樣的話,對于一個普通的 1024x768 的屏幕分辨率??偣驳檬褂?1024x768x128bit=20MB,對于目前的動則上 GB 的顯卡內(nèi)存,可能不算什么。但是使用 G-Buffer 耗費(fèi)的顯存還是很多的。一方面,對于低端顯卡,這么大的顯卡內(nèi)存確實(shí)很耗費(fèi)資源。另一方面,如果要渲染更酷的特效,使用的 G-Buffer 大小將增加,并且其增加的幅度也是很可觀的。順帶說一句,存取 G-Buffer 耗費(fèi)的帶寬也是一個不可忽視的缺陷。
對于 Deferred Rendering 的優(yōu)化也是一個很有挑戰(zhàn)的問題。下面簡單介紹幾種降低Deferred Rendering 存取帶寬的方式。最簡單也是最容易想到的就是將存取的 G-Buffer 數(shù)據(jù)結(jié)構(gòu)最小化,這也就衍生除了 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 以及對應(yīng)位置的材質(zhì)索引值。
(2)在 FS 階段利用上面的 G-Buffer 計算出所必須的 light properties,比如 Normal*LightDir,LightColor,Specular 等 light properties。將這些計算出的光照進(jìn)行 alpha-blend 并存入 LightBuffer(就是用來存儲 light properties 的 buffer)。
(3)最后將結(jié)果送到 forward rendering 渲染方式計算最后的光照效果。
相對于傳統(tǒng)的 Deferred Render,使用 Light Pre-Pass 可以對每個不同的幾何體使用不同的 shader 進(jìn)行渲染,所以每個物體的 material properties 將有更多變化。這里我們可以看出對于傳統(tǒng)的 Deferred Render,它的第二步(見偽代碼)是遍歷每個光源,這樣就增加了光源設(shè)置的靈活性,而 Light Pre-Pass 第三步使用的其實(shí)是 forward rendering,所以可以對每個mesh 設(shè)置其材質(zhì),這兩者是相輔相成的,有利有弊。另一個 Light Pre-Pass 的優(yōu)點(diǎn)是在使用MSAA 上很有利。雖然并不是 100%使用上了 MSAA(除非使用 DX10/11 的特性),但是由于使用了 Z 值和 Normal 值,就可以很容易找到邊緣,并進(jìn)行采樣。
下面這兩張圖,左邊是使用傳統(tǒng) Deferred Render 繪制的,走遍是使用 Light Pre-Pass 繪制的。這兩張圖在效果上不應(yīng)該有太大區(qū)別。
2.2.2 Tile-Based Deferred Rendering
TBDR 主要思想就是將屏幕分成一個個小塊 tile。然后根據(jù)這些 Depth 求得每個 tile 的bounding box。對每個 tile 的 bounding box 和 light 進(jìn)行求交,這樣就得到了對該 tile 有作用的 light 的序列。最后根據(jù)得到的序列計算所在 tile 的光照效果。
對比 Deferred Render,之前是對每個光源求取其作用區(qū)域 light volume,然后決定其作用的的 pixel,也就是說每個光源要求取一次。而使用 TBDR,只要遍歷每個 pixel,讓其所屬tile 與光線求交,來計算作用其上的 light,并利用 G-Buffer 進(jìn)行 Shading。一方面這樣做減少了所需考慮的光源個數(shù),另一方面與傳統(tǒng)的 Deferred Rendering 相比,減少了存取的帶寬。
2.3 Forward+
Forward+ == Forward + Light Culling[6]。Forward+很類似 Tiled-based Deferred Rendering。其具體做法就是先對輸入的場景進(jìn)行 z-prepass,也就是說關(guān)閉寫入 color,只向 z-buffer 寫入 z 值。注意此步驟是 Forward+必須的,而其他渲染方式是可選的。接下來來的步驟和 TBDR 很類似,都是劃分 tiles,并計算 bounding box。只不過 TBDR 是在 G-Buffer 中完成這一步驟的,而 Forward+是根據(jù) Z-Buffer。最后一步其實(shí)使用的是 forward 方式,即在 FS 階段對每個pixel 根據(jù)其所在 tile 的 light 序列計算光照效果。而 TBDR 使用的是基于 G-Buffer 的 deferred rendering。
實(shí)際上,forward+比 deferred 運(yùn)行的更快。我們可以看出由于 Forward+只要寫深度緩存就可以,而 Deferred Render 除了深度緩存,還要寫入法向緩存。而在 Light Culling 步驟,F(xiàn)orward+只需要計算出哪些 light 對該 tile 有影響即可。而 Deferred Render 還在這一部分把光照處理給做了。而這一部分,F(xiàn)orward+是放在 Shading 階段做的。所以 Shading階段 Forward+耗費(fèi)更多時間。但是對目前硬件來說,Shading 耗費(fèi)的時間沒有那么多。
Forward+的優(yōu)勢還有很多,其實(shí)大多就是傳統(tǒng) Forward Rendering 本身的優(yōu)勢,所以Forward+更像一個集各種 Rendering Path 優(yōu)勢于一體的 Rendering Path。
3. 總結(jié)
首先我們列出 Rendering Equation,然后對比 Forward Rendering,Deferred Rendering 和Forward+ Rendering。
3.1 Rendering Equation
其中點(diǎn) x 處有一入射光,其光強(qiáng)為Le,入射角度為 wi。根據(jù)函數(shù) f 和 Le來計算出射角為 wo)。注意此處的 n 為場景中總共有 n 個光源。
3.2 Forward Renderng
由于 Forward 本身對多光源支持力度不高,所以此處對于每個點(diǎn) x 的處理不再考慮所有的 n 個光源,僅僅考慮少量的或者說經(jīng)過挑選的 m 個光源??梢钥闯鲞@樣的光照效果并不完美。另外,每個光線的V'(wo)是計算不了的。
3.3 Deferred Rendering
由于 Deferred Rendering 使用了 light culling,所以不用遍歷場景中的所有光源,只需遍歷經(jīng)過 light culling 后的 n 個光源即可。并且 Deferred Rendering 將計算 BxDF 的部分單獨(dú)分出來了。
3.4 Forward+ Rendering
可以看出 Forward+和 Forward 最大區(qū)別就是光源的挑選上有了很到改進(jìn)。
-
光源
+關(guān)注
關(guān)注
3文章
687瀏覽量
67683 -
光照
+關(guān)注
關(guān)注
0文章
53瀏覽量
11018
原文標(biāo)題:實(shí)時渲染中常用的幾種 Rendering Path
文章出處:【微信號:Imgtec,微信公眾號:Imagination Tech】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論