天天看點

Unity 性能優化:資源篇

Unity性能優化

大的方面來說,通過

Unity

對于項目的性能優化大概可以分為下面幾個部分:

  • 資源
  • 渲染
  • 程式
  • 項目配置

而在這個部分中,資源的性能優化屬于最基礎、最有效的優化手段,也是遊戲開發者日常開發最需要注意的一部分,是以本篇文章就簡單的介紹一下對資源進行操作時需要關注哪些點

一、紋理(簡單來說就是圖檔)

紋理的資源優化主要集中于下面的幾點:

  • 紋理大小
  • 壓縮格式
  • 導入設定

通常,在遊戲運作時,大部分記憶體都是用在了紋理上,是以你的導入設定非常關鍵,我們可以在

Inspector

面闆看到圖檔資源的資訊,類似于下面這張圖:

Unity 性能優化:資源篇

從性能優化的角度看,紋理導入需要遵循如下的原則:

  • 降低最大分辨率
  • 采用二次幂壓縮格式:
  • 制作紋理圖集
  • 取消勾選

    Read/Write Enabled

  • 禁用多餘的

    Mip Map

    Mip Map

    貼圖在2D精靈和

    UI

    圖形這類大小始終一直的紋理上并無用處

1、降低最大分辨率

很好了解,紋理分辨率越大,在遊戲運作時,占用的記憶體資源越大,并且占用的記憶體量是與其分辨率的平方成正比的。也就是說,分辨率變為原來的二倍,那麼記憶體量就需要消耗四倍。是以需要從資源優化的角度來講,需要對其進行一定的限制,簡單的來說,一個

Button

的紋理通常低于

128

X

128

,如果使用

1024

X

024

規格的大小就造成了性能的浪費

如下面的圖,我們可以在圖檔資源的

Inspector

面闆中最下面的屬性中看到它:

Max Size

,同時可以看到可以根據不同平台來選擇不同的最大分辨率:

第一張圖檔:

Max Size

調整為

1024

,紋理資源大小為

4.9MB

Unity 性能優化:資源篇

第二張圖檔:

Max Size

調整為

512

,圖檔資源大小為

1.3MB

Unity 性能優化:資源篇

2、對于紋理的壓縮

在你主動選擇壓縮格式之前,

Unity

本身會對圖檔做一些處理,無論你放入的是

PNG

JPG

PSD

或者

TGA

Unity

都會手動幫助我們調整為

Texture 2D

,這是一種簡單的排程政策:

Unity 性能優化:資源篇

那麼既然

Unity

本身都已經智能轉換好了,為什麼還要給予開發者選擇壓縮格式的選擇呢,直接對

Texture 2D

封裝好壓縮方法不就可以了嗎?

其實

Unity

相對于其他開發引擎有一個很明顯的優勢,就是多适配性,那麼為了實作這種多适配性,對于單一平台的針對性就會相對減弱,而不同平台的性能表現又不盡相同,就需要開發者來根據平台特點來選擇針對遊戲平台的壓縮格式。

同樣,即使在同一平台,也會根據不同的情況有着不同的壓縮需求,比如有一些關鍵的首頁面圖檔,玩家感覺強的地方,就對圖檔的品質要求高些,一些邊角的輔助圖檔,可能要求就低一些,如果都使用高品質壓縮,性能方面就造成了浪費,但若都是用低品質壓縮,品質又跟不上。是以,同樣需要根據實際需求選擇不同的壓縮格式。

在說到

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;

    }
           

執行程式,得到的結果為:

Unity 性能優化:資源篇

可以看到,求到的結果是接近于

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;
    }
           

得到的日志為:

Unity 性能優化:資源篇

可以發現,從三層開始,基本就維持在

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中也有一些選項影響模型加載、渲染等方面的性能表現,我們可以在導入時看到:

Unity 性能優化:資源篇

1、禁用掉Reader/Write Enables:

點選模型,可以在

Inspector

面闆看到這些設定選項,類似于紋理,如果在遊戲中,你不需要對模型進行修改,可以禁用掉

