Unity3D通用的性能優化

http://www.cnblogs.com/willbin/p/3389837.html


官方優化文檔--優化圖像性能
http://docs.Unity3D.com/Documentation/Manual/OptimizingGraphicsPerformance.html

 

unity3d性能優化專題
性能優化是一個異常繁瑣而又涉及到項目開發的方方面面的一個過程,它的本質是在運行時的一個時間裏儘可能完美展現豐富的內容。
實現優化可以通過優化資源、渲染、粒子、物理等模式;
也可以通過修改模型大小、減少紋理尺寸並結合Unity3D的一些相關特性來提升遊戲的性能。
隨着移動端的設備硬件能力的提升,如何使用儘可能優化的資源和程序效率來展現出更多的細節內容就成爲了每個開發者都應該思考的內容,這也使得優化變成了項目開發中非常重要的一環。

***********
首先介紹下draw call(這個東西越少你的遊戲跑的越快):
在遊戲中每一個被展示的獨立的部分都被放在了一個特別的包中,我們稱之爲“描繪指令”(draw call),然後這個包傳遞到3D部分在屏幕上呈現出來。這就和你希望你的親友收到準備好的聖誕禮物需要包裝好然後穿過城市準時放在他應該出現的地方一樣沒什麼不同。你的CPU來完成包裝和傳遞他們的活,同時會消耗很多的帶寬,所以最終分配好這些關鍵性資源很重要。目前,真正可怕的事情是從描繪指令消耗遠景開始,每一個獨立的飛濺到地板上的血跡和一個角色或者一具死屍消耗的字節是一樣的多的:他們都消耗同樣的描繪指令。除此之外,沒有什麼更多的差別。
那麼如何降低 draw call 呢??那麼我們就用到Culling(剔除)技術。如果不應用這個技術,電腦是不管3721把場景裏所有的東西都送去渲染的。看得見的也渲染,看不見得照樣也送去渲染。很傻是吧,那咋辦呢。得告訴電腦,那個你
看得見的渲染,看不見的就算了。於是就有了
1.視錐體剔除(Frustum Culling)這個unity系統自帶了好像,就不用操心了。
2.遮擋剔除(Occlusion Culling)

Unity 3專業版內置了一個強大的 Occlusion Culling 插件 Umbra免費的
遮擋剔除(Occlusion Culling) 
遮擋剔除是一種什麼樣的特性呢, 當一個物體被其他物體遮擋住而不在攝像機的可視範圍內時不對其進行渲染。. 遮擋剔除在3D圖形計算中並不是自動進行的。因爲在絕大多數情況下離 camera 最遠的物體首先被渲染,靠近攝像機的物體後渲染並覆蓋先前渲染的物體(這被稱爲重複渲染,無效渲染"overdraw"). 遮擋剔除不同於視錐體剔除. 視錐體剔除只是不渲染攝像機視角範圍外的物體而對於被其他物體遮擋但依然在視角範圍內的物體則不包括在內. 注意當你使用遮擋剔除時你依然受益於視錐體剔除(Frustum Culling).
***********
Unity(或者說基本所有圖形引擎)生成一幀畫面的處理過程大致可以這樣簡化描述:引擎首先經過簡單的可見性測試,確定攝像機可以看到的物體,然後把這些物體的頂點(包括本地位置、法線、UV等),索引(頂點如何組成三角形),變換(就是物體的位置、旋轉、縮放、以及攝像機位置等),相關光源,紋理,渲染方式(由材質/Shader決定)等數據準備好,然後通知圖形API——或者就簡單地看作是通知GPU——開始繪製,GPU基於這些數據,經過一系列運算,在屏幕上畫出成千上萬的三角形,最終構成一幅圖像。
在Unity中,每次引擎準備數據並通知GPU的過程稱爲一次Draw Call。這一過程是逐個物體進行的,對於每個物體,不只GPU的渲染,引擎重新設置材質/Shader也是一項非常耗時的操作。因此每幀的Draw Call次數是一項非常重要的性能指標,對於iOS來說應儘量控制在20次以內,這個值可以在編輯器的Statistic窗口看到。 Unity內置了Draw Call Batching技術,從名字就可以看出,它的主要目標就是在一次Draw Call中批量處理多個物體。只要物體的變換和材質相同,GPU就可以按完全相同的方式進行處理,即可以把它們放在一個Draw Call中。Draw Call Batching技術的核心就是在可見性測試之後,檢查所有要繪製的物體的材質,把相同材質的分爲一組(一個Batch),然後把它們組合成一個物體(統一變換),這樣就可以在一個Draw Call中處理多個物體了(實際上是組合後的一個物體)。 
但Draw Call Batching存在一個缺陷,就是它需要把一個Batch中的所有物體組合到一起,相當於創建了一個與這些物體加起來一樣大的物體,與此同時就需要分配相應大小的內存。這不僅會消耗更多內存,還需要消耗CPU時間。特別是對於移動的物體,每一幀都得重新進行組合,這就需要進行一些權衡,否則得不償失。但對於靜止不動的物體來說,只需要進行一次組合,之後就可以一直使用,效率要高得多。 - See more at: http://ravenw.com/blog/2011/10/14/unity-optimization-of-draw-call/#sthash.0WfH4KnX.dpuf

