Unity性能優化
大的方面來說,通過
Unity
對于項目的性能優化大概可以分為下面幾個部分:
- 資源
- 渲染
- 程式
- 項目配置
而在這個部分中,資源的性能優化屬于最基礎、最有效的優化手段,也是遊戲開發者日常開發最需要注意的一部分,是以本篇文章就簡單的介紹一下對資源進行操作時需要關注哪些點
一、紋理(簡單來說就是圖檔)
紋理的資源優化主要集中于下面的幾點:
- 紋理大小
- 壓縮格式
- 導入設定
通常,在遊戲運作時,大部分記憶體都是用在了紋理上,是以你的導入設定非常關鍵,我們可以在
Inspector
面闆看到圖檔資源的資訊,類似于下面這張圖:
從性能優化的角度看,紋理導入需要遵循如下的原則:
- 降低最大分辨率
- 采用二次幂壓縮格式:
- 制作紋理圖集
- 取消勾選
Read/Write Enabled
- 禁用多餘的
:Mip Map
貼圖在2D精靈和Mip Map
圖形這類大小始終一直的紋理上并無用處UI
1、降低最大分辨率
很好了解,紋理分辨率越大,在遊戲運作時,占用的記憶體資源越大,并且占用的記憶體量是與其分辨率的平方成正比的。也就是說,分辨率變為原來的二倍,那麼記憶體量就需要消耗四倍。是以需要從資源優化的角度來講,需要對其進行一定的限制,簡單的來說,一個
Button
的紋理通常低于
128
X
128
,如果使用
1024
X
024
規格的大小就造成了性能的浪費
如下面的圖,我們可以在圖檔資源的
Inspector
面闆中最下面的屬性中看到它:
Max Size
,同時可以看到可以根據不同平台來選擇不同的最大分辨率:
第一張圖檔:
Max Size
調整為
1024
,紋理資源大小為
4.9MB
第二張圖檔:
Max Size
調整為
512
,圖檔資源大小為
1.3MB
2、對于紋理的壓縮
在你主動選擇壓縮格式之前,
Unity
本身會對圖檔做一些處理,無論你放入的是
PNG
、
JPG
、
PSD
或者
TGA
,
Unity
都會手動幫助我們調整為
Texture 2D
,這是一種簡單的排程政策:
那麼既然
Unity
本身都已經智能轉換好了,為什麼還要給予開發者選擇壓縮格式的選擇呢,直接對
Texture 2D
封裝好壓縮方法不就可以了嗎?
其實
Unity
相對于其他開發引擎有一個很明顯的優勢,就是多适配性,那麼為了實作這種多适配性,對于單一平台的針對性就會相對減弱,而不同平台的性能表現又不盡相同,就需要開發者來根據平台特點來選擇針對遊戲平台的壓縮格式。
同樣,即使在同一平台,也會根據不同的情況有着不同的壓縮需求,比如有一些關鍵的首頁面圖檔,玩家感覺強的地方,就對圖檔的品質要求高些,一些邊角的輔助圖檔,可能要求就低一些,如果都使用高品質壓縮,性能方面就造成了浪費,但若都是用低品質壓縮,品質又跟不上。是以,同樣需要根據實際需求選擇不同的壓縮格式。
在說到
Unity
圖檔壓縮時,經常會看到這樣一張圖,來介紹不同壓縮格式的特點與适用場景,我也是百度圖檔直接爬取的這張圖檔,大家可以參考着來看(如果有侵權,請告知我,我會立刻删除)
在了解上面這張圖之前做一個簡單的計算,一張
1024
乘
1024
的
RGBA32
格式圖檔的占用存儲空間為:
由于
RGBA32
一個像素每一個顔色值是由兩個16進制的數組成,也就是8位,那個一個像素就是8位乘以4個顔色值
R
、
G
、
B
、
A
得到的是4個位元組,即
4B
,然後乘上
1024
*
1024
個像素,最終得到的大小為
4MB
,也就是4兆。很明顯這是很恐怖的一個數字,要知道現在移動端手機的運作記憶體大概
6G
,除去系統的占用記憶體,真正給遊戲用的最多也就4G`,再配置設定給GPU|的顯存就更少了,而若這些記憶體被用來大量加載貼圖很明顯是不能被接受的。同時,這樣資料量的圖檔加載也會給記憶體
基于上面原生紋理帶來的包體與記憶體問題,就需要根據不同的情況采取一些壓縮政策,由于本人基本沒有美術功底,是以對與各種壓縮格式的美術呈現效果不是很了解,主要是從功能性與性能的角度來分析各種壓縮格式的适用場景:
- 高品質壓縮格式:
作為一種高保真的壓縮格式,能夠極大的保證圖檔品質RGBA32
- 中品質壓縮格式:
+RGBA16
一聽就是Dithering
的閹割版,簡單來說,相比于RGBA32
其色彩細分程度大,可以明顯的看出階梯感,視覺表現相對于RGBA32
不夠平滑RGB32
- 低品質壓縮格式:
+ETC1
/Alpha
這些壓縮格式往往是移動段最常用的壓縮格式,其相對于其他壓縮格式有着無可比拟的性能優勢PVRTC4
注意:
- 除了由于壓縮邏輯不同帶來的加載帶寬減少之外,同時還需要了解像
、
ETC1
等這類在記憶體中不需要進行解壓,而是可以直接被
PVRTC4
支援,是以相比其他壓縮格式通常會有最好的性能表現
GPU
3、取消勾選Read/Write Enabled
該功能是為了使得遊戲開發者可以通過
C#
腳本調用對與圖檔的讀取與寫入的控制,很明顯,這是由CPU來控制實作的,是以為了可以使得
CPU
擷取資料,需要在記憶體中備份一份讓
CPU
通路。同時為了圖形渲染與顯示,又會将其加載到顯存中為
GPU
提供資料。
簡單來說,該選項會在遊戲運作時,分别在
CPU
記憶體與
GPU
記憶體中備份出一張貼圖,如果你并不需要對于紋理進行讀寫操作,可以嘗試關閉該選項,這樣就可以避免遊戲運作時占用多餘的記憶體
4、禁用多餘的Mip Map
Mip Map
類似于模型的
LOD
,同樣是一種基于渲染距離改變渲染貼圖精度的技術。其優勢是在物體距離渲染距離比較遠時,可以節省性能。但是使用
Mip Map
時會增大記憶體占用量。
Mip map
的技術原理是根據原始圖進行2的幂次方的遞減來生成一組不同精度的圖檔。當遊戲運作時,會将這組圖檔加載到記憶體中,然後根據渲染的距離不同,來使用不同精度的圖檔。
Mip map會增大多大的記憶體占用量呢
- 在我們使用
時,假設大小為
Mip map
,并且會生成
256X256
張不同精度的圖檔。根據
8
的幂次方進行遞減計算每張貼圖大小并累加。這樣最終得到的圖檔組的體積大概比原來的單張貼圖大
2
33%
通過一個執行個體來驗證,假設原始圖檔大小為
8M
,生成的第一張低精度圖檔的大小為
2M
(分辨率減少一半,大小就會變為原來的四分之一,很好了解)這樣大概遞減
8
次,然後累加,就會擷取最終的圖檔組大小,通過一個簡單的遞歸方法來計算一下:
public void Awake()
{
Debug.Log(GetMipmapSize(8, 8)/8);
}
// index:處理總層級數 imageSize:初始大小 傳回增大的記憶體量
float GetMipmapSize(int index,float imageSize)
{
float lastSize = 0;
if (index == 1)
{
lastSize = imageSize*0.25f;
}
if (index > 1)
{
lastSize = imageSize * 0.25f + GetMipmapSize(index - 1, imageSize * 0.25f);
}
return lastSize;
}
執行程式,得到的結果為:
可以看到,求到的結果是接近于
33%
,當然如果
Mip Map
對一張紋理的處理不是八次,計算的結果會有偏差但是實際上,從三次往上,記憶體占用量的增加比例已經非常少了,基本都維持在
33%
左右,将上面的代碼稍微修改,做個小驗證:
public void Awake()
{
for (int i = 3; i < 15; i++)
{
Debug.Log(string.Format("處理 {0} 次記憶體占用量為: {1}",i,GetMipmapSize(i, 8) / 8));
}
}
float GetMipmapSize(int index,float imageSize)
{
float lastSize = 0;
if (index == 1)
{
lastSize = imageSize*0.25f;
}
if (index > 1)
{
lastSize = imageSize * 0.25f + GetMipmapSize(index - 1, imageSize * 0.25f);
}
return lastSize;
}
得到的日志為:
可以發現,從三層開始,基本就維持在
33%
的記憶體占用量,并且在層數逐漸增大的時候,記憶體占用量增加量就非常非常少了。
而
Unity
所支援的紋理最小為
32X32
,也就是最小的紋理也會額外的産生三層低精度紋理:
16X16
、
4X4
、
1X1
,這就是為什麼很多文章介紹到使用
Map mip
會說其大約會增加
33%
的記憶體占用量
雖然
Mip map
本身是一種性能優化的技術,但是在
2D
精靈或者
UI
元素這些不會改變渲染精度的紋理上,隻會占用多餘的記憶體,是以在
2D
精靈或者
UI
元素上使用紋理時記得不要勾選
Mip map
。
5、打包圖集
圖集的打包主要是優化UI圖形渲染過程中Draw call的數量,其基本原理也是通過UI元素合批來減少Draw Call,進入提升CPU的性能表現,關于其具體細節,可以檢視我之前的文章:
圖集打封包章:
- Unity 将Sprite打包進圖集
二、模型
相比與紋理,模型的性能表現更多的取決于美術規範,程式來講沒有更多可以優化的地方,但是在Unity中也有一些選項影響模型加載、渲染等方面的性能表現,我們可以在導入時看到:
1、禁用掉Reader/Write Enables:
點選模型,可以在
Inspector
面闆看到這些設定選項,類似于紋理,如果在遊戲中,你不需要對模型進行修改,可以禁用掉
Reader/Write Enables
來避免資料的備份而占用多餘的記憶體,我們可以在Unity官方文檔中找到相關介紹:
翻譯過來就是:
- 啟用此選項後,
會将Unity
資料上傳到Mesh
可尋址記憶體,但也會将其儲存在GPU
可尋址記憶體中。這意味着CPU
可以在運作時通路Unity
資料,可以從腳本中通路它。Mesh
- 而禁用此選項後,
會将Unity
資料上傳到Mesh
可尋址記憶體,然後将其從GPU
可尋址記憶體中删除CPU
- 預設情況下,此選項處于禁用狀态。在大多數情況下,要節省運作時記憶體使用量,請禁用此選項
而對于模型本身來說,盡量避免模型留有多餘的面數。尤其是移動端。因為高精度模型除了本身所帶來的壓力外,在其他方面也有諸多的性能挑戰
2、盡量不要勾選不需要的功能選項
在
Unity
中,某些功能即使你未使用到,也會 消耗一定的資源去維護其狀态。類似上面的
Reader/Write Enables
選項,是以用不到的功能我們可以考慮盡量的去禁用掉
3、設定一些關于品質與性能的選項
Unity
提供了一些對模型進行優化的選項,可以查閱
Unity
官方文檔來閱讀了解他們,這裡也簡要的列出:
通過上面一張圖檔可以看出,影響模型表現與遊戲性能的選項有下面幾個:
-
:通過使用網格邊界和每個元件較低的位深度來壓縮網格資料,增加壓縮率會降低網格的精度。最好在Mesh Compression
看起來與未壓縮版本沒有太大差別的情況下将其調得盡可能高。這對于優化遊戲大小很有用Mesh
-
:确定三角形在網格中列出的順序以獲得更好的Optimize Mesh
性能,預設都會勾選GPU
-
:如果網格模型既不是法線貼圖也不受實時光照影響,就選用Normals
,這樣也能夠很好的提升性能表現None
其實,
Unity
設定了一些通過程式控制模型品質來改變性能表現的選項,但是不建議使用,預期通過這些選項來調整性能表現,還不如直接讓美術直接處理模型。畢竟他們更加專業,可以更好的保證模型的表現效果與性能表現的平衡。
4、使用LOD
關于
LOD
,其實應該在渲染這一段來講,但是這個技術又與模型網格有很大的關系,是以提前介紹一下
LOD
即
Levels of Detail
,翻譯過來就是多層次細節,類似與紋理渲染的
Mip map
技術,同樣是一種根據渲染距離設定渲染精度的一種技術。其實作方式是在遊戲開發時,美術根據不同的渲染距離制作一組不同精度的模型,導入到
Unity
通過
LOD
元件連接配接其這一組模型,并設定相關參數。這樣在遊戲運作時,就會在不同的距離有不同的渲染精度:
這是
Unity
官方文檔的一個案例,可以看出,随着渲染距離的增加,渲染精度逐漸下降,直到最終被剔除,這樣做的優勢是保證遊戲畫面表現的同時,可以最大程度降低渲染壓力。簡單的了解,如果不采用
LOD
,随着距離增加,物體占用的螢幕像素就會越少,那麼機關像素的三角面數就會越多。機關的渲染壓力就會增大。畫面表現需求不高的地方渲染壓力反而更高,這顯然是不合理的。是以就需要通過
LOD
來解決這樣的問題。
當然這種技術本身也是有相當大的缺陷的,首先就是會增大包體的體積,同時也會增加美術的工作量。是以在實際開放中,一般隻會對一些重要的對象使用該技術
總結
上面所介紹的關于資源影響遊戲性能的一些常用的點,都是遊戲開發者日常接觸最多的,更深入,更底層的就需要根據項目的特點進行專門的适配與調整。