2020年5月18日 星期一

Image Based Lighting (2)


  •  前言
在上一篇,我將 rendering equatiom 分成了 Diffuse 和 Specular 兩個部分,



並 pre-compute(預先計算) Diffuse 部分,將結果儲存在一個 cubemap 內。

本篇會承續上一篇未處理的 Specular 部分,討論如何實作 pre-compute,

以減少 real-time 渲染的負擔。


  • Specular BRDF

首先將 Specular 的部分使用在 淺談 physically based rendering (4) 

提到的 Cook-Torrance Model 展開,


其中:

ks 是物體的反射率(註1)

在這裡,可以觀察到反射的結果會受到兩個變數的影響,

 ωi (入射光方向) 和 ωo (觀察者方向)。

對這兩種的所有組合做積分是不實際的。

因此,勢必要想辦法減少變數的數量,才能達到實用的階段。

  • split sum approximation
首先,使用 Monte Carlo method (蒙地卡羅積分法) 對 Specular BRDF 做積分,



其中

p : 是 probability density function (簡稱 pdf)

lk : 使用 importance sampling (重點採樣) 計算出來的反射光方向樣本。

根據 [2]、[3] 得知,


pdf 代入積分式,得到



如此,原來的積分式就被分成了兩個部分


前面的部分為 Integral of BRDF (簡稱 DFG 項)

後面的部分為 Integral of Lighting (簡稱 LD 項)

這種將積分式拆開後分別處理,

最後再將結果一起計算的方法稱之為 split sum approximation [2]。



  • Integral of BRDF

觀察 DFG 項,可以發現變數的數量還是很多,

因此需將算式整理,看看是否能把變數數量壓到最小。

首先為了簡化算式,根據 淺談 physically based rendering (4) 提到的效能優化技巧,

將 G 項和分母結合使其成為 V 項。



接下來展開 F 項,

在 淺談 physically based rendering (4) 我是使用 Fresnel-Schlick approximation 實作F項。



將 F 項代入積分式,可以得到


由於同一個渲染物體  F0 是相同的,所以可視為一個常數。

到了這裡,已經將變數數量限制到一個可接受的範圍內,

half-vector 和 light-vector 可以用 importance sample 計算出來,

因此變數只剩下 roughness 和 view-vector 而已。

為了方便將 pre-compute 結果作為一個 lookup table 儲存在一張 texture 上,

需要將變數範圍限制在 [0,1] 之間,

所以,在 pre-compute 時,將 roughness 和 NdotV (normal 和 view-vector 的內積)作為引數,

計算出 DFG1 term 和 DFG2 term 的積分結果,儲存在 R和G通道。

在 run-time 時,將物體的 F0、roughness 和 NdotV 資訊引入來計算出結果。



在 pre-compute 實作上,我是使用 Unity 的 Compute Shader 來處理,

代碼如下


函數 ImportanceSampleGGX 和 Hammersley

可以參考 importance sampling (重點採樣)Low-discrepancy sequence (準亂數列)

這兩篇文章的討論。

比較值得注意的是在 淺談 physically based rendering (4) , G 項的 K值我是使用



但在 IBL 的 pre-compute , K 值我是使用 k = α / 2。

主要原因是參考了 [2] 的說明,認為 k = α / 2 在 IBL的計算上可以得到一個比較好的結果。

執行後,會得到下面的一個圖片(註1)



U軸為 NdotV, V軸為 roughness


  • Integral of Lighting

接下來觀察 LD 項,可以發現和在 Image Based Lighting (1) 裡,

做 Diffuse 的 pre-compute 很類似,都是收集周遭環境光源做積分運算。

但不同的是,為了簡化 Specular BRDF 積分式的變數數量,

使用 importance sampling 去找出取樣點,分割成 LD 項和 DFG 項。

因此,不能像處理 Diffuse 一樣,

對 Spherical Coordinates (球面座標系) 的  θ 和 φ 做等間隔取樣,

必須用 importance sampling 回傳的取樣點,去做積分計算。


但這裡發現了一個問題,在 LD 項只有 importance sampling 回傳的取樣點法線向量,

光靠法線向量是無法得到周遭環境光源的光線資訊(註2)。

為了解決這個問題,令 normal = view = reflect [2],

 importance sampling 回傳的取樣點法線向量,可以直接取得光線資訊。


圖片來源 : Learn OpenGL - Specular IBL

根據上圖可以觀察到,如果假設 normal = view = reflect 時,

反射結果會變得模糊但仍然可以保持原始輪廓。

[2] 為了減少假設帶來的失真,發現可以在 LD term 算式內,

加入 NdotL (normal dot light-vector) 作為權重值,來近似原始效果。


在 pre-compute 實作上,我同樣是使用 Unity 的 Compute Shader 來處理,

代碼如下

其中,要注意的是 LD term 算式存在著 roughness 這個變數,

根據 [5] 的文章,可以將 roughness 分成數個階段,

將每個階段的結果存儲在 cubemap 的 mipmap 裡面。

在實作上,我是分成5個階段 (i.e. roughness = 0, 0.25, 0.5, 0.75, 1),

由於 roughness 越高,光線越模糊,

因此根據 roughness 的程度由小到大依序儲存在 cubmap 的 minmap 裡。



  • 結論
到了這裡,已經完全說明了 IBL 的基本原理,

在下一篇會展示渲染結果並和 Unity 內建的 standard shader 做個比較和分析。


  • References



  • 附註
1.在 [2] 文章裡面,是使用 R16G16 的圖片格式來儲存 Integral of BRDF 的資料,
   主要是認為精準度會對渲染結果有很大的影響。
   但在我的實作中,因為考慮到 mobile 裝置支援性,選擇了傳統的 RGB24格式,
   這部分可以視情況做修改。

2.本篇是延續  Image Based Lighting (1) 使用 Environment Map (環境貼圖)
   作為周遭的環境光源,因此需要反射光向量來取得光線資訊。

沒有留言:

張貼留言