Unity3D遊戲場景優化之批處理

2021-07-28 23:56:22 字數 3969 閱讀 7893

目錄(?)

[+]

喜歡我的部落格請記住我的名字:秦元培,我的部落格位址是:

最近開始研究unity3d遊戲場景優化,每次提及遊戲優化這個話題的時候,我的腦海中都會浮現出《仙劍奇俠傳六》這個讓四路泰坦都光榮隕落的神奇遊戲,作為乙個使用unity3d引擎進行遊戲開發的仙劍玩家,我曾經天真的以為,這款使用unity3d引擎打造的仙劍二十周年獻禮之作,會讓我對《仙劍奇俠傳》這個系列遊戲的未來充滿更多期待,然而當遊戲真正呈現在我眼前的時候,我感受到了在歷代仙劍遊戲中從未有過的尷尬和失望,我尷尬的是unity3d這樣乙個比較強大的遊戲引擎硬生生地被北軟玩成了這個鬼樣子,我失望的是這部遊戲除了劇情和跳跳樂以外並沒有什麼讓人看到希望的東西。

我們知道unity3d在螢幕上繪製乙個圖形本質上呼叫opnegl或者directx這樣的api,因此在這個過程中會產生一定程度上的效能消耗。drawcall是opengl中描述繪製次數的乙個量,例如乙個基本的opengl繪製流程是設定顏色->繪圖方式->頂點座標->繪製->結束,在繪製的過程中每幀都會重複這個過程,這就是一次drawcall,所以當遊戲中的繪製過程變得複雜的時候,就會帶來drawcall的急劇增加,進而帶來遊戲的效能問題,反映到遊戲表現上就變成了優化問題。那麼在unity3d中採取了什麼樣的措施來降低drawcall呢?這就是我們今天要說的批處理,換句話說unity3d使用了批處理來達到降低drawcall的目的,批處理希望通過對物體網格的重組來獲得更高的繪製效率,試想以下如果將多個物體合併為乙個物體,那麼在繪製的時候只需要繪製一次就夠了,因此從這個角度上來講這樣做肯定是可以降低drawcall的,更深刻的一種理解是這裡體現了一種資源迴圈呼叫的思想,接觸過android開發的朋友們一定知道listview控制項可以對其元素進行「快取」從而提高效率,因為我們可以發現其實listview是對列表項進行某種程度上的「復用」從而提高了效率,在unity3d這裡同樣遵循了這個原理。在unity3d中進行批處理的乙個前提是相同材質的物體可以被合併,如果這些物體使用不同的材質,那麼當我們把這些材質對應的紋理打成「圖集」以後可以對其進行合併,並且在合併的時候應該是用renderer.sharedmaterial 而非 renderer.material以保證材質是可以共享的。關於drawcall的相關細節大家從這裡來了解,博主並未對圖形學領域有過深入的研究,因此就不在這裡班門弄斧了啊,哈哈!

在unity3d中有靜態批處理和動態批處理兩種方式,下面我們就來分別說說這兩種不同的批處理方式!

靜態批處理

動態批處理

相對靜態批處理而言,動態批處理的要求更為嚴格一些,它要求批處理的動態物件具有一定的頂點,所以動態批處理只適用於包含小於900個頂點屬性的網格。如果你的著色器使用頂點位置,法線和單光,然後你可以批處理300個頂點的動態物件;而如果你的著色器使用頂點位置,法線,uv0,uv1和切線,那麼只能處理180個頂點的動態物件。接下來最為重要的一點,如果動態物件使用的是不同的材質,那麼即使進行了動態批處理從效率上來講並不會有太大的提公升。如果動態物件採用的是多維子材質,那麼批處理是無效的。如果動態物件接收實時光影,同樣批處理是無效的。下面展示的是乙個將多個物體合併為乙個物體的指令碼示例:

[menuitem("modeltools/將多個物體合併為乙個物體")]

static void combinemeshs2()

//建立新物體

gameobject go = new gameobject();

go.name = "combinedmesh_" + ((gameobject)objs[0]).name;

//設定網格資訊

meshfilter filter = go.transform.getcomponent();

if (filter == null)

filter = go.addcomponent();

filter.sharedmesh = new mesh();

filter.sharedmesh.combinemeshes(combines,false);

//設定渲染器

meshrenderer render = go.transform.getcomponent();

if (render == null)

render = go.addcomponent();

//設定材質

render.sharedmaterials = mats;

}

這段指令碼的核心是combinemeshes()方法,該方法有三個引數,第乙個引數是合併例項的陣列,第二個引數是是否對子物體的網格進行合併,第三個引數是是否共享材質,如果希望物體共享材質則第三個引數為true,否則為false。在我測試的過程中發現,如果選擇了對子物體的網格進行合併,那麼每個子物體都不能再使用單獨的材質,缺省會以第乙個材質作為合併後物體的材質,下面演示的是合併前的多個物體和合併後的乙個物體的對比:

