Unity3D優化專題

Unity3D優化專題

來自專欄 Unity3D從入門到放棄4 人贊了文章

一:遮擋剔除OcclusionCulling

二:層級消隱

三:層級細節LOD

四:DrawCall講解

五:Profile 工具使用

六:常見優化策略


一:遮擋剔除OcclusionCulling

遮擋剔除 (Occlusion Culling) 功能可在對象因被其他物體遮擋,當前在相機中無法看到時,禁用對象渲染。該功能不會在三維計算機圖形中自動開啟,因為在大部分情況下,離相機最遠的對象最先渲染,離相機近的對象覆蓋先前的物體(該步驟稱之為「重複渲染 (overdraw)」)。遮擋剔除 (Occlusion Culling) 與視錐體剔除 (Frustum Culling) 不同。視錐體剔除 (Frustum Culling) 只禁用相機視野外的對象渲染,不禁用視野中被遮擋的任何物體的渲染。注意,使用遮擋剔除 (Occlusion Culling) 功能時,仍將受益於視錐體剔除 (Frustum Culling)。

當場景中包含大量模型時,勢必會造成渲染效率的降低。如果使用遮擋剔除技術,可以使那些被阻擋的物體不被渲染,從而達到提高渲染效率的目的。

遮擋剔除的基本原理是在場景中創建一個遮擋區域,該遮擋區域由單元格組成;每個單元格構成整個場景遮擋區域的一部分,這些單元格會把整個場景拆分為多個部分。當攝像機能夠看到該單元格時,單元格中的物體就會被渲染出來,而被其他單元格擋住的不被攝像機看到的物體不會被渲染。

下面,我們來做遮擋剔除的案例

(1)首先構建一個高樓林立的虛擬世界。

(2)除了主角,攝像機,直線光,地面,把層級視圖中所有對象標記為遮擋靜態。

特別提示:當我們選擇一個包含多個字對象的父對象,會出現是否修改子對象的提示,根據自己的需求修改即可。

(3)執行菜單命令window->OculusionCulling, 彈出遮擋剔除面板,單機Bake按鈕,開始烘焙。

(4)單擊遮擋剔除窗口的Visualizatior運行程序,進行測試。修改第一人稱的攝像機位置。

當攝像機低頭看地的時候,整個遊戲場景的所有遊戲對象都無需渲染。向左看,右邊的無需渲染。雖然場景中的大部分對象都在攝像機範圍之內,但是由於面前對象的遮擋,後面的高樓是不顯示的。

二:層級消隱

如果場景中存在大量的小物體,則可以使用層消隱優化場景。層消隱就是在比較遠的距離將小物體剔除,減少繪製調用的次數。例如,在比較遠的距離,大型建築物依然可見,但是小型的石塊和碎片隱藏掉。可以將小物件單獨的放入一個層,並且使用Camera.main.layerCullDistance函數設置層的消隱距離。調整攝像機位置進行測試即可。只有在攝像機距離這些物體小於10M的時候,地面上的這些物體才能顯示出來。

