虛擬紋理緩存
還記得我提到過實體圖形不支持虛擬紋理嗎?好吧,似乎《城市天際線2》實現(xiàn)了自己的虛擬紋理/紋理流系統(tǒng)。我一開始認(rèn)為這款游戲使用了Unity的內(nèi)置解決方案,但在傳統(tǒng)的Unity模式下,盡管它在2020年被收購后被添加到引擎中,但它仍然是實驗性的,不受支持。
什么是虛擬紋理?我的理解是,虛擬紋理是一種加載和管理紋理數(shù)據(jù)的方法,比傳統(tǒng)的每個紋理資產(chǎn)使用一個GPU紋理的方法更具內(nèi)存效率。紋理存儲在紋理圖集中,這基本上是更花哨的版本的精靈表。貼圖集由固定大小的貼圖組成,每個貼圖可以包含一個或多個紋理。節(jié)省內(nèi)存的技巧是,大的紋理可以分成多個貼圖,所以如果你有一個大的紋理,只在屏幕的一小部分可見,你只需要加載實際可見的貼圖。虛擬紋理可見性信息是在稍后的通道中作為正常渲染的副產(chǎn)品產(chǎn)生的,并且可見性信息用于CPU端來確定哪些貼圖需要加載,哪些可以卸載。如果你想進(jìn)一步了解,虛幻引擎的文檔似乎提供了更詳細(xì)的技術(shù)描述。除了地形外,游戲似乎對所有靜態(tài)3D物體都使用了虛擬紋理。
在我的實例幀的渲染中使用的貼圖地圖集之一。大規(guī)??s減了尺寸,原始尺寸為16368×8448。
這種紋理方法在理論上是相當(dāng)優(yōu)雅的,但它伴隨著許多權(quán)衡,并且游戲的執(zhí)行仍然存在一些初期問題,例如高分辨率紋理有時無法加載,即使表面已經(jīng)十分靠近相機了。虛擬紋理的使用也可能是游戲缺乏對各向異性紋理過濾支持的罪魁禍?zhǔn)?,各向異性紋理過濾是千禧年以來PC游戲的標(biāo)準(zhǔn)功能。
天空盒的生成
游戲使用Unity HDRP內(nèi)置的天空系統(tǒng),所以它每幀生成一個天空盒紋理(一個立方體貼圖)。這大約需要0.65毫秒,這與其他內(nèi)容相比并不算多,但如果游戲的目標(biāo)是60 FPS,那么它將占總幀時間預(yù)算的4%。
Pre-pass
來自Renderdoc的截圖,顯示了我的示例幀的正常/粗糙度緩沖。
現(xiàn)在我們開始實際的渲染?!冻鞘校禾祀H線2》使用延遲渲染,這基本上意味著渲染是在許多階段完成的,并使用幾個不同的中間渲染目標(biāo)。第一階段是預(yù)通,它把每像素深度,法線和平滑信息分成了兩個單獨的紋理。
這個過程非常耗能,因為它需要大約8.2毫秒來生成,這就是游戲渲染開始出現(xiàn)最大問題的地方。但首先我們需要談?wù)勓例X。
牙齒之爭
關(guān)于《城市:天際線2》的表現(xiàn),一個奇怪而流行的觀點是,角色模型有完整的牙齒模型,盡管在游戲中根本沒有辦法看到它們,除非我們使用照片模式并將相機剪切到角色的頭部。Reddit用戶Hexcoder0使用NVidia Nsight Graphics進(jìn)行了一些挖掘,并將他們的發(fā)現(xiàn)發(fā)布在Reddit上(這也啟發(fā)了我自己進(jìn)行研究并撰寫這篇毫無意義的長篇文章)。據(jù)透露,游戲角色不僅有完整的牙齒模型,而且它們一直以最高質(zhì)量渲染。更重要的是,與角色相關(guān)的所有內(nèi)容都是如此:所有角色網(wǎng)格都沒有任何LOD。Colossal Order很快就公開承認(rèn)了這一點,他們甚至提到了LOD處理中其它的問題。
Colossal Order還告訴我們,他們正在使用名為Didimo Popul8的中間件來生成角色模型。如果我沒記錯的話,關(guān)于牙齒的爭論甚至在游戲發(fā)布之前就開始了,當(dāng)時有人注意到Didimo角色規(guī)范中包含了牙齒和睫毛等單獨的網(wǎng)格。我最初認(rèn)為游戲是使用Didimo的默認(rèn)角色網(wǎng)格——因為老實說,他們看起來非常普通和沒有靈魂,但現(xiàn)在我不那么確定了。
游戲中的網(wǎng)格實際上比Didimo的默認(rèn)網(wǎng)格有更多的多邊形:例如臭名昭著的嘴/牙齒模型由6108個頂點組成,遠(yuǎn)遠(yuǎn)超過默認(rèn)網(wǎng)格的1060個頂點。甚至在我們添加頭發(fā),衣服和配飾之前,一個角色大約有56000個頂點,這是很多的。就環(huán)境而言,在添加庭院道具和其他細(xì)節(jié)之前,平均低密度住宅建筑使用的頂點少于1萬個。
在這個示例幀中,游戲渲染了13組牙齒,它們對幀的視覺影響為零:沒有一個像素受到影響。即使是角色本身,除了噪音和偽影,對畫面也幾乎沒有貢獻(xiàn)。
過度未優(yōu)化的角色模型并不是游戲表現(xiàn)不佳的唯一原因(因為問題從來都不是那么簡單),但它們指向了是游戲在資產(chǎn)和渲染方面的更廣泛的問題。游戲經(jīng)常使用太多多邊形繪制太多對象,但對最終圖像幾乎沒有影響。這并不是特定于預(yù)通階段,因為同樣的問題似乎會影響所有柵格化幾何圖形的渲染通道。我認(rèn)為這主要有兩個原因:
有些模型根本沒有任何LOD變體。
游戲的篩選系統(tǒng)不是很先進(jìn):自定義渲染代碼只實現(xiàn)了截錐體遮蔽,完全沒有遮擋遮蔽的跡象。有一些基于距離的遮蔽,但不是很激進(jìn),這對于避免畫面閃爍很好,但對性能很不利。
這里有一些除了角色模型之外的其他例子。
這些密集的晾衣繩由25000個頂點組成,每條都有幾十個獨立模型的晾衣夾。甚至還有一個更密集的版本,具有超過3萬個頂點。
從技術(shù)上講,這個停車場網(wǎng)格沒有在我的示例幀的預(yù)通道中使用,但它仍然存在于場景中,后來在陰影映射通道中使用。這個網(wǎng)格由超過四萬個頂點組成,沒有LOD,并且具有大多數(shù)AAA游戲中都沒有的奢華細(xì)節(jié),例如連接屏幕和鍵盤的單獨建模電纜。它們甚至可以從桌子上的一個相對較圓的洞里穿過去!將建筑和家具合并到一個網(wǎng)格中可以節(jié)省繪制調(diào)用,但這也意味著道具不能單獨遮蔽。
這種由一堆原木組成的網(wǎng)格類似地只用于陰影渲染通道,并且具有超過10萬個頂點。這是迄今為止我在游戲中遇到的最高的多邊形模型,盡管我只玩了幾個小時。
現(xiàn)在你可能會說這些只是吹毛求疵的例子,而且現(xiàn)代硬件可以很好地處理這些模型。你大致上是正確的,但問題是所有這些相對較小的成本會逐漸積累,特別是在一個城市建造游戲中,一個未優(yōu)化的模型可能會在一個幀中渲染幾百次。無論硬件是否能夠處理它,在每幀每實例中對成千上萬個多邊形進(jìn)行柵格化并且不影響單個像素都是一種浪費。幸運的是,這些問題很容易解決,既可以通過創(chuàng)建更多的LOD變體,也可以通過改進(jìn)遮蔽系統(tǒng)來解決。但這需要一些時間,CO和P社是否愿意投入時間還有待觀察,特別是如果這涉及到檢查大部分游戲資產(chǎn)并逐一修復(fù)它們的話。
需要明確的是,擁有非常詳細(xì)的模型本身并不是問題,特別是如果你打算創(chuàng)造一款自稱為“次世代城市建造游戲”的話。問題在于,游戲很難處理這種級別的細(xì)節(jié),而且多邊形的使用效率低下且不一致。每個具有豐富建模的的角色模型,都可以被低多邊形的模型給替代。我認(rèn)為如果游戲運行良好,人們將會大肆宣揚這些非常細(xì)致的模型,并在社交媒體上發(fā)表那些標(biāo)題黨帖子,比如“天哪,開發(fā)者想到了一切”和“我不敢相信他們模擬了停車場的電纜”和“城市天線2最精細(xì)的游戲?”的標(biāo)題視頻。但如今,我們面臨的情況卻又如此糟糕。
讓我們回到正題。
運動矢量
游戲渲染每像素的運動矢量作為一個單獨的通道,這可以用于抗混疊和運動模糊。我認(rèn)為現(xiàn)在的運動矢量有點運作不良,這也是游戲在撰寫本文時不支持DLSS或FSR2的原因。在高級設(shè)置菜單中隱藏了一個臨時抗鋸齒選項,它在一定程度上提高了渲染質(zhì)量,但是使用頂點著色器的動畫,比如樹,只是被偽影和鬼影覆蓋了。
這個過程大約需要0.6毫秒。
道路和貼片
現(xiàn)在我們終于渲染了一些能認(rèn)出來的東西:道路,還有草坪,以及其他沿著地形表面延伸的東西。
這個過程大約需要1毫秒。
主要通道
這是延遲渲染過程中的主要過程。這個通道將所有中間渲染目標(biāo)與虛擬紋理緩存和一些看似硬編碼的紋理一起產(chǎn)生,并產(chǎn)生更多的緩沖區(qū),包括反射率,法線,不同的PBR屬性和深度。它還生成我之前提到的虛擬紋理可見性信息。它以半水平分辨率呈現(xiàn),可能是一種優(yōu)化。地形不使用虛擬紋理,所以它以全分辨率和恒定的顏色渲染,而不考慮實際地形紋理。
虛擬紋理可見性緩沖區(qū)
這個過程需要16.7毫秒,如果我們的目標(biāo)是每秒60幀,那么他花掉了整個幀的預(yù)算。這個通道再次柵格化了所有的幾何圖形,所以使前置通道變慢的原因也適用于這里。額外的成本可能是由于額外輸出的數(shù)量,加上虛擬紋理緩存查找和紋理映射本身的成本。
環(huán)境光遮蔽
接下來,游戲?qū)⑹褂眠\動向量、法線和深度緩沖以及前一幀的最后兩個副本來生成環(huán)境光遮蔽緩沖。根據(jù)著色器的調(diào)試名稱判斷,算法是GTAO。這大約需要1.6毫秒。
級聯(lián)陰影映射
《城市:天際線2》使用了層疊陰影貼圖,在我看來效果不是很好。陰影中充滿了偽影,尤其是當(dāng)太陽或任何樹葉移動時(它們一直都在移動),陰影會不斷閃爍。即使屏幕沒有完全被偽影覆蓋,陰影的分辨率也相當(dāng)?shù)?,不同陰影級?lián)之間的質(zhì)量差異也非常明顯。
游戲使用四個級聯(lián),每個級聯(lián)的分辨率為2048×2048像素。在高級圖形設(shè)置菜單中有一個方向陰影地圖分辨率設(shè)置,但在本文撰寫時,它沒有連接到代碼中的任何內(nèi)容;無論是單個設(shè)置還是整體陰影質(zhì)量設(shè)置都不會改變陰影貼圖的分辨率。這就是為什么中陰影和高陰影設(shè)置預(yù)設(shè)是完全相同的原因。我不知道這是一個疏忽,還是因為它引起了問題而匆忙禁用了設(shè)置。低預(yù)設(shè)不同于中高預(yù)設(shè),因為它消除了地形投射的陰影。
盡管質(zhì)量較低,但它是迄今為止最慢的渲染過程,大約需要40毫秒或幾乎占總幀時間的一半。就繪制調(diào)用的數(shù)量而言,它也使所有其他通道相形見絀:在我的測試幀中,6705個繪制調(diào)用中有4828個用于陰影映射,這一比例達(dá)到了驚人的72%。這就是為什么當(dāng)禁用陰影時,性能會有如此巨大的提升。
這個通道之所以如此緩慢,背后的原因與前置通道和主通道是一樣的:太多不必要的幾何渲染和太多的渲染調(diào)用。Renderdoc的性能計數(shù)器視圖表明,許多渲染調(diào)用在陰影地圖中造成的影響在0到100像素之間,并且牙齒又回來了。這款游戲似乎將每個3D對象視為潛在的陰影對象,無論大小或距離如何。這里有很多優(yōu)化的空間,理論上對LOD和遮擋的一般改進(jìn)應(yīng)該對陰影映射性能也有很大的影響。希望在性能得到改善后,CO或民間Mod玩家可以將陰影質(zhì)量設(shè)置恢復(fù),并將陰影分辨率提高到2023年的水準(zhǔn)。
讓我們以積極的一面來結(jié)束這部分內(nèi)容:在深入研究陰影處理代碼時,我偶然發(fā)現(xiàn)游戲會使用當(dāng)前日期、時間和城市坐標(biāo)來計算太陽和月亮的位置。這是一個非常有趣的細(xì)節(jié)!
屏幕空間反射和全局照明
游戲使用Unity HDRP內(nèi)置的屏幕空間反射(SSR)和屏幕空間全局照明(SSGI)進(jìn)行實現(xiàn)。我不會詳細(xì)介紹它們,因為Unity的文檔已經(jīng)相當(dāng)全面了,而且我也不會假裝自己完全理解它們。全局照明使用ray-marching,默認(rèn)以半分辨率進(jìn)行評估。采用去噪和時間積累技術(shù)提高圖像質(zhì)量。如果這款自詡為次世代城市建造的游戲除了支持這些屏幕空間解決方案外,還支持硬件加速光線追蹤,那就再好不過了,但我并不抱太大希望。
延遲照明
這是最終的產(chǎn)物。到目前為止產(chǎn)生的大多數(shù)中間緩沖都被組合起來渲染接近最終的圖像。關(guān)于這個通道沒有太多可說的,除了它大約需要2.1毫秒。
奇怪的衣服通道
有一個小的渲染通道只是為渲染Didimo角色的衣服,在這種情況下,有著3件連衣裙,1件連體衣和1套西裝褲子。剩下的8個角色要么赤身裸體,要么他們的衣服使用不同的著色器。此通道在此縮放級別下幾乎不影響像素。幸運的是,這只需要0.2毫秒。
天空渲染
接下來使用之前生成的天空盒紋理渲染天空,盡管它在我的示例幀中不可見。這個過程大約需要0.3毫秒。
透明對象預(yù)通道
傳統(tǒng)的延遲渲染不適用于透明對象,因此它們是單獨渲染的。透明對象的渲染分兩個階段,從這個預(yù)通道開始,它只更新法線和深度緩沖區(qū)。這一幀中沒有很多獨特的透明對象,所以這個過程大約需要0.12毫秒。
水體渲染
游戲在計算著色器中做一些預(yù)處理,為水渲染做準(zhǔn)備,然后在產(chǎn)生幾個降格和模糊的版本,應(yīng)用到最終的圖像中。這些輸入被饋送到渲染水面的主水渲染著色器。這大約需要1毫秒。
粒子、雨和透明物體
這個通道處理大多數(shù)透明的東西,包括粒子、天氣效果和由玻璃和其他透明材料制成的3D物體。畫面中沒有可見的顆粒,但游戲仍然試圖渲染工業(yè)區(qū)煙囪里的煙霧,以及污水管道產(chǎn)生的泥漿流。接下來渲染雨水,這部分使用20個實例,每個實例有1.2萬個頂點。有趣的是,剩下的透明物體是在下雨后渲染的,因此當(dāng)透明物體(如溫室和電線)和雨重疊時,會產(chǎn)生一些奇怪的現(xiàn)象。所有這些大約需要0.56毫秒。
虛擬紋理反饋處理
我們之前得到的虛擬紋理可見性緩沖是用計算著色器處理的,結(jié)果輸出紋理是原始分辨率的1/16。為了展示,我將最近鄰的輸出縮放為8倍,這樣更容易讀。這是游戲最終從GPU獲得的信息,用于決定加載和卸載哪些紋理貼圖。Renderdoc報告在這上面花費的時間很少,不到0.1毫秒。
一些后處理
這款游戲使用了許多Unity內(nèi)置的后期處理效果,包括時間AA(就像我之前提到的,非常崩壞),bloom和色調(diào)映射,以及DOF和動態(tài)模糊(如果啟用)。我懶得總結(jié)所有這些效果,但總共花費大約是1到2毫秒。
輪廓,文本和其他UI
最后剩下的渲染調(diào)度用于渲染所有不同的UI元素,既包括繪制到世界中的元素,也包括更傳統(tǒng)的UI元素,如底部欄和其他控件。許多渲染調(diào)度用于Gameface的UI元素,盡管最終這些調(diào)度與渲染過程的其余部分相比非???。道路名稱使用2D符號距離字段渲染到場景中。如果文本位于建筑物或其他物體的后面,游戲會使用深度緩沖將文本與場景混合,這是一個很好的設(shè)計。這最后一個通道花費的時間較少。
至此就大功告成了!
我試著不把它變成深入的圖像研究,但我想我失敗了。希望你學(xué)到了一些新東西。
總結(jié)
那么為什么《城市:天際線2》對GPU的要求如此之高呢?簡而言之,游戲在顯卡上投放了太多不必要的幾何圖形,以至于游戲在很大程度上受到可用的柵格化性能的限制。造成不必要幾何體的原因是許多游戲網(wǎng)格缺乏簡化的LOD變體,以及過于簡單和看似未調(diào)整的遮蔽執(zhí)行。游戲之所以使用自己的遮蔽執(zhí)行而不是使用Unity的內(nèi)置解決方案(理論上應(yīng)該更先進(jìn))是因為Colossal Order必須自己執(zhí)行許多圖像方面的內(nèi)容,因為Unity在DOTS和HDRP之間的整合仍處于開發(fā)階段,并且可能不適合大多數(shù)實際游戲。同樣地,Unity的虛擬紋理解決方案仍然處于測試階段,所以Colossal Order也必須執(zhí)行他們自己的解決方案,這仍然存在一些初期問題。
以下是我認(rèn)為發(fā)生的事情:Colossal Order在Unity的新技術(shù)上進(jìn)行了賭博,在某些方面它獲得了巨大的回報,但在其他方面卻給他們帶來了很多麻煩。這在軟件開發(fā)中并不罕見,我自己作為一名學(xué)習(xí)網(wǎng)絡(luò)的開發(fā)人員,在日常工作中也經(jīng)歷過這種情況。他們選擇DOTS作為架構(gòu)來修復(fù)之前游戲所遭遇的CPU瓶頸,并增加模擬的規(guī)模和深度,并且在這方面取得了很大的成功。Colossal Order在DOTS還處于實驗階段時便開始了這款游戲,并且即使在DOTS正式被認(rèn)為可以投入生產(chǎn)時,他們也不得不自己執(zhí)行這么多內(nèi)容。如果他們一開始使用實體圖像,但當(dāng)他們意識到Unity的官方解決方案無法解決問題時,他們不得不轉(zhuǎn)向自定義解決方案,如遮蔽、骨骼動畫、紋理流等,我不會對此感到驚訝。最終,由于資金和發(fā)行商的壓力,游戲不得不在這些系統(tǒng)尚未完善時過早發(fā)布。這些技術(shù)問題在發(fā)布當(dāng)天對開發(fā)者來說都不是什么新聞。他們一開始就宣稱游戲的目標(biāo)是30 FPS,這十分令人難以置信——自21世紀(jì)初以來,沒有一款純PC游戲只有30幀的,哪怕圖像規(guī)格再高也不應(yīng)如此。
雖然我確實發(fā)現(xiàn)了很多關(guān)于游戲技術(shù)的抱怨,也讓我在過去的1.5周內(nèi)耗費了我大量空閑時間進(jìn)行調(diào)查,但這個過程也讓我欣賞了這款游戲的崇高目標(biāo),并更加同情這款在技術(shù)上雄心勃勃但卻陷入困境的游戲的開發(fā)者。我已經(jīng)學(xué)到了很多關(guān)于《城市:天際線2》和Unity HDRP的工作原理,我也得到了一些RenderDoc上很好的實踐。