研發日誌技術篇(上)——如何死磕GPU

遊資網發表於2022-12-12
大家好,我們是國產科幻生存RTS《異星前哨》專案組成員,是一群熱愛RTS的老炮兒,在遊戲12月16日將於Steam等平臺發售之際,我們決定把開發過程中遇到的一些經驗積累分享給大家。希望給國內遊戲的開發環境提供一些微末幫助。

研發日誌技術篇(上)——如何死磕GPU

鑑於我們的遊戲型別是生存RTS,內容中包括人類與數量巨大的蟲潮展開激烈對抗,所以多單位作戰是我們要考慮的重要技術點之一。把它繼續拆解後,多少單位作戰,螢幕中可擁有多少單位等問題浮現在我們眼前。

最終,為了使《異星前哨》在市場上更有競爭力,製作人設定了“同屏十萬只怪”這個看起來比較瘋狂的目標。如果把這個S級的技術難點攻破,其它問題應該就也能攻破了。

十萬只怪!技術組開了一禮拜的會,要達成這個目標,我們得面臨CPU+GPU(圖形處理器,顯示卡的核心)的雙重壓力。因為海量物體的繪製,需要GPU承擔。這些物體的尋路、戰鬥、AI等行為又需要CPU的計算,這幾乎是一個極限挑戰。

儘管在公司的另一個專案《鐵甲雄兵》已經實現了同屏數百人戰鬥的大戰場,但同屏十萬的難度跨越著實有點大,經過了對各類效能問題漫長的死磕,初步總結了一些方法,本篇主要從GPU角度的兩個主要方案來談一下。

一、GPU大批次動畫最佳化渲染技術

由於遊戲中存在海量怪物,每隻怪物都需要播放各種動畫,勢必會對執行效率產生嚴重影響,所以需要一種新的動畫渲染技術,能夠承受海量的物體。

研發日誌技術篇(上)——如何死磕GPU

現代顯示卡的效能增長非常快,瓶頸逐步變成GPU和CPU之間的資料同步,每一次同步都需要互相等待,雙方浪費大量時間。之前的很多遊戲包括手遊,都把減少drawcall作為一個重要最佳化手段,因為每一次drawcall都會導致一次CPU上傳資料+GPU繪製的同步。這裡要注意的是,並不是說drawcall本身效率很低,而是常規drawcall導致的資料同步效率極低,當然drawcall本身也是有開銷的,能減少是最好。

因此這次的最佳化方向是,減少CPU與GPU的資料同步。怪物儘可能由GPU繪製,最大可能減少資料同步,以達到最高非同步效率。知易行難,定了方向但是還有很多技術細節需要克服。

傳統骨骼動畫計算(CPU計算骨架+GPU計算蒙皮)

研發日誌技術篇(上)——如何死磕GPU

傳統的做法是CPU按照動畫序列計算當前各骨架位置(BoneMap),然後把BoneMap傳遞給GPU,GPU透過VertexShader,結合各mesh頂點資料,對應BoneMap資料以及骨骼權重等進行插值計算,得到新頂點位置。傳統動畫處理由於需要CPU+GPU兩個步驟順序進行計算,每個動畫物體需要每次單獨呼叫繪製,同時獨立物體動畫計算時需要在Drawcall時傳遞不同材質引數,不利於合批高效繪製,另外CPU與GPU之間頻繁進行資料互動也會產生很大開銷,所以我們必須換一種方式。

貼圖骨骼動畫

研發日誌技術篇(上)——如何死磕GPU

為了將計算挪入GPU,必須把相關資料存入視訊記憶體供GPU讀取,首先是美術輸出的骨骼動畫資訊,我們選擇將各骨骼運動的動畫幀變化存到貼圖RGBA Float格式中作為貼圖骨骼動畫檔案,在VertexShader中透過函式tex2dlod採點獲取貼圖顏色數值,透過將顏色數值轉換為變換矩陣,從而與本地mesh頂點等資訊,產生動畫變換。(注:在材質設定貼圖取樣模式時選用point,避免貼圖取樣受其他模式干擾)

研發日誌技術篇(上)——如何死磕GPU

