unity3d Drawcall優(yōu)化方法

unity3D 對(duì)于移動(dòng)平臺(tái)的支持無(wú)可厚非,但是也有時(shí)候用3D 開發(fā)出來(lái)的應(yīng)用、游戲在移動(dòng)終端上的運(yùn)行有著明顯的效率問題,比如卡、畫質(zhì)等各種問題。自己在做游戲開發(fā)的時(shí)候偶有所得。對(duì)于主要影響性能的因素做個(gè)總結(jié)。

主要因素有:

1. Saved by batching 值過大 ---- > 這個(gè)值主要是針對(duì)Mesh的批處理,這個(gè)值越高,應(yīng)用就越卡

2. Drawcall 值過大 ---- > Drawcall 值過大,所需要的 GPU 的處理性能較高,從而導(dǎo)致CPU的計(jì)算時(shí)間過長(zhǎng),于是就卡了

3. 點(diǎn)、面過多 ---- > 點(diǎn)、面過多,GPU 根據(jù)不同面的效果展開計(jì)算,并且CPU計(jì)算的數(shù)據(jù)也多,所以效果出來(lái)了,但是卡巴斯基

由于 Saved by batching 和 Drawcall 值過大所引起的卡的問題我所做的優(yōu)化方式有:

1. 對(duì)于模型 :Mesh 合并,有個(gè)不錯(cuò)的插件(DrawCall Minimizer ---> 直接上Asset Store 下載即可,免費(fèi)的,而且有文檔,很容易上手)

2. 對(duì)于UI : 盡量避免使用Unity3D自帶的 GUI 換用 NGUI或者EZGUI;因?yàn)檫@兩個(gè)UI插件對(duì)于UI中的圖片處理是將UI圖片放置在一個(gè) Atlas 中,一個(gè) Atlas 對(duì)應(yīng)一個(gè)Drawcall

3. 對(duì)于燈光: 可以使用 Unity3D 自帶的 Lightmapping 插件來(lái)烘焙場(chǎng)景中的燈光效果到物體材質(zhì)上

4. 對(duì)于場(chǎng)景: 可以使用 Unity3D 自帶的 Occlusion Culling 插件把靜止不動(dòng)的場(chǎng)景元素烘焙出來(lái)

4. 對(duì)于特效:盡量把材質(zhì)紋理合并

對(duì)于Unity3D 在移動(dòng)終端上支持的Drawcall 數(shù)到底多少,主要是跟機(jī)子性能有關(guān)的,當(dāng)然也不是說(shuō)值小性能就一定沒問題(本人親測(cè),也有17就卡的,主要是模型材質(zhì)紋理過大所引起的),目前我做的是70左右的,還OK,挺正常的

由于點(diǎn)、面過多所導(dǎo)致的性能問題,最好用簡(jiǎn)模,用四面體來(lái)做復(fù)雜的模型,但是面、點(diǎn)也別太多,至于Unity3D 到底支持多少點(diǎn)、面的說(shuō)法各異,我也搞不懂,總之少些肯定OK

檢測(cè)方式:

一,Unity3D 渲染統(tǒng)計(jì)窗口

Game視窗的Stats去查看渲染統(tǒng)計(jì)的信息:

1、FPS

fps其實(shí)就是 frames per second,也就是每一秒游戲執(zhí)行的幀數(shù),這個(gè)數(shù)值越小,說(shuō)明游戲越卡。

2、Draw calls

batching之后渲染mesh的數(shù)量,和當(dāng)前渲染到的網(wǎng)格的材質(zhì)球數(shù)量有關(guān)。

3、Saved by batching

渲染的批處理數(shù)量,這是引擎將多個(gè)對(duì)象的繪制進(jìn)行合并從而減少GPU的開銷;

很多GUI插件的一個(gè)好處就是合并多個(gè)對(duì)象的渲染,從而降低DrawCalls ,保證游戲幀數(shù)。

4、Tris 當(dāng)前繪制的三角面數(shù)

5、Verts 當(dāng)前繪制的頂點(diǎn)數(shù)

6、Used Textures 當(dāng)前幀用于渲染的圖片占用內(nèi)存大小

7、Render Textures 渲染的圖片占用內(nèi)存大小,也就是當(dāng)然渲染的物體的材質(zhì)上的紋理總內(nèi)存占用

8、VRAM usage 顯存的使用情況,VRAM總大小取決于你的顯卡的顯存

9、VBO Total 渲染過程中上載到圖形卡的網(wǎng)格的數(shù)量,這里注意一點(diǎn)就是縮放的物體可能需要額外的開銷。

10、Visible Skinned Meshes 蒙皮網(wǎng)格的渲染數(shù)量