***********

 

輸出設置相關:
1:最終輸出的時候,在unity - edit - project setting - player 裏, 把 other settings - script call optimization 改成 快速, 但沒有例外. fast but no exceptions

2: unity - edit - project setting - time 裏, 把 maximum allowed timestep 改成0.1

3:

 

常規優化
1: 聯結(combine)優化
顯卡對於一個含100個面片的物體的和含1500個面片的物體的渲染消耗幾乎是等價的。所以如果你有N個同一材質的東西,那麼把他們聯成同一個物體再統一用一個material那麼對於顯卡的渲染消耗就要降低N倍。

在unity裏再聯結,這個要怎麼做呢,其實也挺簡單的,經常看Island Demo項目的人應該很早就注意到裏面的石頭這些都是連在一起的,原因就在這裏,他提供了現成就腳本實現聯結。
先到Island Demo的Assets/Script下找到CombineChildren.cs和MeshCombineUtility.cs兩個腳本複製到自己的項目文件(我們要用的只是前者,但他會調用後者,沒有後者unity會報錯,所以把後者扔到項目裏不管就好)
然後把你項目裏那些用同一Materials的東西扔到一個空物體裏面去,再把CombineChildren.cs貼到那個空物體上,搞定!

2: 模型
(1)只用一個mesh renderer, 少用多materials, 最多3個
每個角色儘量使用一個 Skinned Mesh Renderer
這是因爲當角色僅有一個 Skinned Mesh Renderer 時, Unity 會 使用可見性裁剪和包圍體更新的方法來優化角色的運動,而這種優化只有在角色僅含有一個 Skinned Mesh Renderer 時纔會啓動。
(2)模型骨骼不超過30個.
(3)儘量減少面 300-1500
(4)模型儘量不要分開, 如果多個模塊, 會多次調用dc
(5)一般角色應該沒有 IK 結點
這是因爲角色的動作大多數都是事先設定好的,並不需要經過 IK 操作來進行實時計算( Rogdoll 除外),所以在模型導入時,不要將 IK 結點一起導入。
(6) 不要附加 Animation Component 在靜態實體上附加 Animation 部件雖然對結果沒有影響,但卻會增加一定的 CPU 開銷來調用這一組件,所以儘量去掉該組件。

 

3:儘量不用像素光(pixels Lights)

4:不用 軟陰影(soft shadow), 或者不用陰影(No Shadows)
燈光能不用就不用, 陰影可以用面來代替.

5:少用實時燈光, 儘量使用lightmap
http://blog.sina.com.cn/s/blog_409cc4b00100no8y.html
動態實時燈光相比靜態燈光,非常耗費資源。所以除了能動的角色和物體(比如可以被打的到處亂飛的油桶)靜態的地形和建築,通通使用Lightmap。
強大的Unity內置了一個強大的光照圖烘焙工具Beast,這個東東是Autodesk公司的產品

6:transform和OnGUI (運算上的優化遠比不上 繪製效率上的優化,少個dc可能就比得上這些了)
http://fredxxx123.wordpress.com/2013/05/22/unity%E6%80%A7%E8%83%BD%E5%84%AA%E5%8C%96%E4%B9%8B%E5%B0%8F%E6%B8%AC%E8%A9%A6/
// transfrom
少用transform, 多用 myCachedTransform
頻繁使用該物件的地方,就是先把該物件cache起來,理論上就會避免這種不必的開銷。
※這也可延伸至其他是property的變數。例如:transform.position。
***直接使用的版本:
public class TestUnit : MonoBehaviour
{
void Update()
{
var t = this.transform;
t = this.transform;
t = this.transform;
t = this.transform;
t = this.transform;
}
}
***cache 使用的版本:
public class TestUnit : MonoBehaviour 