public class SeperateControl : MonoBehaviour {? // Use this for initialization? void Start () {? float[] distances = new float[32];? distances [8] = 10;? Camera.main.layerCullDistances = distances;?? }?}

三:層級細節LOD

層級細節LOD全稱為LevelOfDetail,它是根據物體在遊戲畫面中所佔據的百分比來調用不同複雜度的模型的。簡單理解就是當一個物體距離攝像機比較遠的時候,使用複雜度低的模型,比較近的時候,使用複雜度高的模型。

在建模軟體中,製作好各個層級的模型,並且根據複雜程度自高向低命名為:模型名稱_LOD0,模型名稱_LOD1,模型名稱_LOD2,數字越低,複雜程度越高。

我們新建一個場景,構造最簡單的LOD模型示例。

(1)準備3個Unity基本遊戲對象,添加必要的材質。

(2)定義一個空對象,命名為_LOD,添加LODGroup組件

(3)分別將以上三個基本對象拖拽到LODGroup的各個級別上

(4)首先添加LOD0的對象,當然中間需要修改父節點,點擊確定即可

(5)在Scene視圖中,拖動攝像機分別近距離與遠距離觀察模型的變化。

四:DrawCall講解

一個DrawCall,表示U3D使用這個材質/紋理,來進行一次渲染,那麼這次渲染假設有3個對象,那麼當3個對象都使用這一個材質/紋理的時候,就會產生一次DrawCall,可以理解為一次將紋理輸送到屏幕上的過程,(實際上引擎大多會使用如雙緩衝,緩存這類的手段來優化這個過程,但在這裡我們只需要這樣認識就可以了),假設3個對象使用不同的材質/紋理,那麼無疑會產生3個DrawCall。

Unity在運行時可以將一些物體進行合併,從而用一個繪製調用來渲染他們。這一操作,我們稱之為「批處理」。一般來說,Unity批處理的物體越多,你就會得到越好的渲染性能

下面我們通過具體的例子來觀察DrawCall的變化。

(1)創建3個物體A,B,C,同時創建三種材質A,B,C。如下圖所示。記得關閉光源和天空盒子。

(2)將A和C的材質都修改為A材質,觀察Stats變化,發現Batches從剛才的3變為2,因為同一個材質進行一次批處理即可。

(3)A和C採取相同的材質,但是修改A和C為不同的網格模型。修改為Sphere的時候,發現批次沒有變化還是3次,修改為Cube的時候,還是2次。為什麼呢?

批處理動態物體需要在每個頂點上進行一定的開銷,所以動態批處理僅支持小於900頂點的網格物體,如果你的著色器使用頂點位置,法線和UV值三種屬性,那麼你只能批處理300頂點以下的物體;請注意:屬性數量的限制可能會在將來進行改變。

(4)相同的物體採取相同的材質,修改其縮放比例,觀察DrawCall的變化。

新版本官網上的介紹中去掉了縮放的限制,增加了一條對於鏡像物體無法進行批處理,比如Scale為(1,1,1)的物體與Scale為(-1,1,1)的物體無法進行批處理。

上面我們講述的例子主要是使用到了動態批處理,降低DrawCall,下面我們看一下靜態批處理。

功能描述如下: Static Batching 是將標明為 Static 的靜態物件,如果在使用相同材質球的條件下,Unity 會自動幫你把這兩個物件合併成一個 Batch,送往 GPU 來處理。

Static Batching可以讓引擎降低任何尺寸網格的Draw Call,如下圖所示:

靜態合批,要共享材質,也就是多個物體,只用1個材質,所以需要組合貼圖。

靜態合批,會增加內存,因為多出來 combine Mesh。

統計信息窗口包含以下信息:-