Reader/Write Enables

來避免資料的備份而占用多餘的記憶體,我們可以在Unity官方文檔中找到相關介紹:

Unity 性能優化:資源篇

翻譯過來就是:

  • 啟用此選項後,

    Unity

    會将

    Mesh

    資料上傳到

    GPU

    可尋址記憶體,但也會将其儲存在

    CPU

    可尋址記憶體中。這意味着

    Unity

    可以在運作時通路

    Mesh

    資料,可以從腳本中通路它。
  • 而禁用此選項後,

    Unity

    會将

    Mesh

    資料上傳到

    GPU

    可尋址記憶體,然後将其從

    CPU

    可尋址記憶體中删除
  • 預設情況下,此選項處于禁用狀态。在大多數情況下,要節省運作時記憶體使用量,請禁用此選項

而對于模型本身來說,盡量避免模型留有多餘的面數。尤其是移動端。因為高精度模型除了本身所帶來的壓力外,在其他方面也有諸多的性能挑戰

2、盡量不要勾選不需要的功能選項

Unity

中,某些功能即使你未使用到,也會 消耗一定的資源去維護其狀态。類似上面的

Reader/Write Enables

選項,是以用不到的功能我們可以考慮盡量的去禁用掉

3、設定一些關于品質與性能的選項

Unity

提供了一些對模型進行優化的選項,可以查閱

Unity

官方文檔來閱讀了解他們,這裡也簡要的列出:

Unity 性能優化:資源篇

通過上面一張圖檔可以看出,影響模型表現與遊戲性能的選項有下面幾個:

  • Mesh Compression

    :通過使用網格邊界和每個元件較低的位深度來壓縮網格資料,增加壓縮率會降低網格的精度。最好在

    Mesh

    看起來與未壓縮版本沒有太大差別的情況下将其調得盡可能高。這對于優化遊戲大小很有用
  • Optimize Mesh

    :确定三角形在網格中列出的順序以獲得更好的

    GPU

    性能,預設都會勾選
  • Normals

    :如果網格模型既不是法線貼圖也不受實時光照影響,就選用

    None

    ,這樣也能夠很好的提升性能表現

其實,

Unity

設定了一些通過程式控制模型品質來改變性能表現的選項,但是不建議使用,預期通過這些選項來調整性能表現,還不如直接讓美術直接處理模型。畢竟他們更加專業,可以更好的保證模型的表現效果與性能表現的平衡。

4、使用LOD

關于

LOD

,其實應該在渲染這一段來講,但是這個技術又與模型網格有很大的關系,是以提前介紹一下

LOD

Levels of Detail

,翻譯過來就是多層次細節,類似與紋理渲染的

Mip map

技術,同樣是一種根據渲染距離設定渲染精度的一種技術。其實作方式是在遊戲開發時,美術根據不同的渲染距離制作一組不同精度的模型,導入到

Unity

通過

LOD

元件連接配接其這一組模型,并設定相關參數。這樣在遊戲運作時,就會在不同的距離有不同的渲染精度:

Unity 性能優化:資源篇

這是

Unity

官方文檔的一個案例,可以看出,随着渲染距離的增加,渲染精度逐漸下降,直到最終被剔除,這樣做的優勢是保證遊戲畫面表現的同時,可以最大程度降低渲染壓力。簡單的了解,如果不采用

LOD

,随着距離增加,物體占用的螢幕像素就會越少,那麼機關像素的三角面數就會越多。機關的渲染壓力就會增大。畫面表現需求不高的地方渲染壓力反而更高,這顯然是不合理的。是以就需要通過

LOD

來解決這樣的問題。

當然這種技術本身也是有相當大的缺陷的,首先就是會增大包體的體積,同時也會增加美術的工作量。是以在實際開放中,一般隻會對一些重要的對象使用該技術

總結

上面所介紹的關于資源影響遊戲性能的一些常用的點,都是遊戲開發者日常接觸最多的,更深入,更底層的就需要根據項目的特點進行專門的适配與調整。

繼續閱讀