Transform myTransform;
void Start()
{
myTransform = transform;
}

void Update ()
{
var t = this.myTransform;
t = this.myTransform;
t = this.myTransform;
t = this.myTransform;
t = this.myTransform;
}
}
// OnGUI
空物件版本:
public class TestUnit : MonoBehaviour
{
}
外加一個空白OnGUI的版本:
public class TestUnit : MonoBehaviour
{
void OnGUI()
{ }
}
實際上是被GUI.Begin()和GUI.End()所佔據,function本身是無辜的~~
非必要避免使用OnGUI(),即使它裡面甚麼事情都沒做!
如需使用,請盡可能集中到同一個OnGUI()底下執行繪製,好避免Begin End的開銷。


7:動態物體的相關優化

優化主要分爲兩個方向,一個是資源相關優化和引擎相關的優化。資源相關的優化,大概分爲動態物體、靜態物體、紋理數據、音頻數據、程序包數據。對於動態物體比如NPC、怪物等,需要對面片數量的控制,大概在300到2000面。1500面就可以體現人物細節,但如果是人物比較多,可能要降低面數,不要低於300。另外,一方面是控制Skinned Mesh Renderer的數量;另一方面是控制材質數量在1到3種。人物最好用小於30根骨骼,如果你用的骨骼越多,耗費的CPU就更多,所以在移動平臺上儘量少於30根。現在我們看其他動態物體,利用Dynamic Batching進行合批。這個下雨特效並不是系統做的,是包含很多雨點的網格進行重複拷貝,然後錯亂移動實現的。每一個雨點並不是一個粒子,這樣能減少很多CPU的消耗,每一個整體網格都會有一個頂點的控制,通過控制頂點數量,對系統實現雨點效果來說,這是一個相當省時省力的方法。

8:靜態物體的相關優化

下面我們來看靜態物體,靜態物體也是要控制面數和頂點數,頂點數少於500個。static是不會進行移動縮放、旋轉的,把它標記爲static,當然他們的材質是一樣的。不要添加animation組建,對於靜態物體來說,這個組件毫無意義,能把他丟掉就丟掉,因爲這對CPU的消耗是非常客觀的。

9:音頻程序的優化
關於音頻時間的播放,比如背景音樂,建議使用MP3壓縮格式,比如音效,要求數據儘快加載,這些數據比較小就可以,使用WAV和AIF未壓縮音頻格式。關於程序包的優化,很多開發者會埋怨說打出來的包太大,現在介紹減少程序包的方法,首先使用壓縮格式的紋理,以顯卡的壓縮格式保存,使用壓縮網格和動畫數據。網格壓縮是先採用量化處理,當然這個壓縮是保證在包裏面的數據小,但運行時佔用的內存沒有減少,因爲我們並沒有把頂點刪除,但是對動畫數據來說,動畫數據經過壓縮處理後降低,可以減少遊戲列層。


關於代碼儘量不要使用System.xml,我們建議使用Mono.xml。啓用Stripping來減少庫的大小,使用剝離方式。


10:引擎相關優化和物理相關優化
下來是引擎相關的優化,例如光照設置、相繼設置、粒子特效、物理特效等。那拿光照設置來說,光源全部的實時光照這是很恐怖的,每一次實施光照代表着每一次使用消耗,怎麼優化?有人使用LightMapping來製作靜態場景,他的好處是不需要用多張實施光照,而給場景很好的光照效果。有人使用Light Probes代替實時光照,好處是完全不用怎麼消耗,而且運作性能也非常高。在有些時候使用Light Probes代替光照,他能跟場景很好的融合,在一個角落裏,這個任務會被陰影打得暗一些。如果說場景中確實需要一些實時光源,那麼肯定是需要做過優化設置的實時光源,控制important的光源個數。如果說光源有些地方產生了交叉光,這個時候你可以通過設置Pxel Light,控制每一個光源都只接受一個動態光照,數目大概是1—2個。對於關閉光源的實時陰影,並不是所有平臺都支持實時陰影,消耗也非常大,不建議大家使用。關於相機方面的設置,平面越近,渲染越少。我們更建議使用分層,比如遠處的建築,對於建築物的裁減平面遠一些,如果是花草,就可以使用平面就近一些。現在看一下粒子特效,粒子也是遊戲中需要優化的東西,建議屏幕中最大的粒子數不要超過200,同時每個發射器發射的最大粒子數不要超過50。粒子尺寸也要儘可能小,最終在屏幕有多少像素。他們中間的像素可能會被渲染很多次,至少四五次,這時發現粒子系統僅像素就填充了更多屏幕,這時候對遊戲來說非常耗費,對遊戲的其他功能性能也有所影響。另外一方面,對於非常小的粒子,儘量不要開啓粒子碰撞功能。

