Skip to main content

對多重散射的誤解

9 月

01, 2020

by c0de517e


技術

我們來自己做一個可以處理物理成像的現代、多重散射的雙向反射分布函數(bidirectional reflectance distribution function,又稱 BRDF)吧。

當然,這個東西已經有其他人做過了,他們運用了數學和物理方面的知識來創造出非常厲害的解決方法。 如果您想多做一點閱讀了解一下,我強力推薦這幾篇文章:

但是,我們今天會跳過這些文章,用更簡單的方法講這個主題。 準備好了嗎?

我們先從擁有 Smith 模型特徵的原始現代 GGX 開始吧。 以下這組圖像是用迪士尼 BDRF 瀏覽工具的環境照明模式生成的。 我們看到了粗糙度介於 0.3 到 0.9 之間的金屬 GGX,使用的是常見的 alpha = 粗糙度平方參數。

有注意到粗糙度很高的時候,材質會看起來太黑嗎? 這邊一樣有很聰明的人找到了解決的方法。

我們來用 Kulla 和 Conty 的多重散射 GGX BRDF 比對一下:

處理完畢!

我們現在可以嘗試思考這個方法背後的原理。 用到的數學都擺在您面前了,這樣您更應該看它一眼。 這樣不僅有趣,您還可以多加了解其他相關的問題。 但是,我再說一次,現在這些通通都會跳過。

我們從頭開始吧。 為什麼我們要偏向物理導向成像(physically based rendering,又稱 PBR)? 不是因為我們喜歡物理(至少我本人不喜歡啦)。 如果考慮到我們在端到端的圖像轉換流程的種種大問題,物理方面的問題只能算是芝麻小事,不值得我們那麼深刻地去關切…

電腦圖像不是透過預測成像完成的。 我們的目標不是利用物理塑造真實性。 製作漂亮的圖像才是我們想做的。

我們發現透過物理模擬來製作漂亮的圖像的時候會讓某一些工作流變得更簡單,像是將材質和照明拆分、減少小步驟和參數得數量、允許使用針時材質的資料庫等等。

因此,當我們要決定如何繼續時,我們會把藝術層面的考量放得比技術層面得考量更重。 如果我們找得出某個藝術層面的問題,我們就可以看看有沒有一個運用物理的技術層面解決方法。 我們目前的 BRDF 所遇到的問題是我們很希望我們的材質參數是正交的,而圖像會因為調高粗糙度而變黑這種情況非常不理想。

那麼,物理幫得到我們嗎? 這個變黑的情況以物理角度來看的話,正確嗎? 我們要怎麼測試? 答案:把東西放進熔爐! 我們把裹上 GGS 的金屬物件放進一個照明均勻的環境,看看會發生什麼事。

光會打到表面,也會打到微表面。 根據菲涅耳方程式,微表面會將一部分的光反射,也會將一部份的光折射。 如果我們假設表面是金屬材質,根據物理原則,折射的光會「進入」表面,被吸收而轉換成熱能,不會再出來了。

金屬物體再熔爐里也會有顏色這件事是合理的,因為有一些能量會被吸收。 但是如果把微表面調到會反射所有的光(不會吸收任何光),那會發生什麼事呢?

我們這樣就要把 f0 設為 1。(記住,菲涅耳方程式會決定微表面會散射的光量。) 我們試試看會發生什麼事吧。

物體還不是白的。 有問題! 觀察得比較仔細的讀者可能會問,「您怎麼知道有問題?說不定散射的光量會因方向而不同?」 要想出正確的方法往往沒有那麼簡單。

我們來思考一下一道從鏡頭開始前進的光線會長得怎樣吧。 光線會打到一個或好幾個微表面,之後會彈來彈去,最後會離開物體而跑到熔爐環境裡。熔爐環境一直都在散發著固定的能量。

那麼,有多少能量會跑到鏡頭呢? 全部! 原因是根據我們的設定,不管光線打到幾個微表面,所有能量都會被反射。 所有的光線最後都會回到鏡頭。

因此,上面的圖像應該要完全白色才對。 如果不是,那就代表我們用的數學有問題。

如果您對 BRDF 有研究,您就會知道微表面模型裡有一個遮蔽陰影可見度函數,決定哪一些微表面會被其他微表面遮蔽。 我們通常不會考慮的是這些會遮蔽的東西本身就是微表面,所以光線不會被捨棄,而是彈來彈去之後最後跑出來。

這就是多重散射模型可以架構和處理的地方。如果我們把 Kulla 和 Conty 的 GGX 放到熔爐裡,出現的會是一個完全無味但正確的白色圖像。這裡用的是會將光完全反射的材質,粗糙度不會影響最後的結果。

但是 Kulla 的模型有一點複雜,通常不適合用在那麼小的問題上。 那麼,有沒有更簡單的方法解決這個問題? 可以知道我們的 BDRF 在特定的粗糙度和視角(而 f0 還是 1)時在熔爐裡會散發多少光嗎? 我們可以用那個值把 BDRF 歸一化嗎?

偷偷告訴您,可以,而且還很簡單。 我們在大部分當代的引擎中都可以找到這個「熔爐」值,它就在廣用的切分總和圖像用照明近似示像(split-sum image-based lighting approximation)會用到的尋找表裡。