  • Time per frame and FPS 處理和渲染一個遊戲幀 (是倒數,每秒幀數) 所需的時間量。請注意,這一數字只包括做幀更新和渲染遊戲視圖; 所需的時間它不包括在編輯器中繪製 scene 視圖、 inspector 和其他編輯器處理的時間。
  • Draw Calls meshes網格繪製應用批處理後的總數。請注意,在多次呈現對象(例如,由像素燈照明的對象),每個在一個單獨的渲染結果繪製調用。
  • Batched (Draw Calls) 最初分開的draw calls被添加到batches。「Batching」是引擎將嘗試結合多個對象到一個繪製調用以減少 CPU 開銷的呈現。以確保好的batching,應該分享儘可能多的不同對象之間的材料。
  • Tris and Verts 三角形和頂點繪製的數目。這主要為低端硬體優化
  • Screen 屏幕大小,以及其(anti-aliasing )抗鋸齒級別和內存使用情況。
  • SetPass 渲染改變( passes)次數。每個改變 需要Unity運行時綁定一個新的渲染器(shader),它可能會引入 CPU 開銷。
  • Visible Skinned Meshes skinned meshes 渲染的數量
  • Animations 動畫播放次數

五:Profile 工具使用

性能分析工具可以給我們提供遊戲性能表現的詳細信息。如果我們的遊戲存在性能問題,如低幀率或者高內存佔用,性能分析工具可以幫助我們發現問題的起因,並協助我們解決問題。

Profiler工具是Unity內置的強大的性能分析工具,本文介紹如何使用它。當我們閱讀完本文,並且熟悉Profiler的界面和功能時,我們可以繼續學習怎麼使用它對不同類型的性能問題進行診斷。

Profiler可以給我們提供,關於我們的遊戲的不同部分是怎樣運行的深入的信息。

使用Profiler我們可以學習遊戲性能的不同方面,例如我們的遊戲如何使用內存,不同的任務使用了多少cpu時間,物理運算執行的有多頻繁。最重要的是,我們可以利用這些數據找到引起性能問題的原因,並且測量我們的解決方案的有效性。

Profiler布局

在我們使用Profiler收集遊戲數據之前,先打開它熟悉下界面布局。從菜單Window > Profiler打開。

在窗口左側,可以看到一列profilers,每個profiler顯示我們遊戲的一個方面的信息,分別為cpu使用情況,gpu使用情況,渲染,內存使用情況,聲音,物理和網路。

當開始錄製時,窗口上部的每個profiler會隨著時間顯示數據。性能是隨著時間變化的,所以隨著時間變化的信息是比僅僅一幀的信息有用的多的。有些性能問題是持續性的,有些問題是僅僅在一幀中出現的,還有一些性能問題是隨著時間逐漸顯現的。

Profiler的下半部顯示我們選擇的當前profiler當前幀的詳細信息。

這裡顯示的數據依賴於我們當前選擇的profiler。例如,當選中內存profiler時,這個區域顯示如遊戲資產使用的內存和總共內存佔用等。如果選中渲染profiler,這裡會顯示被渲染的對象數量或者渲染操作執行次數等數據。

這些profiler會提供很多詳細信息,但是我們並不總是需要使用這些所有的profiler。事實上,我們通常在分析遊戲性能時只是觀察一個或者兩個profiler。例如,當我們的遊戲運行的比較慢時,我們可能一開始先查看cpu usage profiler。

cpu usage profiler給我們一個總覽,可以觀察到我們遊戲的哪個部分佔用了最多的cpu時間。然後我們可以查看那個部分相關的profiler。例如我們發現物理運算函數佔用了很長時間,那麼我們就需要使用物理profiler去獲取更多的詳細信息。

我們可以關閉一些我們不關心的profiler,通過點擊x按鈕就可以關閉。

錄製分析數據

現在我們理解了profiler的界面布局,讓我們繼續學習如何錄製數據,以及如何解讀數據來幫助我們理解遊戲的性能。

我們需要理解,當錄製數據時,遊戲性能會受到微小的影響,一般的性能分析工具都存在這個問題,想要獲取深入的信息而沒有額外的消耗是不可能的。

我們可以在unity editor中運行遊戲時進行分析,也可以在遊戲的development build運行時進行分析。development build的unity遊戲和常規build有兩方面不同:development build在遊戲運行時可以連接profiler,並且包含了調試用的文件。

  • 在Unity Editor中進行分析

在unity editor中進行錄製分析的步驟如下:

-在unity中打開遊戲工程

-菜單中打開profiler Window > Profiler

-確保Profiler窗口頂部的Record按鈕為選中狀態

-在Play Mode中運行遊戲

此時Profiler會隨著遊戲中的互動實時的顯示分析數據。

  • Windows, OSX, Linux and WebGL

步驟如下:

-在unity中打開想要分析的項目

-菜單中打開profiler Window > Profiler

-確保Profiler窗口頂部的Record按鈕為選中狀態

-打開build settings(File > Build Settings)

-勾選Development Build

-勾選Autoconnect Profiler

-點擊Build and Run

使用Profiler診斷問題

現在我們理解了Profiler是怎樣工作的,我們可以使用它來確定我們遊戲中的問題,並協助我們解決這些問題。

如果你的遊戲運行慢,卡頓,大家就可以使用Profile進行調試了。

六:常見優化策略

項目優化策略

項目優化技能是優秀研發人員的基本素質,除了以上我們介紹的兩種主要性能優化方式與性能檢測工具。下面還有豐富的經驗與優化建議與大家共享。下面從六個方面進行歸納與總結

