U3D遊戲中使用材質屬性塊來替換Material屬性操作

? 前言

Unite2017國外技術專場中Arturo Nú?ez在他的shader性能與優化專題里提到了一個這樣的優化建議,那就是使用材質屬性塊。

Use MaterialPropertyBlock Is faster to set properties using a MaterialPropertyBlock rather than material.SetFloat(); Material.SetColor();

因此我特意查找了下關於MaterialPropertyBlock的官方文檔,文檔是這樣說的,材質屬性塊被用於Graphics.DrawMeshRenderer.SetPropertyBlock兩個API,當我們想要繪製許多相同材質但不同屬性的對象時可以使用它。

例如:你想改變每個繪製網格的顏色,但是它卻不會改變渲染器的狀態。

我們來看看Renderer這個類,它總共包含了materialsharedMaterial這兩個屬性;以及GetPropertyBlockSetPropertyBlock兩個函數,其中兩個屬性是用來訪問和改變材質的,而兩個函數是用來設置和獲取材質屬性塊的,我們知道,當我們操作材質共性時,可以使用sharedMaterial屬性,改變這個屬性,那麼所有使用此材質的物件都將會改變,而我們需要改變單一材質時,需要使用material屬性,而在第一次使用material時其實是會生成一份材質拷貝的,即material(Instance).

?實驗

首先聲明兩個數組,一個用來保存用於操作材質,另一個用來保存操作材質屬性塊。

GameObject[] listObj = null;GameObject[] listProp = null;

再次聲明一個公共變數,用來控制數組長度,以及一個材質屬性塊。

public int objCount = 100;MaterialPropertyBlock prop = null;

然後在Start函數中做初始化工作,我們在屏幕左側空間生成objCount個球體Sphere用來處理材質材質,在屏幕右側空間生成objCount個球體Sphere用來處理材質屬性塊。

然後我們在Update函數中響應我們的操作,這裡我使用按鍵上下健位來操作↓

void Update () { if (Input.GetKeyDown(KeyCode.DownArrow)) { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < objCount; ++i) { float r = Random.Range(0, 1f); float g = Random.Range(0, 1f); float b = Random.Range(0, 1f); listObj[i].GetComponent<Renderer>().material.SetColor("_Color", new Color(r, g, b, 1)); } sw.Stop(); UnityEngine.Debug.Log(string.Format("material total: {0:F4} ms", (float)sw.ElapsedTicks *1000 / Stopwatch.Frequency)); } if (Input.GetKeyDown(KeyCode.UpArrow)) { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < objCount; ++i) { float r = Random.Range(0, 1f); float g = Random.Range(0, 1f); float b = Random.Range(0, 1f); listProp[i].GetComponent<Renderer>().GetPropertyBlock(prop); prop.SetColor(colorID, new Color(r, g, b, 1)); listProp[i].GetComponent<Renderer>().SetPropertyBlock(prop); } sw.Stop(); UnityEngine.Debug.Log(string.Format("MaterialPropertyBlock total: {0:F4} ms", (float)sw.ElapsedTicks * 1000 / Stopwatch.Frequency)); } }

然後我們再來看一下對比數據↓

從結果對比來看,確實使用材質屬性塊要快於使用材質,其消耗將近是操作材質耗時的四分之一。

同時不管是材質還是材質屬性塊,第一次操作比後面的操作耗時要大,尤其是材質,可見在第一次使用材質改變屬性操作時,其拷貝操作消耗還是非常大的。

Renderer[] listRender = null;Renderer[] listRenderProp = null;...listRender[i] = o.GetComponent<Renderer>();...listRenderProp[i] = o.GetComponent<Renderer>();

再來看下運行對比數據↓

同時我也通過profilermemory模塊,切換進Detailed選項,對其進行採樣,可以發現在Sence Memory下面會有material的拷貝(材質操作導致,而材質屬性操作不會)這也驗證了操作材質時會有實例化存在,而使用材質屬性塊則不存在實例化。

?遊戲中處理

正如官方文檔介紹材質屬性塊一樣,unity地型引擎正是使用材質屬性塊來繪製樹的,所有的樹使用的是相同的材質,但是每棵樹有不同的顏色,縮放和風因子。

對於大場景大世界來說,我們肯定是採取動態載入來處理地圖的,這個時候我們可以配合Gpu Instance來進一步的提高性能,使用Gpu Instance一是可以省去實體對象本身的開銷,二是能夠起到減少Drawcall的作用,同時還能減少動態合批的cpu開銷,靜態合批的內存開銷;可謂一舉多得,遺憾的只能在ES3.0以上的設備上使用。

對於一些遊戲中存在自定義皮膚顏色玩法的,材質屬性塊的優勢就可以發揮出來了,你想當你100個不同玩家同屏時,如果使用材質操作顏色屬性的話,那麼首先就存在100份材質拷貝的實例,其次,材質操作屬性本身就比材質屬性塊操作要慢那麼點,在性能優化中一毫秒的優化就是勝利,這了一毫秒那裡一毫秒加起來就不得了了。

Arturo Nú?ez 的shader性能與優化的工程和本次測試工程的地址,可以私信我們wx公眾號的小編獲取哦!

wx公眾號:西山居質量


推薦閱讀:

TAG:Unity遊戲引擎 | 材質 | 遊戲優化 |