- single-pass, multi-light
- multi-pass, multi-light
- deferred shading
Single-pass, Multi-light
for each object: render object with all lights in one shader此種作法是最單純且直接,但是此方式會連 hidden surface 也一併 shading ,造成運算上的浪費。此外,當光源數目 N 很大的時候,shader code 必須擴展進而去計算每盞光源的作用。但若是指令數超過 shader model 的限制時(視 shader model 而定),就不得不拆成多個 render pass。
Multi-pass, Multi-light
for each light: for each object affected by light: render target += BRDF(object, light)這方式是將每盞燈光的所影響的結果累加於 render target,+= 的部分是透過 ping-pong 的方式在兩個 render target 之間作替換。然而 invisible surface 所造成的計算浪費仍舊沒有獲得解決之道,新增的 geometry pass 也在 vertex transform 與 setup 的部分帶來了額外的開銷。這部份的問題,雖然可以透過粗略的 front-to-back sorting 獲得一些抒解,但 deferred shading 卻對此問題有著更根本的解決方案(當然也會帶來其他的限制)。
Deferred Shading
for each object: render lighting properties to G-buffer for each light: render target += BRDF(G-buffer, light)Deferred shading 之所以能解決 hidden surface 所造成的 shading 浪費,主要是因為它在第一階段先輸出 position, normal 與其他參數至 G-buffer。使第二階段的 lighting 運算能完全作用在 visible 的 fragment 上,同時也有助於降低 light/object 之間的相依性,藉此達到 render 效率的提升。一切看起來似乎都很美好,但故事就這樣結束了嗎?喔!不,故事才剛開始...
每個 pixel 的顏色在 lighting 的階段是由 ambient, diffuse 和 specular 這三個部分所組合而成的。由於 deferred shading 一般都把 ambient light 和 environment light 的部分拆在其他 pass 獨立運算,因此在僅考慮 local lighting model 的情況下,deferred shading 的公式主要可分為 diffuse 和 specular 兩個部分: $$ L_o(v)=\sum_{k=1}^n{(c_{diff}*f_{diff}(v,n,I_{L_k})+c_{spec}*f_{spec}(v,n,l_k,I_{L_k}))}$$ 其中 \(f_{diff}\) 和 \(f_{spec}\) 是 diffuse 和 specular 的 local reflection model(ex. lambert, phong, blinn, ... etc),\(c_{diff}, c_{spec}\) 是 surface 的 diffuse 和 specular color,\(l_k\) 與 \(I_{L_k}\) 則分別代表著第 k 盞燈光的方向與 intensity。 傳統的 deferred shading 大致將上式的運算分解成下列步驟:
- render 場景內非透明的物件,並將所需的參數存至 G-buffer (ex. position, normal, albedo, roughness, ... etc)。
- render 一個 image plane (screen space quad),在 pixel shader 中透過 texture sampler 讀取所需的參數並完成上面 shading 的計算。
- 按照 back-to-front 的順序,render 場景中透明的物件。
Light Indexed Deferred Lighting
既然,material 的複雜度會嚴重影響到 bandwidth,那何不反過來,把參數複雜度較為固定的 light 資訊存進 buffers 中呢?LIDL [Damian Trebilco] 的概念就是作了一個這樣的嘗試,不過它不是在每個 shading pixel 裡直接紀錄 light 的參數(ex. position, color, attenuation, ... etc),而是先賦予每個 light 一個獨立的 index,將其記錄在每個 shading pixel 中,然後透過這 index 去另一批參數 buffers 擷取 light 的參數。主要的步驟大致如下:- render depth。
- 將每盞燈影響的範圍(light volume) render 至 light index texture。
- 用 forward rendering 的方式 render geometry,從 light index texture 擷取參數並完成 lighting 的運算。
Pre-Lighting / Light Pre-Pass Render
為了降低傳統 deferred shading 於 bandwidth 的瓶頸,pre-lighting/light pre-pass render 的觀念也在近幾年應運而生[Wolfgang Engel] [CryEngine3]。和以往最大不同的地方是它把 lights 相關的性質(ex. light color, attneuation, N.L, ...etc),再進一步從 shading 公式中抽離出來: $$ L_o(v)=c_{diff}*\sum_{k=1} ^n{f_{diff}(v,n,I_{L_k})} + c_{spec}*\sum_{k=1}^n{f_{spec}(v,n,l_k,I_{L_k})}$$- render 非透明的物件,輸出 normal, depth 等資訊。
- render 一個 screen quad pass,將 lights 的屬性輸出至 light buffers。
- 再 render 一次場景內所有不透明的物件,搭配 light buffers 的各項資訊完成每個 material 的 shading 計算。
- render 場景內透明的物件。
參考文獻
- Deferred lighting approaches from Real-Time Rendering
- The Light Pre-Pass Renderer - Designing a Renderer for Multiple Lights, by Wolfgang Engel
- 6800 Leagues Deferred Shading
- Photo-realistic Deferred Lighting from Beyond3D
0 comments:
Post a Comment