前言
在上一篇中,主要覆蓋的內容可以概括為動態的網格策略、不一樣的多層材質策略,以及如何應用虛擬紋理來渲染地表。這次繼續讀這篇分享的下半部分, 還是以翻譯原文PPT頁及解說稿為主,打星號的部分則是我個人的補充。
正文
*作為和上篇的銜接,會先從虛擬紋理(縮寫為VT)頁的組合回收策略開始。
頁合成管理
每一幀,我們只計算有限數量的頁(page)。在低端移動端硬件上,這個數量不超過4頁;而在高端PC上,每幀的數量是20。
而不在緩存中的最重要的頁,則始終被最先合成。(*原文中是Popular這個詞)
如果還有剩餘空間,我們重新計算可能用到的低精度的頁——它們的生成也會按優先級排序。
所有頁都有一個壽命(age)。被請求的頁會將age重置為0,而不可見的頁會隨時間遞增這個值;超過3幀未被請求的頁會被回收,而回收始終從最舊(age最大)的頁開始。
貼花渲染
貼花(Decals)在渲染大地形中扮演重要的角色。
我們遊戲中有3類貼花:
- 地形貼花——頂視角,矩形正交投影(top-down rectangular orthographic projections)。
- 樣條曲線(splined)貼花——頂視角,沿著一個貝塞爾曲線矩形正交投影(follow a Bezier curve)。
- 體積貼花——不限於頂視角,對於任意幾何體都有效,不限於地形。
在參考截圖中:
- 第一張截圖中展示了地形沒有貼花時的樣子。
- 在第二張截圖中,地形層上的地形貼花被渲染了。
- 在第三張截圖中,樣條曲線貼花被渲染了——如你所見,這種貼花被我們用來製作道路。
- 最後的截圖展示了幾種貼花組合在一起的效果。
Streamer連接
我們把負責從硬盤加載資源的系統稱為streamer(流式加載器)。它的策略通常是基於攝像機的位置。
例如,streamer負責加載(全屏幕分辨率下)貼圖對應的mip;它也負責加載網格的LOD,以及聲音、動畫等其它類型的資源。
大地形需要從硬盤加載大量的數據。基於攝像機位置,遊戲計算出網格的頂點和索引、貼圖mip、地形層級和貼花等需要加載的數據。
當一個頁被請求合成時,遊戲首先檢查是否有合適的mip被加載。如果未被加載,則提高它們的加載權重,使其能儘快被加載完畢:
- 如果有貼圖mip未加載,我們仍合成該頁,但標記其為低精度(low-rez)。
- 如果關鍵數據未加載——例如貼花節點,我們則標記該頁為未完成(not ready)。
- 渲染有著未加載元件的頁會導致顯著的視覺故障。
- 如果沒有元件缺失,我們則標記這一頁為加載完畢(ready)。
地形渲染合成
地形(渲染的)合成使用3層被稱為scratch buffers的G-buffer。(*scratch沒有合適的翻譯,直譯是劃痕)
第一層存儲了漫反射係數(albedo),第二層存儲了法線(normal),第三層存儲了金屬度、遮蔽度和粗糙度(metalness, occlusion & gloss)。當合成一個頁時,會同時寫入這3個buffer。
Scratch buffers是未壓縮的渲染目標(render targets),因而很耗顯存。
*render targets可以理解成是臨時渲染的一些中間結果,用於後面的合成和渲染步驟。它們不會直接顯示在最終的屏幕上,但會佔用相應的顯存。
地形渲染合成
- 我們首先使用一個compute shader(計算著色器)來渲染地形層;
- 之後用一個vertex & pixel shader(頂點-像素著色器)來渲染樣條貼花;
- 再之後通過一個vertex & pixel shader來渲染地形貼花;
- 最後用一個compute shader來渲染體積貼花。
貼花彼此之間是排序過的,但(基於管線)無法使一個體積貼花先於地形貼花來渲染。
*compute shader對於低端的移動設備還是不兼容的。
壓縮
在把scratch buffer中的頁複製到物理紋理中前,我們需要對其進行壓縮——這也通過我們編寫的compute shader來完成。
在移動端,我們使用ASTC壓縮,而其它平臺我們使用BC壓縮格式。在除了移動端外的其它平臺,數據可能被壓縮成2張BC3紋理、或3張BC1紋理——注意這是我們目前為止可見的最大視覺質量的損失。
*BC是block-compression的縮寫。兩種壓縮方式都是的解壓插值計算都由對應的GPU來支持。文末附帶了BC壓縮格式的說明。
顯存中的物理紋理
*這裡雖然是翻成物理紋理,實際上應理解成“最終合成的地形紋理”。
我們的物理紋理有2層mip——這會在紋理的雙線性插值中使用到。
而我們的物理頁有4像素的邊緣,這是為了使8X的各向異性過濾能正確工作——注意,這意味著我們每頁只能使用248*248像素(預留了邊緣)。
我們的BC3物理紋理(兩張)有8個通道供使用:
The first texture encodes albedo and metalness, while the 2nd encodes normal, occlusion & gloss. The X component goes in the green channel while the Y component goes in the alpha channel.
- 第一個紋理編碼了albedo和金屬度,第二個編碼了法線和遮擋—粗糙度。
- X座標分量數據存儲到了綠色通道,而Y座標分量存儲到了alpha通道。(*albedo就對應RGB,直接佔用3個通道)
在BC1壓縮模式中,我們有9個通道(3張)供使用:
- 第一個紋理編碼了albedo。
- 第二個紋理編碼了粗糙度、法線的X分量和遮擋程度。
- 第三個紋理編碼了金屬度和法線的Y分量——剩餘一個通道可以供任意使用。
法線的分量存儲在綠色通道中,是因為綠色通道有6bits的存儲精度,而其它通道只有5bits。
雖然理論上BC3模式應該有著更好的視覺質量,但我(作者)從來無法區別兩者的差異——而BC1模式節省了三分之一的顯存,並且添加了一個通道,因而是更好的方案。
*這裡綠色多1bit是因為人眼對顏色RGB的感知本來也不是均等的。
置換紋理
在開啟了GPU曲面細分和置換貼圖(Tessellation&Displacement)的平臺,我們也使用虛擬紋理來存儲置換貼圖。
一個置換物理頁是32X32的尺寸,有著1層mip以及1像素的邊緣——因為它只用來做頂點的位移,而頂點的密度明顯遠小於像素的密度。另外,它使用BC4的壓縮,因此內存佔用非常小。
需要注意置換貼圖不能影響碰撞(collision),這意味著它們不能做得太大,以至於導致明顯的顯示問題。
*Displacement是一種以紋理數據描述運行時頂點形變的技術,只修改渲染時的頂點位置。作為對比,法線(normal)機制隻影響光照著色,並不改變頂點位置。
可變精度
由於我們的地形技術需要運行在各種不同的設備上,因此AVT(上篇介紹過,自適應VT)參數可以調整以應對不同平臺。我們有5檔渲染質量——參數可以參考上圖。
離線烘焙虛擬紋理
VT pages for the top 6 levels of the mip chain are baked offline. Usually, these pages are quite expensive as they cover large swath of the map.
最高6個級別的mip鏈是離線烘焙的。通常,渲染這些頁的開銷過高,因為它們覆蓋了大範圍的畫幅。通過離線烘焙這些紋理,我們顯著降低了GPU的壓力,並確保視覺質量。
圖中你可以看到僅使用離線烘焙頁的世界渲染效果。
它對於任何很靠近攝像機的物體來說無疑精度不足,但在一定距離看來,離線烘焙的紋理對於GPU方面的優化是更好的方案。
Although these images have to be streamed, it ends up saving memory, because we don’t need to stream terrain layers & decals for terrain surfaces that are sufficiently far away. This saves memory and reduces streamer pressure.
儘管這些紋理需要被stream加載(而不是GPU合成),它仍能節省顯存,因為可以略過很遠處的地形層和貼花的加載過程——這最終節省了顯存並緩解了streamer的壓力。
額外的通道
當使用BC1壓縮模式時,一共有9個可用通道——這意味著能多出一個通道用於其它渲染數據(前面也提到了)。
目前為止,我們共有4種不同的使用方式:
散射(Scattering)通常用來渲染雪,使它看起來更真實——如圖所示。
熱量(Thermal)用於夜晚和熱成像(heat vision)。
發光度(emissive,直譯是放射、輻射度),你可以在地面放置琥珀等寶石。
閃亮(Glint)通常用於沙地或雪地的閃爍效果。
啟用這類特性是在地圖級別的,我們只支持一個地圖同時啟用一種額外特性。(*因為就一個通道)
一體化的高度圖
當每個地形表面有各自的高度圖(heightmap)時,要得知大地形上任意一點的高度就變得困難了;而當一整個高度圖用以覆蓋整個大地形時,這個問題就變得微不足道了。
我們將其稱為一體化高度圖(unified height map)——縮寫為UMH,因為我們將所有地塊的高度圖合併成一個了。
基於UHM,實現邊緣環繞(skirts,直譯就是裙邊,如圖)效果變得可能了——而這是我們的藝術家一直想在地形上做出的效果。
虛擬高度圖
一個傳統的UHM需要消耗32MB的內存,而藝術家認為如果有128MB或更多,他們就能做出描述更精確的地形。而32MB是很多平臺的(單紋理)內存上限。(*上篇提到了,memory這裡同時是內存和顯存的概念)
通過將UHM虛擬化,我們顯著減少了內存上的限制。我們將這項技術稱為虛擬高度圖(Virtual Height Map),縮寫為VHM。
VHM有著144頁,每頁是64X64,以及額外2像素的邊緣。這意味著使用的緩存空間略小於1.3MB。
它同樣使用了一個間接映射表(indirection table),類似於VT用到的那樣;不同的是它只是一個buffer(純數據的)——對於最大的地圖,它也只佔用8KB。
VHM的頁基於和攝像機的距離排序優先級,並且我們始終會填滿這一緩存。頁存在緩存中有一定滯後量(hysteresis),使其更不容易被釋放(ejected,直譯是彈出)。
在渲染圖中,你可以看到不同的地形塊的精度不同。綠色最高,藍色次之,以此類推。
高度圖超採樣
在項目早期,我們評估了多種超採樣技術,以便我們能在內存中加載整個壓縮後的VHM。
雖然最終VHM還是要被stream加載,但這些調研並不是毫無成果。
所用這項研究使用的高度圖數據均來自美國國家地址調查(United State Geological Survey)主頁。
*後面幾頁是一組超採樣算法的演示。
在這一頁及後幾頁演示的這個算法中,P是一個父級紋素(parent texel),需要通過代碼來預判4個子紋素的高度。
P是ABCD的高度值的平均值。我們始終從A開始重新構建(reconstruct),之後才是BCD。
Simplest image upsampling technique is to assume that child height is the same as parent height. This is our C0 predictor.
最簡單的圖像超採樣技術會假設子節點的高度與父節點相同,這就是我們在C0步驟的預測器。
We can do something a little more complicated for D, now that we know the value of A, B & C.
對於D我們可能經過更復雜的計算,而我們現在知道了ABC的值(等於P)。這項超採樣技術將內存節省至了60%(相對於源數據)。
在C0的基礎上我們把A加入了B,AB加入了C的計算中,不過僅帶來了微不足道(paltry)的1%的內存節省。
C1預測器把相鄰的父節點列入計算考慮,相對源數據把內存節省至了43%。圖中展示了我們重新計算A值的方式。
對於B,我們直接計算相鄰的4個A值的平均。
對於C,我們基於相鄰A和B做平均(如圖)。
對於D,我們使用ABC的均值。而由於C的位置距離D紋素更遠,因此我們本應對其使用更低的計算權重——不過我們目前的實現並沒有這麼做。
我們也實現了C2預測器——它和C1很相似,除了我們會查找更大範圍的相鄰格。因此為了重新構建C2,我們使用了連續曲線(continuous curve)而不是直線。
這個方案在USGS(*前面提到的國家地址調查地圖)地圖上效果很好,但對於我們在遊戲中使用的地圖沒有提供足夠的內存優化程度。
我們也嘗試通過一個簡單的神經網絡(neural network)算法來計算,雖然它比C2預測器效果更好,但開銷也過大。
In the end, we used gradient descent to optimize a 5x5 kernel.
最終我們使用梯度下降算法來作為5X5計算核的優化。(*計算核表示覆蓋的像素範圍)
然而,它比起Neville遞增預測器來說提升也不大——而後者是我們目前使用的算法,因為它不需要機器學習。
無損壓縮
使用預測器(predictor)的目的是為了在不同的紋素上預測計算高度,但我們需要重構建的高度是無損的。
So, we save the delta between the predicted value and the true value in an error stream.
因而,我們計算了預測值和原值之間的差值,存儲在一個誤差數據流中。
得到的誤差數據流非常稀疏(sparse),因而能被簡單的壓縮技術壓縮得很好(壓縮比很高)。加上壓縮這步就是我們對於高度圖的全部處理了。
虛擬高度圖
使用Neville遞增算法,在PS4上解壓縮68X68的頁需要大約1毫秒。顯然我們會重估這一情況,並更傾向於採用C1預測器,因為它對於我們的數據來說更快更省。
邊緣環繞效果
使用UHM或VHM,我們現在能實現邊緣環繞效果(skirts)了。Skirts能使網格與地形無縫融合。
Skirt包含兩個步驟:
- 首先,頂點高度被調整以匹配地形高度。藝術家們能控制在抬高或降低形變(morphing)生效的距離。
- 其次,當為網格著色時,我們可以從虛擬紋理中採樣,來判斷網格上的一個像素點與地形的距離。
在(這一頁)下方的圖中,我們能看到石頭與地形無縫地混合在一起。
Clutter also makes use of the skirt tech. In the top picture, vertex morphing & color blending are disabled. You can see that the grass actually floats above the terrain.
放置地表雜物(Clutter)時我們也使用了skirt技術。在上方的圖中,頂點形變與顏色混合被禁用了,可以看到草地“浮”在地形表面上了。而當頂點形變啟用時,草葉就能正確的融入地形了。
地表雜物染色
地表雜物,例如草,可以被染色以便更無縫地融入下方的地形。
我們使用了一個更低精度的VT用於染色,以獲取顏色的均值。
可以逐實例、逐頂點或逐像素來設置染色值,這都取決於(美術資源)內容製作者的需要。
網格體染色
與雜物染色相關的是網格體染色。通過以特定規則採樣低精度的VT,我們能使同一個網格體(的不同實例)看起來獨一無二,更能融入環境。
右圖中的懸崖就使用了這項技術。
一體化的地形表面
目前我們正在開發新的被稱為一體化地形表面(Unified Terrain Surface)的技術。相比於使用多個地形表面的組合,這個方案使用一個地形表面來覆蓋整個世界。
它能解決我們之前遇到的很多問題,例如當兩個地形表面有著不同的頂點密度,而又是相鄰時——這通常會導致地面破皮或有縫(如圖),而藝術家需要放置網格體來遮瑕。
通過UTS,就不再有這個問題了——通過它我們可以實現一體化的頂點、顏色映射(貼圖)。
*其實不同頂點密度的地形之間也有動態拼合頂點的方案,但也有其侷限,並且很多時候做不到視覺上完全無縫。
虛擬索引與顏色圖
在進行了一體化處理後,我們進入地形虛擬化(virtualization)階段。虛擬化能解除一體化地形的內存限制——而這是之前地圖不能一體化的主要侷限點。
Because the index map cannot be interpolated, as each value is quite literally a terrain layer index, we use point sampling to generate the lower resolution mips
由於索引圖無法被插值,並且每個值都確實是一個地形層的索引,我們使用點採樣來生成低精度的mip。(*一種無插值的模式,一般紋理默認是雙線性或三線性插值)
由於索引圖上的每一個像素都只對應顏色圖上的一個像素,因此對於顏色圖我們也使用點採樣方式。
最終,索引和顏色圖分辨率必須匹配——因為兩者就是一一對應的關係。
虛擬索引圖目前是16bits,但我們計劃將其降至10bits。而虛擬顏色圖使用BC1壓縮格式。
*這裡(包括上篇)中,渲染和數據層的概念詞map很多我沒有直接翻譯成“圖”,以避免進一步和貼圖或紋理混淆——實際上這個數據結構單獨說就是“圖”,而且其實它們的意義是有共性的。很多時候概念的意義是靠上下文來保證的,我相信讀文章多的一定能理解這一點。
虛擬切口圖
在虛擬化索引圖和顏色圖之外,我們也虛擬化了切口圖(cutout map)。
一個切口圖每像素僅需1bit,記錄該像素是否被剔除了。
在視覺上,(切口圖)絕大部分的頁都是白色的,除了少數黑色的頁,以及少數頁混合了兩種顏色的像素。
虛擬切口圖使用了一張間接映射表(indirection table),類似其它虛擬映射結構。
對於純白或純黑的頁,我們只需要使用一個特定的索引值來代表。在緩存中我們只需要放入兩種顏色組合的頁。
數據量已經足夠小,我們都不需要考慮降採樣的問題了。
由於我們最多只能有254個這樣的頁,最差的內存情況下(所有都是兩色的頁)也只需要150KB的內存。而在我們的大地圖上,間接映射表的內存通常小於4KB。
遠景連續UV與VT不混合
Vista UV技術(*上篇介紹的)能使地形在攝像機無論遠近位置都有不錯的視覺。
The issue is that it relies on camera distance. We alpha in macro details and alpha out micro details when the camera moves away from the surface.
(在一體化地形中)它問題是它依賴攝像機的距離。當攝像機遠離一個表面時,我們使宏觀細節淡入、微觀細節淡出。
這麼處理的原因是宏觀細節在遠處能有不錯的視覺貢獻,但在近處會顯得像素化。
通過虛擬紋理,攝像機距離這個參數就成了問題,因為VT技術過程不知道攝像機位置。
我們不得不通過轉義mip級別來模擬出(fake)攝像機距離參數。例如,mip0意味著從最遠5英尺的距離觀看,mip1的距離加倍,以此類推。
It creates a discontinuity in the mip chain of the VT pages, since now, 2 adjacent mips will effectively have slightly different data.
這產生了VT頁中mip鏈的不連續,而由此兩個相鄰的mip會實際上有著輕微的數據差。
雖然在實踐中我們已經能在很多場合克服這個問題了,但我們仍在探索改進這一問題的方案。
*這裡由於沒有配圖,不能確定這種不連續會導致哪種視覺問題。
視差貼圖
對於模擬冰,人們通常會使用一種高性價比的視差貼圖(parallax mapping)技術,它對於模擬視覺深度有著不錯的效果。
視差貼圖目前不被我們的地形系統支持,但我們覺得需要支持這項技術。
*視差和置換的不同是,並不實際改變網格頂點位置。
目前我們正在增加第4個BC1紋理——這會為我們帶來額外的3個通道,這樣一共就有4個可選通道(*前面介紹過4類,只能用於1個通道)。
散射和閃亮參數通常被用於雪——而目前我們兩者只能取其一;相似的,視差通常會被用在冰上——在我們的雪地關卡中。通過引入第4張BC1紋理,這些效果參數就能同時生效了。
生成式貼花
我們也在實驗程序生成貼花的技術,這樣的貼花有著0內存消耗。這能為一些特別單調的地形快速增加一些細節。
動態貼花
Virtual texturing is a static cache, but we believe we could have some amount of dynamicity.
虛擬紋理是一個靜態緩存,但我們相信能有一定程度的動態性(dynamicity)。
一個很好說明這一點的例子就是雪地或沙地的腳印——所有前置技術都齊備了,我們只需要試試重計算虛擬紋理上的一塊區域(作為腳印貼花的範圍)。
大地圖事件
近期我們也增加了觸發大規模事件的功能,以戲劇性地改變地圖的一些區域。對於地形來說,我們希望它的幾何體也能被改變。
在這個例子中,一架飛機墜毀到了地面。使用虛擬化映射的技術,實現這一效果變得輕鬆簡單,因為只有受影響的頁需要被更新。
結語
作為一個協助開發組,分享人所在的工作室提供的這套跨平臺的地形解決方案還是挺驚豔的,考慮了很多方面的具體實現問題。他們在地形的各個層面都大量使用了虛擬紋理技術,在材質混合、地形尺寸、高度圖、貼花等方面都所有突破,有很多領先的“一體化”嘗試,也沒有放棄低端設備上的基礎視覺。
感謝來自High Moon工作室的所有開發者們
最後是資料鏈接:
BC壓縮格式的官方文檔
Large-Scale Terrain Rendering in Call of Duty 的PPTX