其次,遊戲執行時,每隻怪物都有自己的當前動畫資訊,這些資訊也需要同步給GPU,考慮後續的批次繪製等最佳化技術,我們還是選擇貼圖做為載體。先將各動畫集如idle,run,attack轉換為貼圖動畫紋理,在將這些貼圖動畫作為一個材質的各貼圖通道。

另外由於批次繪製中各個角色播放的動畫第幾幀以及播放哪個動畫集不確定,所以在呼叫draw call前,透過CPU計算各個單位動畫系統得到一張全單位mask圖,圖上每個畫素代表一個單位,一個畫素RGBA資訊記錄,該單位播放哪個動畫集以及播放第幾幀。這樣,一張貼圖就包含了所有單位的實時狀態資訊,可以直接傳輸給GPU,只有一次資料同步。

總之,就是想辦法把骨骼計算壓力轉給GPU,把CPU閒出來。

大批次例項化繪製

普通drawcall繪製函式一次只能繪製當前的一個mesh,而繪圖系統還提供了批次繪製的介面,可以繪製一個mesh多次,出現在不同位置座標。函式型別形如:

void DrawMeshInstanced(Mesh mesh, int submeshIndex, Material material, List<Matrix4x4> matrices);

這個函式一般只能拿來加速靜態物體,比如場景裡的石頭之類。因為動態物體有動畫,幾個動態物體很難完全一致。

研發日誌技術篇(上)——如何死磕GPU

我們發現由於遊戲裡怪物數量非常多,總有一些怪物的動畫幀當前相同,正好使用這個函式進行加速。舉個例子,最常見的怪物進攻,假設移動動畫長2秒,當前執行速度每秒60幀,無論多少隻怪物,理論上最多呼叫60x2=120次就可以畫完全部怪物。因為怪物最多隻有120種狀態。當然實際情況更復雜,因為每臺機器幀率都不一樣。

動畫物體大批次例項化繪製

要使用動畫物體大批次例項化繪製介面,首先要使多單位物體材質合批繪製,另外為了提高骨骼動畫效率採用貼圖動畫技術,因此最終方法為:

研發日誌技術篇(上)——如何死磕GPU

效果展示:

研發日誌技術篇(上)——如何死磕GPU

二、大批次物體陰影最佳化渲染技術

陰影在遊戲效果中能體現物體的更真實表現,在實際遊戲中由於單位眾多,每個單位所需陰影效果也是需要一種最佳化渲染技術。

傳統陰影功能

在傳統繪製陰影功能中,需要將產生陰影的物體額外繪製一遍ShadowMapDepth,最後將所生成的ShadowMap與畫面中的接收陰影物體比較深度從而確定是否繪製陰影著色。

在多單位的情況下這裡的額外繪製ShadowMapDepth處理成為影響效率的瓶頸,如何降低這個Pass繪製次數成為了關鍵問題所在。

批次化GPU陰影技術

首先按照螢幕大小建立一個RT,控制渲染次序在場景不透明物體渲染之後,獲取當前場景繪製完的深度圖,呼叫例項化繪製介面繪製多單位陰影,將深度圖等資訊傳入所需大批次單位繪製ShadowCasterPass,從而在RT中獲得當前螢幕內多單位最終陰影效果貼圖,將該RT貼圖與螢幕當前畫素合併從而實現陰影最終效果。

研發日誌技術篇(上)——如何死磕GPU

進一步改進,透過在繪製單位中預留代理陰影體,在VertexShader階段中做動態切換本體頂點或代理陰影體頂點,切換最終哪些頂點光柵化在pixel shader著色計算陰影提高效率。(調Shader可真是個體力活)

關於GPU的分享就到這裡了,“同屏十萬只怪”最終呈現效果如何,可關注《異星前哨》相關動態,遊戲將於2022.12.16正式發售,售價68元,Steam與WeGame首周九折,一次買斷!

關於遊戲:

《異星前哨》是一款融合即時戰略的國產科幻生存RTS,不同於傳統RTS遊戲,本作捨棄了PVP對戰,將核心玩法聚焦於生存模擬。玩家將成為探索外星的領導者,建立基地並抵禦外星蟲潮的攻勢。遊戲首創英雄體系融入核心玩法,每個英雄擁有專屬技能、兵種以及建築單位等;數萬蟲潮同屏效果震撼,可暫停和隨時存檔。誠邀各位體驗!

研發日誌技術篇(上)——如何死磕GPU

相關文章