這個切分總和表將會把「熔爐裡的 BDRF」(又稱定向反照率或定向半球反射率)縮減成比例和偏差係數,之後就會套用到菲涅耳 f0 值上。

我們這裡想要再考慮 f0=1 的情況下進行歸一化,所以我們只要把 BRDF 葉片的 bias(roughness,ndotv) + scale(roughness,ndotv) 往上加 1 就好了。 結果如下圖所示:

我們開始在高粗糙度時取回一些能量了,如果我們在熔爐裡面進行測試,當 f0=1 時我們也會獲得正確的全白圖像。 但是,這還是和 Kulla 的結果有點不一樣。 色彩飽和度在比較粗糙的材質上明顯差了一截。 為什麼? 如果您有偷做功課,您應該就會知道答案。

光線在離開表面前打到越多微表面,我們就會接收越多顏色;這就是正確的多重散射會增加飽和度的原因。(將顏色增加一次方時,顏色就會更飽和。) 這個結果為什麼以物理角度不可能出現,但是實際測試的時候還是出現了?

我們因為沒有把多餘的飽和模擬出來而維持著能量守恆,但是我們的 BDRF 參數所代表的意思已經變了。 我們「刻意忽略細節」的多重散射 BDRF 裡 f0 的「意思」和 Kulla 模型裡的不一樣。 反照率不一樣了,但是 BDRF 本身還是會維持能量守恆。 這裡只是參數化不一樣罷了。

更重要的是,我覺得這樣的參數化反而更好! 還記得我們的目標嗎? 我們玩物理不是為了確保正確的物理行為,而是為了我們要達成的事情。

我們想讓參數有更多的正交,這樣繪畫師不需要在高粗糙度時刻意把 BDRF「加亮」。 如果我們用「比較正確」的方法(想多了解的話,我強力推薦 Narkowicz 最近發布的文章),我們就會加入一個不同的關係,使粗糙度不會讓材質更黑而是讓材質更飽和。(但是,這樣做就沒有意義了。) 您可能會想得到這樣做還不錯的情況,不過我覺得在我們的使用情況理幾乎都不應該這麼做。

如果我們要模擬額外的飽和度的話,我們可以用幾個簡單的方法。 一樣用「刻意忽略細節」(容易)的方法的話,我們就會把 BDRF 擴張 1+f0*(1/(bias(roughness,ndotv) + scale(roughness,ndotv)) – 1), 得到的結果如下圖所示:

現在我們開始靠近 Kulla 的方法了。 為什麼 Kulla 的示像不一定會在圖像裡出現,反而還會更好呢?原因是我們的示像沒有遵守相反法則。

這對一些動畫工作室可能很重要(某些可以離線使用的追蹤式光線傳輸算法需要用到相反法則),不過對我們其實沒什麼關係。

既然我們找到了一個不錯的示像,我們應該更進一步探討我們真正在做些什麼。 嗯,公式我們有,不過需要用到尋找表。

這本身是小事(反正我們要運用圖像進行照明的話還是會用到尋找表),但是這等於我們要多抓一次紋理,這樣的話最好多看一眼算法。 我們把現在在用的 1/[bias(roughness,ndotv) + scale(roughness,ndotv)] 函數視覺化看看吧:

看起來很簡單! 不需要複雜的工具出馬就找得到了,我直接給您看好了:1 + 2*alpha*alpha * ndotv。 非常棒。

示像與正確的歸一化係數的比較(灰色表面)

我們看得出來熔爐測試出了一點錯。 我們可以用特徵多項式改善這個情況,上述公式的「2」和「1」並不是最恰當的常數。但是,光是定義「最恰當」這個詞就是一門學問了,因為我們看不太出來對歸一化函數進行均方最小化的意義。(我們應該在意的是末端視覺呈現、認知表準、哪些角度更重要等等。) 我們已經花太多時間在一個渺小的問題上了。 另外,實際的成像本身很難跟使用表格的方法清楚分辨。

如果還想再簡單一點,捨棄 ndotv 的關係…我們先來看看會怎樣吧。 在這樣的情況下,1+alpha*alpha 的成效也還不錯。

我們在 BDRF 多加了一個倍增因數,讓它在高粗糙度的情況下可以更亮;這樣做非常合情合理。

相對地這也會造成更多誤差,BDRF 形狀改變的時候會更明顯。 我們從粗糙平面的掠射角會獲得更多能量,不過您沒有那麼挑剔的話,或許這樣就夠了:

我們在這裡透過降低曝光率來突顯一個重點:使用近似示像的時候,我們有時候會失去光能,有時候要增加光能。

希望您到這裡已經了解到大致的概念了。 沒錯,您可以埋首重現完美模擬現實物理定律的圖像。 但是,您也可以走小捷徑。 不管是為了效能,簡易度,還是純粹想偷懶,有時候走捷徑未必是壞事。


Roblox Corporation 與此部落格不為任何公司或服務宣傳或背書。 另外,我們不保證此部落格李的資訊的準確度、可靠度及完整度。

此部落格文章原本在 Roblox 科技部落格發布。