  • DrawCall
  • 模型/圖像方面
  • 光照與攝像機處理
  • 程序優化方面
  • Unity系統設置
  • 開發與使用習慣

一:DrawCall優化

對於Unity研發人員來說,系統的性能優化幾乎等同於DrawCall優化,重要性可見一斑。

一個模型的數據經過CPU傳遞到GPU,並且命令GPU進行繪製,稱為一個DrawCall。

Unity引擎準備數據並且渲染對象的過程是逐個對象進行的,所以對於每個遊戲對象,不僅GPU的渲染很耗時,引擎重設材質與Shader也是一項非常耗時的操作。因此,每一幀的drawcall是一個非常重要的性能指標

降低DrawCall的基本原理基於DrawCall是CPU調用底層圖形介面,對於GPU來說,一個對象與大量遊戲對象,其圖形處理的工作量是一樣的。所以對於DrawCall的優化,主要工作量就是為盡量減少CPU在調用圖形介面上的開銷而努力。針對drawCall,我們的主要思路是,每個遊戲對象盡量減少渲染次數,多個遊戲對象盡量一起渲染。

降低DrawCall的主要途徑

一般項目中,角色和場景是消耗資源最多的兩個方面,其中角色是CPU的瓶頸,場景是GPU的瓶頸。所以項目的優化就是降低DrawCall。總體思路就是對美術資源進行合併,大量合併drawcall、人物角色減少材質與紋理的依賴,簡化多餘特效等。當然也可以允許玩家在低端設備上關閉一些特效換取更佳流暢的性能。

下面通過10個途徑來進行詳細討論

1:DrawCall批處理技術(DrawCall Batching)

Unity運行的時候可以將一些遊戲對象進行合併,也就是把多個遊戲對象打包,然後再使用一個DrawCall來渲染他們,這個操作稱為批處理。

批處理的核心在於可見性測試之後,檢查所有要繪製的對象材質,把相同材質的合為一個對象,這樣就可以在一個call裡面處理多個對象了。

Unity提供了靜態批處理和動態批處理兩種方式,動態批處理是完全自動進行的,不需要也無法進行任何干預,對於頂點數在900以內的可移動物體,只要使用相同的材質,就會組成批處理。靜態批處理則需要把靜態的物體標記為靜態,然後無論大小,都會進行批處理。

為了更好的使用靜態批處理,需要明確指出哪些物體是靜止的,並且在遊戲中永遠不會移動、旋轉與縮放。在屬性窗口將static勾選即可。所以應該盡量將非運動對象設置為static。

2:使用圖集減少材質的使用

Unity判斷哪些對象進行批處理時候,一般根據這些對象是否具有共同的材質和貼圖,也就是說擁有相同材質的對象才可以進行批處理。因此,盡量復用材質到不同的對象上。

3:盡量少使用反光陰影,因為會使物體多次渲染

4:視錐體合理裁切

視錐體合理裁切是Unity自帶的功能,我們需要做的就是尋求一個合理的裁剪平面。一般來說,對於大型場景中的大量遊戲對象進行合理分層,對於大型建築物使用大型裁剪距離,對於小遊戲對象使用小裁剪距離,場景中的粒子對象使用更小的裁剪距離。

5:遮擋剔除

6:網格渲染器的控制

可以將暫時不進行顯示的對象render設置為unenable,需要的時候在顯示

7:減少遊戲對象的鏡像縮放

分別擁有大小(-1,1,1)和(1,1,1)的兩個對象將不會進行批處理。

8:減少多通道Shader的使用

多通道的Shader會防礙批處理操作

9:盡量多使用預設體

使用預設體的對象會自動使用相同的網格模型和材質,因此會自動被批處理。

二:項目優化之模型與圖像方面

模型與圖像方面的優化分為以下6個途徑進行討論

途徑1:模型優化

(1)模型幾何體的優化

模型的優化主要是模型的頂點,三角面數量。如果可能,把相近的合併為一個。比如森林與樹木,完全可以坐在一起

(2)壓縮面片:3D模型導入Unity之後,不影響顯示效果的前提下,最好打開MeshCompression,Off,Medium,Low,High幾個選項,根據需要進行設置。

(3)避免大量使用Unity自帶的Sphere對象

內建的部分遊戲對象,多邊形數量比較大,如果物體不要求特別圓滑,導入其他的模型替換。

途徑2:紋理優化

(1)使用貼圖壓縮優比:尺寸越小,壓縮比率越高的貼圖,佔用的內存空間越低,因此可以降低對他的渲染處理時間,同時減少遊戲文件的體積。

(2)貼圖壓縮格式選擇:紋理建議使用壓縮紋理。不透明貼圖的壓縮格式為ETC。安卓的GPU有多種,但是都支持ETC格式。透明貼圖,我們選擇RGBA 16或者32

(3)選擇支持MipMap

Mipmap技術有點類似於LOD技術,但是不同的是,LOD針對的是模型資源,而Mipmap針對的紋理貼圖資源

使用Mipmap後,貼圖會根據攝像機距離的遠近,選擇使用不同精度的貼圖。

缺點:會佔用內存,因為mipmap會根據攝像機遠近不同而生成對應的八個貼圖,所以必然占內存!

優點:會優化顯存帶寬,用來減少渲染,因為可以根據實際情況,會選擇適合的貼圖來渲染,距離攝像機越遠,顯示的貼圖像素越低,反之,像素越高!

MipMap可以用於跑酷類遊戲,當角色靠近時,貼圖清晰顯示,否則模糊顯示

如果我們使用的貼圖不需要這樣效果的話,就一定要把Generate Mip Maps選項和Read/Write Enabled選項取消勾選!因為Mipmap會十分佔內存!

mipMap會讓你的包占更大的容量!

下面來看下怎麼設置貼圖的mipmap:

設置貼圖的Texture Type為Advanced類型 → 勾選Generate Mip Maps → Apply應用

途徑3:材質

盡量合併使用相同材質球的對象,儘可能減少網路材質的使用數量。盡量使用合圖工具。

途徑4:碰撞體

如果可以,盡量不使用MeshCollider,如果不能優化,則盡量減少面數。車輪碰撞和布料也是盡量少用

途徑5:粒子系統

屏幕上的最大粒子數建議小於200,每個粒子發射器的最大發射數量建議不超過50.如果可以,粒子的size盡量小點。非常小的粒子,去掉alpha通道。不要開啟粒子的碰撞功能。

途徑6:其他

盡量使用預設體

三:光照與攝像機處理

分為三個途徑進行探討

途徑1:渲染路徑(RenderingPath)

Unity提供了不同的渲染路徑,這些渲染路徑用於決定燈光和陰影的計算方式,不同的渲染路徑具有不同的性能特性和渲染效果。Unity提供了幾種方法,分別是頂點光照(Vertex Lit),前向渲染(Forward Rending),延時光照(Deferred Lighting)

Vertex Lit:頂點光照。攝像機將對所有的遊戲對象座位頂點光照對象來渲染。