現在我們看一下物理相關優化,物理儘可能使用Sphere Coillider、Box Coillider等,儘量避免使用Meh Colllider等。渲染設置,避免使用Alpha Test,因爲非常耗時,性價比很低。關於Sttic Batching,對靜態物體進行Batch,對幾何數據的大小沒有限制。物體被合併後會帶來一些內存消耗,比如說有控制網格的物體,用Batch會合併成大物體。Dynamic Batching目前僅支持小於900頂點的網格物體。如何理解900呢,其實就相當於900個頂點數據大小的物體,如果說使用Position、Normal和UV三種屬性,那麼你只能Batch300個頂點。整體縮放的物體不能被Batch,除非他們的縮放值相同。之前有一個客戶做特效,使用Batch機制把面片合併,最終讓所有面片共享一個紋理,這時候發現這些面片沒有被Batch出來,導致運行遊戲時大概放三個技能就10多個招套。對於非整體用戶體,他們的Batch是需要很好利用到。

11:紋理合並優化
現在來看紋理合並,紋理合並就是爲了特到Batch數量,合併物體首先需要合併工具,還要修改使用紋理的網格的UV,使他們使用紋理。合併紋理主要是參照Batch,提高渲染性能。但在合併材質後需要注意的是腳本訪問Renderer被拷貝。/*安擋剔除,建議使用PVS技術。建議大家使用自定義shader,例如高光效果,高光效果可能不需要做一些入射線的檢測,只是簡單把他的值放大也可以模擬高光效果,從而減少一些消耗。

另外一個是用profiler,通過他給的數據進行針對性的優化。以上是跟大家介紹優化的內容,如何作出良好優化,一定要做好良好的規劃,到後期就不會很麻煩,如果規劃沒有做好有可能會給程序帶來很大壓力,結果可能很不樂觀。*/最後,要不斷實驗不斷總結才能達到自己滿意的效果。


12:
要想降低Drawcal的話,有如下兩點小建議
(1)不要用Unity自帶UI或者iGUI, 用NUI 或者EZ GUI
(2)創建好的GameObject不用了就最好及時 刪除 / 設置active爲false/移出屏幕 。 這幾種方法都可以去掉該物體導致增加的Drawcall


13:

 

 

 

*********************************
*********************************
*********************************
最近一段時間一直在做Unity 在ios設備上的資源優化,結合Unity的官方文檔以及自己遇到的實際問題,我把自己認爲一些重要的信息羅列在下面,並儘可能對將其量化,以方便更多需要做優化的朋友。

1、 角色
每個角色儘量使用一個 Skinned Mesh Renderer
這是因爲當角色僅有一個 Skinned Mesh Renderer 時, Unity 會 使用可見性裁剪和包圍體更新的方法來優化角色的運動,而這種優化只有在角色僅含有一個 Skinned Mesh Renderer 時纔會啓動。
角色 Material 數量
2-3 個
骨骼數量
小於 30 個
面片數量
300-1500
一般角色應該沒有 IK 結點
這是因爲角色的動作大多數都是事先設定好的,並不需要經過 IK 操作來進行實時計算( Rogdoll 除外),所以在模型導入時,不要將 IK 結點一起導入。

2、 靜態實體
不要附加 Animation Component
在靜態實體上附加 Animation 部件雖然對結果沒有影響,但卻會增加一定的 CPU 開銷來調用這一組件,所以儘量去掉該組件。
網格頂點數
小於 500
UV 值範圍儘量不要超過( 0, 1 )區間
儘量保證 UV 值不越界,這對於將來的紋理拼合優化很有幫助。

