前言
上一篇中主要介紹了定製化噪聲紋理
在體積雲建模中的應用。這次粗讀這篇分享的下半部分,即體積雲的——光照、渲染、優化幾個部分。
其中光照之所以從渲染中拆出來,可以認為光照更多的是做物理模型計算上的分析,渲染則更多考慮實際遊戲中的實現。
文章還是以翻譯原文的講稿為主,重點部分會摘一些原文。由於篇幅原因會分為上下兩篇,這是其中的下篇,打星號的部分則是我個人的補充說明。
1 光照
雲的光照是一個計算機圖形學中研究很深的領域。其中最好的結果需要基於大數量的採樣。
在遊戲中,當你詢問雲渲染方面的性能預算時,往往得到的答案是“零”。我們決定先實驗當前主要的基於近似計算的渲染技術,以再現3個最重要的光照特性。
- 雲的直接散射或發光
- 透過雲層看向太陽時的一線亮光(sliver lining)
- 背向太陽時可見的雲的暗色邊緣
前兩者有標準的方案,但是第三條需要我們自己找方案。
當光線穿過雲層時,主要光線大部分時間都在水滴和冰晶之中多次折射後才進入人眼。
當光線最終穿出雲層,被稱為外散射;或被雲層吸收;或與其它光線合併,則被稱為內散射。
在電影視效工業中,我們可以花很多時間來收集光照信息並粗略地再現這個效果(離線渲染),但是在遊戲中我們必須用一些近似方案。有一種把光線的這3方面行為作概率估算的標準方法。
貝爾定律表明,我們可以基於一個點所處介質的光學厚度來計算出射光線的量——這樣我們就有了描述雲中一個點光線量的基本方式。
如果我們用能量代替透射率,用深度代替厚度,並繪製出如圖所示的曲線——你能看到能量隨深度程指數減少。這個函數構成了我們光照模型的基礎。
*這裡的貝爾定律應該對應的是光學上常說的Beer-Lambert Law,可以簡單概括為:光被透明介質吸收的比例與入射光的強度無關,與介質的厚度有關。
不過在一個點的光能計算上還需要考慮另一個組成部分的貢獻,即光線向前或向後的散射概率。這一項被用來產生“一線亮光”的視覺效果。
在雲層中,向前散射的概率是更高的——這被稱為各向異性散射。
在1941年,Henyey-Greenstein提出了這一模型以幫助天文學家計算銀河系尺度的光學(galactic scales),不過今天它被用來較好的再現雲層光照的效果。
*下面簡單介紹一下這個函數:
公式內容
該模型的g值為必須在(-1, 1)範圍內。g的負值對應於背向散射,正值則對應於前向散射。g越大,在沿入射方向附近發生的散射就越多(分別對應背向散射和前向散射)。
0-1之間的系列函數圖
每次我們採樣計算光能量時,要乘上Henyey-Greenstein相函數。
*左側是基于貝爾函數(厚度)計算的結果,右側是兩者考慮計算的結果。
但到這一步我們仍遺漏了一些重要的特性——雲層的暗色邊緣。這是一個還未被很好的提取成論文方案的特性,因此我們需要自己探索並理解發生了什麼。
回想一下雲層中光路的隨機傳播:
- 如果我們把雲層內的一個點與靠近表面的一個點相比,內部的點會接收到更多散射光線。換句話說,被我們稱為雲材質的,可以理解為一個光的收集器(collector)。越深入內部,越多潛在的收集到光的可能性,直到光開始衰減。
- 這在雲的弧形結構中尤其明顯,以至於雲中的裂紋會比起凸出和邊緣處顯得更亮——因為它們受到了少量集中爆發的散射光。
通常在電影工業中,我們會對一個點採樣儘量多的光照並使用一個複雜得多的相函數(phase function)。通過這種暴力的方式也能得到這個結果(原文提到了Magnus Wrenninge的多重散射分享),不過在遊戲中我們必須找到一種近似方案。
我的一個以前的同事Matt Wilson說一堆粉末狀的鹽能得到一個類似的效果,因此我把這個作為研究的方向。
*這幾個特性都是基於光傳播的估計,由於每一項都做了一定的簡化而損失了部分沒考慮的能量,嚴格來說最後的結果是差能量的;但是由於得到差不多的視覺效果即可,因此是可行的。
當你理解了這個效果(邊緣更暗),就會處處留意到它,而不會視而不見。即使在稀疏的雲層也有這一特性,它的暗色漸變會更寬一些。
現有(前面的貝爾+HG)光照公式無法直接實現這個效果的原因是,我們用的光傳播函數也是一個估算,因此沒把這類項列入考慮。
讓我們把這一效果作為基於深度的統計概率問題來考慮。
當光線深入雲層,潛在的散射概率會提高,會有更多光線進入我們的眼睛。
把圖中兩個函數組合起來(Bear's Law和Powder)就得到了這一效果的描述方式。
我仍在美國計算機協會數字圖書館(ACM digital library)尋找 Beer’s-Powder這類近似方案,目前還沒在其中找到有類似名字的方法(意思是這個trick可能是分享人首創的)。
*圖中展示了視覺對比,其中貝爾定律負責基礎散射部分,粉末效果函數負責暗色邊緣部分,最終方案是兩者的組合。
*圖中是貝爾定律和組合方案的對比。
在遊戲中,這一效果為厚的雲層增加了很多真實感,幫助我們突出了場景的部分輪廓。
不過必須記住這個效果是一個視點相關的效果。我們只應該在視覺向量(攝像機方向)與光照向量接近時看到這一效果,因此粉末函數需要內含基於這一點(向量夾角)的漸變過程。
*原分享中這裡有一段視頻展示了攝像機轉動過程中雲的視覺變化。
我們光照模型的最後一部分是——對於積雨雲我們人為的增加了它對於光的吸收係數(顯得更暗)。
最後,回顧一下我們對3個主要視覺特徵的實現:
- 貝爾定律(光通過散射介質的能量基於厚度)
- Henyen-Greenstein相函數(光通過散射介質的多方向能量分佈的一種描述方式)
- 粉末效果函數(邊緣暗色效果的一種近似)
- 對積雨雲增大吸收係數
*這部分主要探索了雲中一個給定位置的點如何計算光照,後面則是基於這個結論出發做光線步進採樣的過程。
2 渲染
前面幾個部分已經介紹了雲的建模與光照算法。這一節開始會描述如何在一幀中進行採樣並渲染雲層,以及如何將雲與大氣和每日時間系統集成。
通過光線步進(ray march)開始渲染的第一步是決定起始位置。在我們的情況中,《地平線》中的主要視點在地球表面上(而地球是球形的)。
構成我們遊戲中地球外層的大氣包圍層的雲,分別處於大氣中的不同層級。
如果從海平面看向遠方,你能清晰地看到地面的曲率,以及雲層下降到地平線以下的效果。
基於這些原因我們遊戲中的雲基於在球狀大氣層中的位置被劃分為兩類:
- 低海拔(1500-4000米)中通過體積渲染平流層類型的雲。
- 高海拔的捲雲和高雲用2D捲動紋理的方式來渲染。高海拔的雲通常不會很厚,這麼處理可以減少渲染上基於ray march帶來的性能負擔。
通過ray marching穿過球形大氣層,我們能:
- 使雲能正確的下降到地平面
- 這也意味著我們可以通過收縮大氣層的弧度來調整場景的視覺規模
*通常意義的“天空盒”實際上是沒有把這個天空弧度問題納入考慮的,這裡考慮進去才能做出比較自然的“海平面”或“天邊”的效果。
在實際的情況中我們不希望使用任何非必要的昂貴技術——相比採樣射線方向的每一個點,我們把採樣分為兩個細節層級(步長不同),僅在實際命中雲層時才進行細節逐步採樣。
回顧之前雲建模中提到的,基於低細節噪聲採樣來繪製雲的基礎形體(左圖),然後採樣高細節的噪聲以添加寫實化的細節(右圖)。
其中高細節的噪聲可以始終視為對雲的基礎形體做邊緣“腐蝕”的效果。
這意味著我們在計算高細節噪聲採樣時,與之相關的其它項都可以通過低細節採樣的結果返回一個非零值。
這能幫助構建一個包圍雲區域的等值面(isosurface)。
So, when we take samples through the atmosphere, we do these cheaper samples at a larger step size until we hit a cloud iso surface. Then we switch to full samples with the high detail noise and all of its associated instructions. To make sure that we do not miss any high res samples, we always take a step backward before switching to high detail samples.
因此,當我們穿過大氣層進行射線採樣時,我們採用大步長的採樣直到命中一個等值面;之後我們就切換到全精度採樣,採樣高細節噪聲和它的相關項。
為了確保我們不錯過高精度採樣的樣本,在切換到高精度前我們始終後退一步再開始高精度採樣。
當圖像的alpha值接近1時,我們就不需要繼續步進採樣了。
如果始終未達到alpha值為1,我們引入了另一個優化:
在幾次連續採樣並返回0密度值之後,我們切換回大步長的檢測,直到再次命中另一個雲層表面或達到體積雲層的頂部。
由於朝向地平線的射線長度更長,因此我們從64個潛在的採樣數開始,最終得到大約是128個潛在採樣數。之所以說是“潛在的”,因為可以通過一些優化使採樣提前結束——並且我們真心希望能如此。
這就是我們如何通過採樣來構建圖像的alpha通道。而要計算光強度,我們需要更多的採樣數。
*這裡可以把採樣得到的alpha值視為“視覺密度”,即對採樣密度的加權累加。
通常在ray march中(採樣光照)需要如圖朝光源採樣,基於光照方程計算能量並加總,直到退出步進過程或alpha值達到1。
在我們的方案中,我們每一步在一個錐體內向太陽(光源)方向做6次採樣,並且通過計算相鄰雲層密度帶入我們的光照方程中,綜合權重得到一個較好的效果。
其中最後一次採樣會被設置為遠離其它採樣點,以捕捉(capture)遠距離雲層投下的陰影。
*由於採樣精度的原因,這裡說的“陰影”最多隻能是影響一些明暗變化。另外,錐體及內部的採樣點的相對位置肯定是基於一些算法預先生成的。
圖中可以看到僅進行alpha採樣步驟後,我們的雲的效果:
- 包含錐體內5個對光源採樣的點
- 以及一個對遠距離雲採樣的點
為了提升光照採樣的性能,我們在圖像的alpha達到0.3後切換到簡易版的shader,這使整體的著色過程提高了2倍速度。
將光照採樣位置的深度(在雲中的)帶入我們光照模型中貝爾定律的部分,並且能量值在雲中會基於深度衰減,最終加總與標準的體積渲染中的ray-marching步驟一致。
*這個核心公式上一篇中介紹過了。
我們的ray march方案的最後步驟是採樣高海拔的2D雲紋理。
這裡展示了一組不同類型的捲雲和高雲的紋理集合,它們可以平鋪並以不同速度和方向進行卷動(以實現動態的高海拔雲層效果)。
在現實中,雲中會混合不同頻率(顏色)的光線來產生優美的顏色效果。而由於遊戲中的世界是基於大量估算的,因此我們在計算雲的顏色使必須基於一些假設。我們在計算雲的顏色時考慮了以下模型(這裡指計算方式的歸納):
- 環境光的貢獻隨著高度增加而提高
- 直接光源的顏色中,太陽的顏色是佔極大比重的
- 大氣層會隨著深度遮蔽雲層(指距離攝像機較遠會有類似霧的基礎顏色衰減過程)
最終,我們把環境項和直接光照項求和,並基於深度值(指遠距離)逐步衰減至大氣層顏色。
現在,我們可以改變一天中的時間來使光照和顏色自動變化。這意味著不需要使用預烘焙來佔用稀缺的內存空間,整體的內存開銷僅有2個3D紋理和1個2D紋理,而不是一堆基於billboard渲染的雲和天穹的紋理。
最後總結一下我們的渲染方案:
- 用“廉價的”(大步長的)方式採樣,直到潛在地命中雲層
- 使用64-128個潛在的採樣數,在雲內部每一步做光照採樣時,使用一個錐體內採樣6個點的方式。
- 光照採樣在雲中一些深度後從全精度切換至“廉價版”方式
*體積渲染的核心思想就是在介質內做ray march,並逐點採樣加總對光照的貢獻。問題就是要平衡性能開銷(採樣數、步長和著色精度)和效果,這一點很難。
*之前介紹過一些方案中會藉助有向距離場來輔助第一次射線命中定位,但那又需要額外儲存空間的SDF數據。
*如果是更規則的固體,或許他們可以使用先對BVH做射線檢測的方式來預判命中位置。我猜測他們沒有這麼做的原因是由於雲形體的不規則性——如果要動態更新雲的包圍盒數據,其實性能上的trade off就未必划算了。
3 優化
前面介紹的方案每幀需要耗時20毫秒(當時在PS4上)。
這意味著儘管效果很好,但仍然不夠快,無法直接集成到遊戲內。我的協作開發者以及導師 Nathan Vos提出了一種優化方案。
- 每幀可以使用一個四分之一精度的緩衝區
- 對於屏幕渲染結果每次更新4X4的16個像素中的一個
像素的位置需要重映射到前一幀,來確保視覺效果上是連續的(類似Motion vector思想)。
*這類基於分時降低渲染壓力的思想後來也廣泛運用於光線追蹤降噪中(之前有文章介紹過)。
在那些無法重映射像素的點,例如屏幕邊緣(攝像機移動時),我們採用低精度緩衝區的結果進行替換(後續屏幕空間精度會有部分像素重新計算,並重寫入緩衝區)。
Nathan的方案使得最終在半精度時的著色都比之前快了10倍甚至更多,並且我們可以使用過濾器(filters)來提升畫面精度。
這項優化是最終體積雲的效果能出現在我們遊戲中的重要原因。最終性能開銷在2毫秒左右,並且大部分來自於大數量的渲染指令調用(而不是採樣或其它)。
最後回顧一下:
相比於最初的目標,我們獲得了巨大的成果。並且我們仍在進行工作以致力於提升性能和可控性;我們也在繼續開發大氣模型和天氣系統,並將在後續在網頁和未來的講座上進行分享。
- 所有性能數據和效果抓取自Playstation 4平臺
- 所有方案內容編寫自PSSL和C++(PSSL就是PS上的圖形語言)
結語
SIG上的分享往往就是有很多圖形學上的細節。例如這篇分享中,基本就從雲的物理分析及建模方案開始,逐步介紹了建模、光照、渲染等幾個部分,從建模上的合理近似到圖形功能開發的過程。最後的降精度、分幀的優化現在來看反而是一個很常見的做法了。整個過程因為限制很多,因此也充滿了實踐智慧。
不過也能看出在2015年的時候,體積雲相對還是一項昂貴的技術,即使優化到了2毫秒,(整合上其它全部渲染內容)幾乎也就判定了遊戲只能以30幀每秒來運行。後面我們可能還會讀到一些體積雲方案的優化迭代,但這個遊戲中的一些基礎探索都是很寶貴的。
其實在遊戲中引入大幅提升畫質的圖形技術,在我看來從來都不是一個玩法設計導向(或製作人拍板)的事,它往往源自美術人員和開發人員的自我激發;最終的性能空間、優化、整合的效果是一個各部門協作和商議的過程,30幀的畫面領先的遊戲和60幀的畫面平庸的遊戲,很難說應該選哪個,這是一個複雜的話題(包括是否要做多種畫質切換與適配)——至少在這個遊戲上,他們選擇做一個畫面領先的遊戲。
類似我的玩家可能會覺得,我不在意雲有多好看,最終還是要玩得流暢——但是實際上你並不可能真的不在意,這是把你引入這個世界(入戲)的重要組成部分。最近迴歸魔獸世界,看到巨龍時代裡和天空盒融為一體的“圖片雲”就特別出戏,由此我也進一步意識到這一點:當你的整個世界的渲染精度都足夠了,那雲也是不能不夠“真”的。
最後是資料鏈接:
The Real-time Volumetric Cloudscapes of Horizon: Zero Dawn這篇分享的PDF地址
這篇分享的PPT地址(含視頻版)
Volume Scattering的一篇知乎翻譯