  Forward:快速渲染。攝像機將所有遊戲對象將按每種材質一個通道的方式來渲染。

  Deferred Lighting:延遲光照。攝像機先對所有遊戲對象進行一次無光照渲染,用屏幕空間大小的Buffer保存幾何體的深度、法線已經高光強度,生成的Buffer將用於計算光照,同時生成一張新的光照信息Buffer。最後所有的遊戲對象會被再次渲染,渲染時疊加光照信息Buffer的內容。

途徑2:光照與陰影

使用光照烘焙技術。

光線性能消耗佔用順序為:聚光燈-點光源-平行光。盡量使用小的點光源,影響的物體少,不在範圍的不受影響。一個網格在有8個以上光源影響的時候,只響應前8個最亮的光源。

如果硬陰影可以解決問題,不要使用軟陰影,並且使用不影響效果的低解析度陰影。實時陰影很消耗性能,允許的話在大場景中使用線性霧,這樣可以使的遠距離對象或者陰影不易察覺,可以減少攝像機的遠裁切距離和陰影距離提高性能。

質量設置面板的ShadowDistance屬性設置陰影的顯示距離,該距離是根據當前攝像機參考的。當可以生成陰影的物體與當前攝像機距離超過該值時候,就不產生陰影。

途徑3:攝像機技巧

層級消隱

四:程序優化方面

從四個方面討論這個問題

1:程序整體優化方面

(1)如果項目的瓶頸不在渲染,那麼肯定就在腳本了。我們要刪除腳本中不用或者為空的默認方法,盡量少在Update裡面做事情,腳本不用時候禁止。

(2)盡量不使用原生GUI,使用NGUI或者UGUI

(3)需要隱藏或者顯示來回切換的對象,少用active,可以將其移出攝像機的範圍。也可以使用腳本方式開啟或者關閉遊戲對象的MeshRender組件

(4)不要頻繁獲取組件,聲明為成員或者屬性

(5)腳本在不使用時候禁用,使用時候啟用

(6)盡量獲取一次腳本組件之後,使用變數保存

(7)盡量少使用update等每一幀調用的函數,節省電量

(8)使用C#的委託與事件機制,比使用SendMessage機制效率高多了。

2:事件函數方面

(1)按照腳本生命周期的原理,對於協程,調用函數,一般腳本禁用的時候,需要顯式禁用

(2)同一腳本頻繁使用的變數,聲明為全局;腳本之間頻繁使用的,聲明為靜態

(3)盡量避免每一幀處理,可以每隔幾幀處理一次

function Update(){if(Time.frameCount%100==0){ DoSomething()}}

(4)使用協同或者InvokeRepeating代替不必每幀都執行的函數

(5)避免在Update等函數中使用搜索方法,比如GameObject.Find()。良好的代碼是把搜索放在單次執行的函數裡面,比如 Start();

3:數學計算方面

盡量使用int代替float,少用正弦等三角函數,盡量少用除法運算和取模運算

4:垃圾回收方面

(1)盡量主動回收垃圾

(2)回收的時機很重要。盡量放在遊戲場景的載入與場景結束的時候,主動卸載資源。

(3)避免頻繁分配內存:使用對象池

(4)在較大的場景中,距離攝像機遠的對象可以將上面的腳本禁用。需要啟用再次setActive即可。這樣也可以配合事件函數中的OnBecameInvisible和OnBecameVisible,使的可見時候啟用,不可見禁用。

(5)善於使用OnBecameInvisible和OnBecameVisible控制物體Update的執行

(6)資源預先載入技術

五:項目優化之Unity系統設置

1:限幀措施

手機上主動減少FPS,能夠顯著的減少發熱和耗電,穩定遊戲FPS。

具體方法:

Sync Count會影響你的FPS,EveryVBlink相當於FPS=60;EverySecond VBlink相當於30。這兩種情況如果都不符合我們的FPS,那麼我們需要手動設置FPS。首先關閉垂直同步功能,然後在代碼的Awake函數裡面手動設置FPS(Application.targetFrameRate=45).

2:物理性能優化

1)增加固定時間步長

設置為0.04-0.06之間即可。降低了物理引擎剛體更新的頻率

2)設置最大允許時鐘回調

物理計算和FixedUpdate執行不會超過該值制定的時間。使的在最壞的情況下封頂物理計算時間

3)設置時間縮放因子

3:調整像素光數量:使遊戲看起來更多彩

六:項目優化之良好開發習慣與使用習慣

(1)養成良好的標籤,層次,Layer條理化習慣,將不同的對象置於不同的標籤或者圖層,三者的有效結合將按照名稱,類別和屬性進行查找

(2)研發過程中經常通過Status或者Profile查看對效率影響最大的方面或對象,而不是等到項目發布再去解決。

(3)採取合適的項目框架,便於維護與協作開發


推薦閱讀:

如何實現切割一張圖片?
Unity接入多個SDK的通用介面開發與資源管理(二)
給unity新手找不到工作的人傳授幾招
(轉)乾貨:Unity遊戲開發圖片紋理壓縮方案
(轉)遊戲開發 應用Docker實現開發環境

TAG:Unity遊戲引擎 | Unity桌面環境 | unity |