Deferred Shading是如今比較流行實時渲染方式,這種渲染方式能把Geometry和Lighting之間的耦合解開,把Forward Shading的Geometry Pass*Lighting Pass複雜度降低爲Geometry Pass+Lighting Pass,特別適合於渲染較多動態光源的場景,本文將快速瀏覽實現Deferred Shading的各個階段,並提供一個帶源代碼的簡單的例子程序,這個程序能夠在SM2.0及以上的硬件上運行,經過dx9接口實現。html
Deferred Shading介紹可參閱《RealTime Rendering》3rd 7.9.二、《GPU Gems2》及《GPU Gems3》。另外,《Deferred Shading Tutorial》提供了詳細的OpenGL實現流程。而網上可找到示例代碼有nVidia SDK 9.52以及Intel的《Deferred Rendering for Current and Future Rendering Pipelines》,網址是http://software.intel.com/en-us/articles/deferred-rendering-for-current-and-future-rendering-pipelines/。框架
Deferred Shading可分爲四個階段:Geometry、Lighting、Post-Processing和MergeOutput,其中第三階段可選。各個階段分別輸出到texture,因此,deferred shading將使用到Render To Texture(RTT)及Multiple Render Targets(MRT)。每一個階段及其對應的輸出以下表:spa
階段3d |
輸出orm |
做用htm |
Geometryblog |
G-Buffer接口 |
記錄整個場景的幾何信息例如normal、depth(position)、diffuse color、specular intensity等ip |
Lightingci |
P-Buffer1 |
使用G-Buffer信息逐像素計算光照 |
Post-Processing |
P-Buffer2 |
後處理,例如motion blur、Bloom、Anti-Aliasing等 |
MergeOutput |
BackBuffer |
混合以前全部Buffer的數據,輸出到BackBuffer |
此階段是把場景內全部3D模型的幾何信息都渲染(記錄)到G-Buffer內,G-Buffer的分辨率是屏幕分辨率,以便後續階段進行逐像素渲染。G-Buffer能夠有多個texture,一般,使用MRT在一個Batch內完成這些屬性的渲染。此階段,場景的幾何信息都以texture coordinate的方式插值並投影到G-Buffer上,因此,須要設置好各個space的轉換矩陣。示例程序在此階段輸出normal、depth、diffuse color及specular intensity到G-Buffer。示例程序在view space計算光照,因此這裏輸出的normal是轉換到view space的值。這裏輸出的depth是已轉換到normalized device space,在計算光照時,depth配合project matrix能夠恢復出view space下的座標值。G-Buffer輸出以下圖:
由上往下分別是normal(view space)、depth、diffuse color、specular intensity。
此階段使用光照模型、光源位置結合G-Buffer的幾何信息計算G-Buffer上每一個像素的顏色,若是有多個光源,每一個光源執行此階段一次,並把計算結果累積到P-Buffer上。再次提示,示例程序是在view space上計算光照,因此G-Buffer上的depth須要恢復爲view space的position。要理解恢復view space position的過程,先來認識一些概念:
G-Buffer上的depth是normalized device space,而view space轉換到normalized device space要經過view-->homogeneous-->normalized device,其中,view-->homogeneous經過projection matrix完成;而homogeneous->normalized device則是把4d vector都除以w,而w是view space下的z。projection matrix以下(請注意D3D使用row-major matrix而且使用pre-multipling) ,所以,咱們獲得homogeneous下的z是
,除以view space的z就是
等於normalized device下的depth。表達是內的z均爲view space下的z,f是far plane,n是near plane。f和n是咱們定義project matrix時指定而且表達式的值咱們知道,因此經過上述表達式,能夠求出view space下的z的值。normalized device space下的xy咱們也知道,分別是texture coordinate的u*2-1及-(v*2-1),這是由於,咱們要把G-Buffer點對點地渲染到P-Buffer上, texture coordinate是[0,1]要轉換到[-1,1]normalized device space的xy區間。想詳細瞭解各個空間轉換及轉換矩陣的推導,可參閱《RealTime Rendering》3rd及《Introduction to 3D Game Programming with DirectX 9.0c—A Shader Approach》。
當咱們獲得了normalized device space下的xyz以及view space下的z後,有兩種方法能夠回到view space,第一種方法,normalized device space的xyzw(w=1)分別乘以view space的z,回到homogeneous clip space,而後經過projection matrix的inverse matrix(projection matrix並無真正把點投影到平面上,只是轉到homogeneous space,因此這個matrix是invertable的)回到view space;第二種方法,使用projection matrix的(0,0)及(1,1)元素計算出view space的xy值,其中R是aspect ration,a是fovy。示例程序使用第二種方法。
獲得每一個像素的view space 座標,就能夠作逐像素光照,獲得P-Buffer1,以下圖:
示例程序進行了AA及Bloom處理。AA處理使用G-Buffer的normal做爲依據,檢測三角形邊界並決定3x3臨近像素的混合權重,混合輸出中心像素。更有效的Post-Processing AA可參考MLAA及SRAA(後續文章中介紹)。Bloom就是對P-Buffer1進行縱向和橫向模糊。下面是Post-Processing的輸出:
這個步驟很簡單,對Post-Processing的輸出進行混合並渲染到Backbuffer上就ok了,下圖就是完整的渲染效果:
最後須要說明的是,示例程序使用《Introduction to 3D Game Programming with DirectX 9.0c—A Shader Approach》的框架代碼及紋理。
示例程序源代碼下載:http://files.cnblogs.com/rickerliang/AmbientDiffuseSpecularDemo-DeferredShading.zip
但願本文對想了解Deferred Shading的朋友有幫助。