11、Animations 播放動(dòng)畫的數(shù)量

注意事項(xiàng):

1,運(yùn)行時(shí)盡量減少 Tris 和 Draw Calls

預(yù)覽的時(shí)候,可點(diǎn)開 Stats,查看圖形渲染的開銷情況。特別注意 Tris 和 Draw Calls 這兩個(gè)參數(shù)。

一般來(lái)說(shuō),要做到:

Tris 保持在 7.5k 以下,有待考證。

Draw Calls 保持在 20 以下,有待考證。

2,F(xiàn)PS,每一秒游戲執(zhí)行的幀數(shù),這個(gè)數(shù)值越小,說(shuō)明游戲越卡。

3,Render Textures 渲染的圖片占用內(nèi)存大小。

4,VRAM usage 顯存的使用情況,VRAM總大小取決于你的顯卡的顯存。

二,代碼優(yōu)化

1. 盡量避免每幀處理

比如:

function Update() { DoSomeThing(); }

可改為每5幀處理一次:

function Update() { if(Time.frameCount % 5 == 0) { DoSomeThing(); } }

2. 定時(shí)重復(fù)處理用 InvokeRepeating 函數(shù)實(shí)現(xiàn)

比如,啟動(dòng)0.5秒后每隔1秒執(zhí)行一次 DoSomeThing 函數(shù):

function Start() { InvokeRepeating("DoSomeThing", 0.5, 1.0); }

3. 優(yōu)化 Update, FixedUpdate, LateUpdate 等每幀處理的函數(shù)

函數(shù)里面的變量盡量在頭部聲明。

比如:

function Update() { var pos: Vector3 = transform.position; }
  可改為

private var pos: Vector3; function Update(){ pos = transform.position; }

4. 主動(dòng)回收垃圾

給某個(gè) GameObject 綁上以下的代碼:

function Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }
  5. 優(yōu)化數(shù)學(xué)計(jì)算

比如,如果可以避免使用浮點(diǎn)型(float),盡量使用整形(int),盡量少用復(fù)雜的數(shù)學(xué)函數(shù)比如 Sin 和 Cos 等等

6,減少固定增量時(shí)間

將固定增量時(shí)間值設(shè)定在0.04-0.067區(qū)間(即,每秒15-25幀)。您可以通過Edit->Project Settings->Time來(lái)改變這個(gè)值。這樣做降低了FixedUpdate函數(shù)被調(diào)用的頻率以及物理引擎執(zhí)行碰撞檢測(cè)與剛體更新的頻率。如果您使用了較低的固定增量時(shí)間,并且在主角身上使用了剛體部件,那么您可以啟用插值辦法來(lái)平滑剛體組件。

7,減少GetComponent的調(diào)用

使用 GetComponent或內(nèi)置組件訪問器會(huì)產(chǎn)生明顯的開銷。您可以通過一次獲取組件的引用來(lái)避免開銷,并將該引用分配給一個(gè)變量(有時(shí)稱為"緩存"的引用)。例如,如果您使用如下的代碼:

function Update () { transform.Translate(0, 1, 0); }
  通過下面的更改您將獲得更好的性能:

function Awake () 
{
myTransform = transform;
}
function Update ()
 {
myTransform.Translate(0, 1, 0);
}
 ```
  8,避免分配內(nèi)存

  您應(yīng)該避免分配新對(duì)象,除非你真的需要,因?yàn)樗麄儾辉僭谑褂脮r(shí),會(huì)增加垃圾回收系統(tǒng)的開銷。您可以經(jīng)常重復(fù)使用數(shù)組和其他對(duì)象,而不是分配新的數(shù)組或?qū)ο蟆_@樣做好處則是盡量減少垃圾的回收工作。同時(shí),在某些可能的情況下,您也可以使用結(jié)構(gòu)(struct)來(lái)代替類(class)。這是因?yàn)?,結(jié)構(gòu)變量主要存放在棧區(qū)而非堆區(qū)。因?yàn)闂5姆峙漭^快,并且不調(diào)用垃圾回收操作,所以當(dāng)結(jié)構(gòu)變量比較小時(shí)可以提升程序的運(yùn)行性能。但是當(dāng)結(jié)構(gòu)體較大時(shí),雖然它仍可避免分配/回收的開銷,而它由于"傳值"操作也會(huì)導(dǎo)致單獨(dú)的開銷,實(shí)際上它可能比等效對(duì)象類的效率還要低。

  9,使用iOS腳本調(diào)用優(yōu)化功能

  UnityEngine 命名空間中的函數(shù)的大多數(shù)是在 C/c + +中實(shí)現(xiàn)的。從Mono的腳本調(diào)用 C/C++函數(shù)也存在著一定的性能開銷。您可以使用iOS腳本調(diào)用優(yōu)化功能(菜單:Edit->Project Settings->Player)讓每幀節(jié)省1-4毫秒。此設(shè)置的選項(xiàng)有:

  Slow and Safe – Mono內(nèi)部默認(rèn)的處理異常的調(diào)用

  Fast and Exceptions Unsupported –一個(gè)快速執(zhí)行的Mono內(nèi)部調(diào)用。不過,它并不支持異常,因此應(yīng)謹(jǐn)慎使用。它對(duì)于不需要顯式地處理異常(也不需要對(duì)異常進(jìn)行處理)的應(yīng)用程序來(lái)說(shuō),是一個(gè)理想的候選項(xiàng)。

  10,

  優(yōu)化垃圾回收

  如上文所述,您應(yīng)該盡量避免分配操作。但是,考慮到它們是不能完全杜絕的,所以我們提供兩種方法來(lái)讓您盡量減少它們?cè)谟螒蜻\(yùn)行時(shí)的使用:

  如果堆比較小,則進(jìn)行快速而頻繁的垃圾回收

  這一策略比較適合運(yùn)行時(shí)間較長(zhǎng)的游戲,其中幀率是否平滑過渡是主要的考慮因素。像這樣的游戲通常會(huì)頻繁地分配小塊內(nèi)存,但這些小塊內(nèi)存只是暫時(shí)地被使用。如果在iOS系統(tǒng)上使用該策略,那么一個(gè)典型的堆大小是大約 200 KB,這樣在iPhone 3G設(shè)備上,垃圾回收操作將耗時(shí)大約 5毫秒。如果堆大小增加到1 MB時(shí),該回收操作將耗時(shí)大約 7ms。因此,在普通幀的間隔期進(jìn)行垃圾回收有時(shí)候是一個(gè)不錯(cuò)的選擇。通常,這種做法會(huì)讓回收操作執(zhí)行的更加頻繁(有些回收操作并不是嚴(yán)格必須進(jìn)行的),但它們可以快速處理并且對(duì)游戲的影響很?。?
``` if (Time.frameCount % 30 == 0
{
System.GC.Collect();
}
 ```
  但是,您應(yīng)該小心地使用這種技術(shù),并且通過檢查Profiler來(lái)確保這種操作確實(shí)可以降低您游戲的垃圾回收時(shí)間

  如果堆比較大,則進(jìn)行緩慢且不頻繁的垃圾回收

  這一策略適合于那些內(nèi)存分配 (和回收)相對(duì)不頻繁,并且可以在游戲停頓期間進(jìn)行處理的游戲。如果堆足夠大,但還沒有大到被系統(tǒng)關(guān)掉的話,這種方法是比較適用的。但是,Mono運(yùn)行時(shí)會(huì)盡可能地避免堆的自動(dòng)擴(kuò)大。因此,您需要通過在啟動(dòng)過程中預(yù)分配一些空間來(lái)手動(dòng)擴(kuò)展堆(ie,你實(shí)例化一個(gè)純粹影響內(nèi)存管理器分配的"無(wú)用"對(duì)象):

```function Start() 
{var tmp = new System.Object[1024];
// make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocks
for (var i : int = 0; i < 1024; i++)
 tmp[i] = new byte[1024];
// release reference
tmp = null;
}
 ```
  游戲中的暫停是用來(lái)對(duì)堆內(nèi)存進(jìn)行回收,而一個(gè)足夠大的堆應(yīng)該不會(huì)在游戲的暫停與暫停之間被完全占滿。所以,當(dāng)這種游戲暫停發(fā)生時(shí),您可以顯式請(qǐng)求一次垃圾回收:

   ```System.GC.Collect();
 ```

  另外,您應(yīng)該謹(jǐn)慎地使用這一策略并時(shí)刻關(guān)注Profiler的統(tǒng)計(jì)結(jié)果,而不是假定它已經(jīng)達(dá)到了您想要的效果。

  三,模型

  1,壓縮 Mesh

  導(dǎo)入 3D 模型之后,在不影響顯示效果的前提下,最好打開 Mesh Compression。

  Off, Low, Medium, High 這幾個(gè)選項(xiàng),可酌情選取。

  2,避免大量使用 Unity 自帶的 Sphere 等內(nèi)建 Mesh

  Unity 內(nèi)建的 Mesh,多邊形的數(shù)量比較大,如果物體不要求特別圓滑,可導(dǎo)入其他的簡(jiǎn)單3D模型代替。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容