什么叫做場景優化?
每一位游戲玩家,無論是傳統的 PC 和主機游戲,還是時新的手游以至 VR 游戲,都會對這樣一個名詞有著特殊的理解和關注,那就是 FPS(幀速率,Frames Per Second)。FPS 太低的話,畫面就一卡一卡的,玩起來不舒服;如果是戴上 VR 頭盔的話,因為暈動癥的影響,玩家甚至會迅速感到頭暈和惡心,一天都無法心情舒暢。
FPS 顧名思義就是每秒鐘渲染的幀數,也就是說,顯卡和顯示器每秒鐘都會更新數十張不同的圖像(幀),進而形成一種動畫的效果。
從傳統動畫的角度上來說,每秒 10 幀以上的畫面就可以形成連續的動畫效果(例如中國古代的走馬燈),而不需要任何交互的電影放映,則采用每秒 25 幀或者接近 30 幀的播放速度,讓觀看者自在地欣賞大片。
但是加入了交互元素之后,例如可以快速轉動視角的鼠標,手柄,以及 VR 頭盔,人們對于幀速率的需求就直線上升了——如果是面對平面液晶顯示屏的話,受限于 IPS 屏幕本身的刷新率特性,內容本身通常需要達到 60FPS 的渲染速度;而戴上 VR 頭盔之后,采用 OLED 屏幕的內容則可以達到 75FPS 的更新,事實上,出于盡量避免暈眩的考慮,游戲內容的畫面刷新也必須達到這個數值。
然而這談何容易:交互游戲并不是預先拍攝好的電影和動畫片。它需要根據玩家的操作來觸發不同的邏輯,并渲染出不同的畫面內容。而游戲場景可能是復雜的,細碎的,并且有很高的真實感和特效方面的要求——而這一切都必須在短短的1/75秒內完成!就算顯示硬件的水準逐年提升,這依然是一個極具挑戰性的話題:如何優化我們的場景和渲染策略,在如此有限的時間要求內,實現高效與細節并存的游戲內容呢?
本文將針對這一話題做適當的討論,然而話題本身的技術含量已是深不見底,所以文章也只能淺嘗輒止,只期望為讀者和同行們小啟一扇門扉。
渲染批次,扼住命運的咽喉
在描述一些晦澀難懂的名詞之前,我們不妨先來看一個例子:
假設我們有一個果園,每天它都要派出一輛卡車,走固定的路線運送水果到市里去。如果運送的水果總量為 10 噸,而這輛卡車一次能夠承載 200 千克水果的話,那么顯而易見它需要跑上 50 趟才能完成這一任務,也許這會花費整整一天的時間才能夠完成。
顯而易見,我們并不希望這項工作花掉那么多的寶貴時間,那么一種直截了當的解決方案是:給這輛卡車換上更為給力的發動機,比如 NVIDIA 的戰術核顯卡,讓它跑全程的速度減半再減半,同樣 50 趟的任務,這回只要一個上午就可以搞定了。
當然生活也許并不總是那么如意的,也許果園的工作人員早就心懷怨氣,每次給卡車只裝了 50 千克水果,于是可憐的司機就需要走上足足 200 個來回,直到別人吃上早飯了還垂死地奔波在路上……
幸好,拯救他的方法也不止一種,比如,裝上戰術核顯卡之后,他至少能夠和別人一樣每天拉完 50 趟再按時回家吃飯了。
但是一個智商正常的果園老板應該不會把這當成是最合理的決策吧……難道不應該先開除那個裝貨的?讓爺的小卡車別這么傻兮兮地跑上 200 個批次嗎?
結束我們的遐想,而現實卻也許傻得有些可愛:當作為內容開發者的我們同樣遇到了“卡車花在路上的時間太長”這種問題的時候,第一選擇往往是換用更逆天的顯卡和系統,而不是好好琢磨一下該死的工作人員藏哪兒了。
沒錯,送了 200 趟水果的卡車,就好比跑了 200 個渲染批次(Draw Call)的顯卡,而每個批次的執行都是會消耗固定時間的。而為了縮短這一時間,進而提升幀速率的開發者們,與其直接買入更新的顯示設備,倒不如先坐下來仔細想一想,渲染批次過多的瓶頸是什么(那個作孽的裝貨工?),我又能把它優化到什么地步(至少恢復到 50 個批次的正常水準?)。而這成百上千噸的水果就好比是復雜和精致的VR場景內容,同一時間內能夠送達的越多,它能夠表達的內容真實感與細節程度也就越詳實。
渲染批次(Draw Call)在以往并不是一個被十分重視的概念,制作游戲場景的美術人員更愿意用“三角面數”(Triangle Face)來描述內容的復雜程度或者制作水準,譬如:這是一個足有 1000 萬面的卡通少女(她的每一根毛發也許都清晰可辨),又或者這是一個只有 100 萬面的故宮實景模型(然而我的高超技巧讓這一藝術瑰寶依然栩栩如生)。
殊不知,對于實時渲染而言,三角面數并不足以決定渲染的效率以及幀速率結果。僅用了一個批次就完成渲染的 1000 萬面模型,和用了 1 萬個批次才渲染完成的 100 萬面模型,其執行效率恐怕是天壤之別。后者在實際執行當中的表現,恐怕一定會讓那些自信于低面數模型的人們大跌眼鏡。
然而這就引起了另一個有趣的論題:為什么會有大量的 Draw Call 呢?既然每一位開發者都能明白這樣的道理,為什么不一開始就設計成一次 Draw Call 執行全部場景物體的渲染操作呢?就算是因為承載力的問題不得不分成多個 Draw Call,這種簡單粗暴的設計依然應當是最優的解法無疑吧?
然而這樣的愿望往往無力成為現實,因為現代圖形渲染底層接口對于 Draw Call 的實際執行,是在繪制實際幾何體圖元(Draw Primitive)的階段;而每次繪制圖元的操作之前,我們只能為這組圖元(可能是 200 個三角面,也可能是 10 萬個三角面)設置一張紋理圖像(Texture),以及一組著色器(Shader)——而這兩者正是虛擬現實和游戲應用當中用于表達物體材質和真實感的最核心組件。一張圖像能夠容納和清晰地表達一個精致美女的肌膚,秀發,慧眼,紅唇,絲衣,高跟鞋,LV包,以及其他種種細致入微的內容嗎?當然不能。所以我們也沒有辦法在一個渲染批次里搞定美女模型的一切。
而與此相關的另一個問題也會困擾著更深層次的圖形開發者:場景內容的管理。例如一座數字化的虛擬城市,每一棟樓,每一個房間,每一輛車,每一位居民,都應當是獨立存在的個體,對這些模型資源的管理也顯然應當按照相似的分類方法。然而從渲染的角度來說,這樣產生的 Draw Call 恐怕遠遠不是最優的選擇,甚至輕而易舉就會讓前文中送水果的卡車司機崩潰掉。
如果按照材質把模型重新分組和整合,倒是可以進一步提升渲染的效率,不過資源的管理工作卻無疑會讓人崩潰。試想一下,在公安局的戶口本上,你不再屬于某個家庭,而你身體的各部分則分別隸屬于短發組,麻臉組,格子衫組,灰褲子組……這種“按材質分組”的行為看起來多少有點“漢尼拔”似的驚悚氣氛了。
然而,場景優化面臨的麻煩還遠沒有盡頭。
有關填充率的那些傳說
試想一位畫家,揮毫潑墨,將現實中的山水人物躍然紙上,描繪得栩栩如生。這些山水人物原本是自然存在的,繪制到紙上,就成了油彩,湊近去看,就會有濃重的顆粒感——雖然大多數情況下這并不妨礙我們觀瞻就是了。
現代計算機的圖形渲染過程與此類同。把復雜的場景模型躍然于屏幕之上,這一過程稱作光柵化(Rasterization),而屏幕上的像素點,近看起來同樣存在顆粒感,低分辨率的屏幕則更為明顯。這種顆粒感對于VR類的內容來說更為顯著(因為 VR 眼鏡相當于放大了屏幕分辨率對畫面質量的影響),而它也是破壞場景真實感和效果的主因之一。
那好辦啊,有人可能會說,那就拼命增加屏幕分辨率不就好了。從現在的 1080p,到 2K,到 4K,到 8K……總會有徹底解決這個問題的一天吧?
然而事情并沒有這么簡單,還是回過頭來談談我們的畫家:在一張 A4 紙上畫簡筆畫,也許他只需要 1-2 分鐘而已;如果是 10 米長卷,那么也許要一天的時間;如果是在萬里長城上……那么畫家可能直接就跳下去了,搞這么一輩子的工程,生不如死啊。
沒錯,這里的畫卷可以類比為我們所說的屏幕分辨率,而畫家求死的原因,只因為要畫的東西太多,而他對畫卷內容的填充率(Fill Rate)太低了,因而渲染效率也變得慘不忍睹。
這個問題的解決方案,無非三種,弊端也是一目了然:
1、改用小點的畫卷(降低屏幕分辨率和用戶體驗);
2、換個瘋狂的畫家(升級硬件,提升填充率);
3、少畫點花里胡哨的東西(降低渲染內容的質量)。
聽起來都不是什么一勞永逸的選擇,并且大多數開發者一定會選用最直接的那個方案,沒錯,換更瘋狂的畫家,搞硬件的軍備競賽。
不過從場景優化的角度來說,方案三反而成了最靠譜的一條道路,并且這給各路算法豪俠和數學家們也提出了一個有趣的命題:如何在渲染質量還看得過去的前提下,盡量少畫點“對用戶沒用”的內容呢?
對這句話的詳細解釋就是:用戶看不到的場景不要畫出來,把它提前裁減掉(Culling);而用戶可能本來也看不清的場景,就用更低的細節程度(Level of Details)把它畫出來。
這里必須要解釋一句。顯卡并不是多么聰明的一種硬件產品,它并不能主動分辨出當前提交的渲染指令中,包含的信息是否真的能夠被顯示到屏幕之上;而是選擇了另一種更為簡單的策略:不管有多少東西都先畫上去,如果不幸沒畫到紙上的話……反正你也不在乎對不對?畢竟最終用戶關注的只有紙面上的內容而已。
然而古今中外,那些為了優化場景而苦思冥想的開發者們,卻僅為了這一個目標前赴后繼,傷痕累累。
當然,這里所說到的“填充率”一詞,只是相關圖形系統運行機制的冰山一角。它還可能被進一步細分為像素填充率(光柵化操作和屏幕緩存繪制的速率)和紋理填充率(紋理在模型表面映射和采樣操作的速率)。而實際執行過程中,還可能受到顯存帶寬(顯卡在單位時間能夠傳輸數據的總量)參數的影響,而這些信息往往都會標識在具體顯卡品牌的性能說明文檔中,作為發燒友比較和購買的依據(雖然它們實際上并無統一標準可言)。
不過這并非本文所要繼續深入闡述的命題了,我們關心的,還是那些圖形學和 VR 領域的先行者們,為了哪怕一點點的優化效果做出過的努力。
聯系方式:0755-81699111
課程網址: http://www.vrkuo.com/course/vr.html