[Volumetric] Study Atmospheric Scattering

River Wang
二流遊戲開發
Published in
4 min readNov 29, 2020

學生時期就看過相關 papers,大概知道技術概念,但一直沒有努力經歷一遍完整的實作。趁現在終於比較有空想來學好這個部分。當然網路上教學也很多,但我找到最好的資源首推 Alan Zucconi 大神的教學系列。真的寫得非常詳盡,基本上 shader codes 也送給你了,只要一步一步照著做就可以完成這個功能,因此我準備成一個 URP 版本 shader。

簡單介紹演算法,這是一個相對單純的繪圖功能,所以可以僅使用單一 shader 就完成。(現成圖解主要來自 NVIDIA GPU GEMS)。

要模擬大氣層的顏色(光譜物理變化),我們將光源通過大氣層的過程視為無數的光子 (Photon) 進入大氣層,因此 Photon 有無限的可能會跟大氣層中的介質/粒子發生碰撞而改變路徑與頻譜。整個物理現象是一個大學問,但如果想要用 GPU 模擬這個現象我們必須盡量簡化牽涉到的計算量,因此假設以單一碰撞發散 (Single Scattering) 為大前提,也就是假設一個光子進入到鏡頭的過程中只會發生一次碰撞,多次反射/折射/吸收的過程直接忽略。

下一步我們考慮 Single Scattering 的過程,這可以使用 Raymarching 方法來實作,針對每個視線 (viewray) 我們計算出其與大氣層的交集線段,在該交集中實作 Numerical Integration 來計算這段區間光源對鏡頭畫面的貢獻,並換算成最終的像素顏色。

對於 Raymarching 過程中每個採樣點 (P1, P2, P3, …),其光源貢獻必定來自太陽光源,因此我們要再次計算太陽光源從到達大氣層 (C) 至採樣點 (Pn) 過程中的光源貢獻 Numerical Ingration (採樣 Q1, Q2, Q3, …)。而太陽光源經由路徑 (C-Pn) 發生碰撞再折射經路徑 (Pn-A) 到達鏡頭這個過程是 In-Scattering;而光子通過路徑 (C-Pn) / (Pn-A) 過程中也會因為碰撞發生部分貢獻能量散射衰減,此過程是 Out-Scattering。整個 Raymarching shader 演算法就是著重在如何實作出整個 Scattering 過程,累積計算出最終光子頻譜和光源強度當作大氣層的顏色。
(我知道我講的很抽象,因為我數學也很爛,就不班門弄斧亂講解了 XD。有興趣的人真的推薦去看完完整的原文教學,寫的非常好。)

雖然我年輕時也是先看到 NVIDIA GPU GEMS 的教學,但我數學太差至今仍有部分看不太懂,而那個教學年代比較久,Raymarching 實現在 vertex shader 當中,Alan Zucconi 的教學則是把物理現象/數學推導解釋地更詳細,終於讓我有機會看懂其推導過程,因此我先選擇實作他的版本。
話說我以前提過的,什麼 XXXX In a weekend 都是騙人的,我也是如此,雖然 shader 是一兩個晚上寫完/debug完成,但其實為了看懂教學我大概花了接近兩周的晚上,整個教學系列重看到第三遍我才確定我做出第一個成功的版本。

因為原文教學就是使用太空環境觀看星球大氣層的應用情境,所以我猜這個 shader 直接拿來當 Procedural Sky 應該會有點問題,而且目前的版本我只考慮了 Rayleigh Scattering 現象,Mie Scattering 現象因為是付費教學內容,要等我有空再繼續去試試。

更新:因為尊重原作者,所以自我練習的 repository 已經變更為私有不公開。

--

--

River Wang
二流遊戲開發

學生時代就跳入 Computer Grpahics 領域,其他技術好像都不想學/學不來,工作也找 Graphics 相關內容,但幾年後第二份工作才真的投入遊戲產業,不過也是個失敗的經驗。目前雖然逃離遊戲慘業,但也沒混出什麼出息,仍然在混口飯吃,並幻想著某天自己終於有時間開始完成自己想做的遊戲作品。