3、 地形
地形的分辨率大小
長寬均儘量小於 257 。這是因爲地形太大,會造成大量頂點數據,給你的內存帶寬造成一定的影響,在目前的 ios 設備中,內存帶寬是非常有限的,需要儘量節省。同時,如果用 Unity 自帶的地形,一定也要使用 Occlusion Culling ,因爲 Unity 的刷地形工具雖然方便,但卻是 framekiller ,刷過之後,你會發現 drawcall 增加的非常多。
混合紋理數量
不要超過 4 。地形的混合操作是很耗時的,應該儘量避免。能合併的紋理儘量合併。

4、 紋理
紋理格式
建議 png 或 tga 。不用轉成 ios 硬件支持的 PVRTC 格式,因爲 Unity 在發佈時會幫你自動轉的。
紋理尺寸
長寬小於 1024 。同時應該儘可能地小,夠用就好,以保證紋理對內存帶寬的影響達到最小。
支持 Mipmap
建議生成 Mipmap 。雖然這種做法會增加一些應用程序的大小,但在遊戲運行時,系統會根據需求應用 Mipmap 來渲染,從而減少內存帶寬。
檢查 Alpha 值
如果紋理的 alpha 通道均爲 1 ,則用 RGB 的 24 位紋理來代替 RGBA 的 32 位紋理。(據說 Unity 內部會進行自動檢測)

5、 光源
光源“ Important ”個數
建議 1 個,一般爲方向光。“ Important ”個數應該越小越少。個數越多, drawcall 越多。
Pixel Light 數目
1-2 個。

6、 粒子特效
屏幕上的最大粒子數
建議小於 200 個粒子。
每個粒子發射器發射的最大粒子數
建議不超過 50 個。
粒子大小
如果可以的話,粒子的 size 應該儘可能地小。因爲 Unity 的粒子系統的 shader 無論是 alpha test 還是 alpha blending 都是一筆不小的開銷。同時,對於非常小的粒子,建議粒子紋理去掉 alpha 通道。
儘量不要開啓粒子的碰撞功能。
非常耗時。

7、 音頻
遊戲中播放時間較長的音樂(如背景音樂)
使用 .ogg 或 .mp3 的壓縮格式。
較短音樂(如槍聲)
使用 .wav 和 .aif 的未壓縮音頻格式。

8、 相機
裁剪平面
將遠平面設置成合適的距離。遠平面過大會將一些不必要的物體加入渲染,降低效率。
根據不同的物體設置不同的遠裁剪平面
Unity 提供了可以根據不同的 layer 來設置不同的 view distance ,所以我們可以實現將物體進行分層,大物體層設置的可視距離大些,而小物體層可以設置地小些,另外,一些開銷比較大的實體(如粒子系統)可以設置得更小些等等。

9、 碰撞
儘量不用 MeshCollider
如果可以的話,儘量不用 MeshCollider ,以節省不必要的開銷。如果不能避免的話,儘量用減少 Mesh 的面片數,或用較少面片的代理體來代替。

10、 其他
Drawcall
儘可能地減少 Drawcall 的數量。 
iOS 設備上建議不超過 100 。

減少的方法主要有如下幾種: Frustum Culling , Occlusion Culling , Texture Packing 。

Frustum Culling 是 Unity 內建的,我們需要做的就是尋求一個合適的遠裁剪平面; Occlusion Culling ,遮擋剔除, Unity 內嵌了 Umbra ,一個非常好 OC 庫。但 Occlusion Culling 也並不是放之四海而皆準的,有時候進行 OC 反而比不進行還要慢,建議在 OC 之前先確定自己的場景是否適合利用 OC 來優化;
Texture Packing ,或者叫 Texture Atlasing ,是將同種 shader 的紋理進行拼合,根據 Unity 的 static batching 的特性來減少 draw call 。建議使用,但也有弊端,那就是一定要將場景中距離相近的實體紋理進行拼合,否則,拼合後很可能會增加每幀渲染所需的紋理大小,加大內存帶寬的負擔。
這也就是爲什麼會出現“ DrawCall 降了,渲染速度也變慢了”的原因。

非運動物體儘量打上 Static 標籤
Unity 在運行時會對 static 物體進行自動優化處理,所以應該儘可能將非運行實體勾上 static 標籤。

場景中儘可能地使用 prefab
儘可能地使用 prefab 的實例化物體,以降低內存帶寬的負擔。檢查實體的 PrefabType ,儘量將其變成 PrefabInstance ,而不是 ModelPrefabInstance 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章