傳統的渲染過程一般爲:1)繪製Mesh;2)指定材質;3)處理光照效果;4)輸出。傳統的過程Mesh越多,光照處理越費時,多光源時就更慢了。html
延遲渲染的步驟:1)Pass0先不作光照處理,將Mesh的Position信息和Normal信息繪製到紋理(RenderTargets,D3D支持多向輸出);2)Pass1僅繪製屏幕大小的一個四邊形,利用以前獲得的Position紋理和Normal紋理對有效地區域選擇性地進行光照處理,再輸出最後的圖像。app
分析:因爲延遲渲染將光照的處理量由空間轉換到了平面,減小了光照等效果的計算量,提升了繪製速度,對多光源的繪製優點更爲明顯。函數
渲染流程post
延遲渲染管線可分爲四個階段:geometry, lighting, composition, post-processing性能
Post-processing階段與傳統的forward shading沒有太大差異,這裏不提,只說明一下前三個階段。優化
Geometry階段:將本幀全部的幾何信息光柵化到G-buffer。包括位置,法線,貼圖等。url
Lighting階段:以G-buffer做爲輸入(位置,法線)進行逐像素的光照計算,將diffuse lighting和specularorm
Lighting 結果分別保存在兩張RT上做爲lighting buffer。htm
Composition階段:將G-buffer中的貼圖buffer和lighting buffer融合,獲得渲染結果。blog
總體渲染過程並不複雜,但在實際的過程當中仍是有許多問題須要考慮的,下面一一列舉。
G-buffer
Geometry階段將幾何信息渲染到multi render target上(MRT),當前最多支持4個MRT。而且驅動要求4個MRT必須相同的bit寬度。RT對顯存佔用過大會增長帶寬,下降cache命中。而簡單格式的RT又會影響畫質。所以決定使用32bit的RT(如A8R8G8B8,R16G16F)或64bit寬度的RT(如A16R16G16B16F)。須要在畫質和性能間作出折衷。(開發時儘量能夠方便的配置)。[1]中有一些性能比較。
MRT中必須的信息:position(depth), normal, diffuse(texture)
可能須要的信息:specular, power, emissive, ao, material id
這些信息須要在這4個RT上用合理格式,合理的組織。這裏還能夠就存儲空間和shader的複雜性作折衷。如只保存depth,而後在光照時計算position[12]。以及用球面座標保存法線[13]。以目前的資料得出的結論是應該儘量地pack數據,減小內存佔用,多出來的若干條shader指令不會明顯影響性能。
光照計算
使用延遲渲染技術最大的好處就是能夠渲染光照極爲複雜的場景。這裏場景中的光照能夠分爲兩類。
影響整個場景的scene light。如directional light。渲染一個screen quad,逐像素光照計算,沒什麼好說的。
另外一類是隻影響一部分區域的local light。如點光源,聚光燈,以及特效等等。這些local light隻影響到屏幕上的某些像素,固然不須要逐像素的進行光照計算。最簡單的方法是繪製這些光源的包圍體(點光源的包圍體是球,聚光燈的包圍體是圓錐),包圍體的大小要大於等於光源的衰減範圍。這些包圍體通過變換投影到屏幕上的對應區域,隨後在pixel shader中計算光照。
優化:
1. 光源包圍體的視錐剔除,遮擋剔除。
2. 光源包圍體投影后很小時剔除;若干個靠的比較近的小光源合併成一個較大的光源[11]
3. 光源包圍體的backface culling
4. 屏幕空間中沒有被光源照到的,或者被更近的物體遮擋住的像素不須要光照計算,所以能夠逐像素的深度剔除。
a. 使用正確的stencil light volume。相似shadow volume的方案,將渲染light volume的正反兩面,獲得正確的stencil mask,而後光照計算時使用stencil buffer。這種方法能夠獲得正確的結果,可是須要渲染每盞燈時頻繁改變render state,可能會帶來必定性能上的損失。
b 使用z test,能夠獲得「必定程度上正確」的結果。
陰影
光照計算的同時計算陰影。使用傳統的shadow map,預先生成一張陰影圖。考慮在編輯場景的時候指定那些重要的光源纔會產生陰影。在計算shadow map時要針對光源的binding volume進行剔除。
方向光和聚光燈可使用基本的shadow map投影(正交投影,透視投影)。點光源會複雜一些,須要使用cubic shadow map。(考慮unwrapping method[14])
半透明
因爲在延遲渲染的過程當中只計算離屏幕距離最近的那個像素的光照,所以沒法處理半透明物體的光照。
方案1
延遲渲染的過程當中只處理不透明的物體,將全部半透明的物體放在渲染過程的最後,使用傳統的forward shading渲染。
方案2
在Geometry階段將半透明的物體和背景逐像素的交織起來,將透明度放在一個單獨的通道中。按通常的方法計算光照。隨後在composition階段再根據透明度將透明物體和背景逐像素的混合起來。
優勢:
光照一致性。半透明的物體也參加延遲渲染,能夠接受多光源的光照。
簡單而且健壯。不須要單獨區分不透明物體和半透明物體,不須要單獨的半透明渲染管道。
速度快。只增長了7到10條ps指令,兩張貼圖,只有約2%的性能損失。
缺點:
模糊。在半透明的物體上會有一點模糊,緣由是在交織的過程當中會有必定信息損失。
邊緣鋸齒。反交織的過程當中半透明物體的邊緣會產生一些鋸齒。
只能有一層半透明。
多種材質
在延遲光照的過程當中支持多種材質須要以下方案:
在G-buffer階段輸出材質的ID到G-buffer的一個通道中,隨後在lighting階段和composition階段根據材質ID使用不一樣的光照函數計算光照。這種方案在sm 3.0中使用動態分支的前提下能夠很好的工做。
反鋸齒
Dx9 API不支持反鋸齒的MRT,Dx10支持。
一種方案是使用超採樣,先渲染到大的RT上,再downsample到正常的大小,獲得沒有鋸齒的結果。延遲渲染的效率跟分辨率有很大關係,所以這種方法會極大的下降性能,基本不可取。
另外一種方案是使用「intelligent blur」,只模糊物體邊緣的像素:
根據相鄰像素的深度和法線提取物體邊界,而後對提取出的邊界進行模糊。模糊時要避免不正確的泄露。如後面物體的顏色泄露到前面的物體上[11]。 整體而言實現會較爲複雜。
另外一種方案:pre-lighting [8][9][17]
一種pre-z rendering 和deferred rendering的結合。G-buffer階段只保存depth和normal,而後計算光照信息到lighting buffer,格式以下
LightColor.r * N.L * Att
LightColor.g * N.L * Att
LightColor.b * N.L * Att
R.V^n * N.L * Att
最後使用傳統的forward shading再將整個場景渲染一遍,期間查詢lighting buffer。
與普通的deferred shading相比:
優勢:
佔用帶寬小,第一遍渲染只輸出normal,depth是自動得到的。
能夠用在較老的硬件平臺上,不須要MRT支持。
對現有forward shading管道改動較小,比較容易實現。
缺點:
整個場景須要渲染兩遍,至關於在pre-z和forward shading中間加了一個lighting stage。