那麼批處理對遊戲效率提公升究竟有怎樣的作用呢?我們來看下面幾組測試對比:

1、三個不同的物體使用同一種材質,不做靜態批處理,不做動態批處理:drawcall為4、面數為584、頂點數為641

2、三個不同的物體使用同一種材質,只做靜態批處理,不做動態批處理:drawcall為2、面數為584、頂點數為641

3、三個不同的物體使用不同的材質,不做靜態批處理,不做動態批處理:drawcall為4、面數為584、頂點數為641

4、三個不同的物體使用不同的材質,只做靜態批處理,不做動態批處理:drawcall為4、面數為584、頂點數為641

5、三個不同的物體使用不同的材質,不做靜態批處理,只做動態批處理:drawcall為4、面數為584、頂點數為641

6、三個不同的物體使用不同的材質,做靜態批處理,做動態批處理:drawcall為4、面數為584、頂點數為641

7、三個不同的物體使用同一種材質,不做靜態批處理,只做動態批處理::drawcall為4、面數為584、頂點數為641

大家可以注意到各組測試結果中,只有第二組的drawcall降低,這說明只有當不同的物體使用同一種材質時通過批處理可以從一定程度上降低drawcall,即我們在文章開始提到的盡可能地保證材質共享。昨天下午興沖沖地將遊戲場景裡的某些物體進行了動態批處理,但是實際測試的過程中發現drawcall非常地不穩定,但是在場景中的某些地方drawcall卻可以降得非常低,如果靜態批處理和動態批處理都不能對場景產生較好的優化,那麼unity3d遊戲場景的優化究竟要從**抓起呢?我覺得這是我們每乙個人都該用心去探索的地方,畢竟遊戲做出來首先要保證能讓玩家流暢的玩下去吧,一味的強調引擎、強調畫面,卻時常忽略引擎使用者的主觀能動性,希望把一切問題都交給引擎去解決,這樣的思路是錯誤而落後的,仙劍六的問題完全是用不用心的問題,我常常看到有人在公開場合說仙劍以後要換虛幻三,其實按照北軟現在這樣的狀態,給他們乙個虛幻四也不過是然並卵。我在知乎上看到了號稱15歲就開發次時代遊戲的高中生妹子,做出個能稱為demo的遊戲就覺得自己可以搞引擎了,更有甚者隨便用directx或者opengl封裝若干函式就敢說自己會做遊戲引擎了,呵呵,你確定你的遊戲能在別人的電腦或者手機上執行起來嗎?優化的重要性可見一斑。

好了,通過今天這篇文章,我們可以整理出以下觀點: 

1、如果不同的物體間共享材質,則可以直接通過靜態批處理降低drawcall

2、動態批處理並不能降低drawcall、面數和頂點數(我不會告訴你我昨天傻呵呵地合併了好多場景中的模型,結果面數和頂點數並沒有降下來,23333)

3、不管是靜態批處理還是動態批處理都會影響culiing,這同樣是涉及到場景優化的乙個概念,好吧,為了讓場景的drawcall降下來我最近可能要研究好多涉及的優化的內容……

相關鏈結

喜歡我的部落格請記住我的名字:秦元培,我的部落格位址是:

自己動手寫遊戲引擎二 D3D遊戲場景

這一節課,我們要做乙個 d3d的開發環境出來,基於 vc 6.0 directx9 我們需要的標頭檔案和庫檔案 d3d9.h 與d3d9.lib 首先,開發之前,我們要了解兩個東西 具體意義,參考 sdk幫助檔案 idirect3d9 pidirect3d9 d3d 介面物件 idirect3dde...

3D遊戲場景DEMO 新增水體效果

好久沒寫了,5月初就把地形弄差不多了,後來幾天把天空穹改了下,弄了多重紋理雲彩飄動。之後一直在做水體的實現,發現水體太煩了。原以為比較簡單沒地形複雜,結果一查閱資料,天吶 比地形複雜多了。和地形也有相似的地方lod之類的,也許是本來對圖形方面的不了解。看了海水生成的方式 2d水波方法 正弦波疊加 f...

3D遊戲場景DEMO 新增水體效果

好久沒寫了,5月初就把地形弄差不多了,後來幾天把天空穹改了下,弄了多重紋理雲彩飄動。之後一直在做水體的實現,發現水體太煩了。原以為比較簡單沒地形複雜,結果一查閱資料,天吶 比地形複雜多了。和地形也有相似的地方lod之類的,也許是本來對圖形方面的不了解。看了海水生成的方式 2d水波方法 正弦波疊加 f...