在unite2017國(guó)外技術(shù)專場(chǎng)中Arturo Nú?ez在他的shader性能與優(yōu)化專題中提到了一個(gè)這樣的優(yōu)化建議那就是使用材質(zhì)屬性塊呻拌,其原話是Use MaterialPropertyBlock
Is faster to set properties using a MaterialPropertyBlock rather than
material.SetFloat(); Material.SetColor();
官網(wǎng)文檔
因此我特意查找了下關(guān)于MaterialPropertyBlock的官方文檔梆暖,文檔是這樣說的拢肆,材質(zhì)屬性塊被用于Graphics.DrawMesh 和 Renderer.SetPropertyBlock兩個(gè)API援制,當(dāng)我們想要繪制許多相同材質(zhì)但不同屬性的對(duì)象時(shí)可以使用它迄本。例如你想改變每個(gè)繪制網(wǎng)格的顏色爷辙,但是它卻不會(huì)改變渲染器的狀態(tài)
我們來(lái)看看Renderer這個(gè)類聘萨,它包含了material竹椒,sharedMaterial這兩個(gè)屬性;GetPropertyBlock米辐,SetPropertyBlock兩個(gè)函數(shù)胸完,其中兩個(gè)屬性是用來(lái)訪問和改變材質(zhì)的书释,而兩個(gè)函數(shù)是用來(lái)設(shè)置和獲取材質(zhì)屬性塊的,我們知道赊窥,當(dāng)我們操作材質(zhì)共性時(shí)爆惧,可以使用sharedMaterial屬性,改變這個(gè)屬性锨能,那么所有使用此材質(zhì)的物件都將會(huì)改變扯再,而我們需要改變單一材質(zhì)時(shí),需要使用material屬性址遇,而在第一次使用material時(shí)其實(shí)是會(huì)生成一份材質(zhì)拷貝的熄阻,即material(Instance)
實(shí)驗(yàn)
首先聲明兩個(gè)數(shù)組,一個(gè)用來(lái)保存用于操作材質(zhì)倔约,另一個(gè)用來(lái)保存操作材質(zhì)屬性塊
GameObject[] listObj = null;
GameObject[] listProp = null;
再次聲明一個(gè)公共變量秃殉,用來(lái)控制數(shù)組長(zhǎng)度,以及一個(gè)材質(zhì)屬性塊
public int objCount = 100;
MaterialPropertyBlock prop = null;
然后在Start函數(shù)中做初始化工作浸剩,我們?cè)谄聊蛔髠?cè)空間生成objCount個(gè)球體Sphere用來(lái)處理材質(zhì)材質(zhì)钾军,在屏幕右側(cè)空間生成objCount個(gè)球體Sphere用來(lái)處理材質(zhì)屬性塊
void Start () {
colorID = Shader.PropertyToID("_Color");
prop = new MaterialPropertyBlock();
var obj = Resources.Load("Perfabs/Sphere") as GameObject;
listObj = new GameObject[objCount];
listProp = new GameObject[objCount];
for (int i = 0; i < objCount; ++i)
{
int x = Random.Range(-6,-2);
int y = Random.Range(-4, 4);
int z = Random.Range(-4, 4);
GameObject o = Instantiate(obj);
o.name = i.ToString();
o.transform.localPosition = new Vector3(x,y,z);
listObj[i] = o;
}
for (int i = 0; i < objCount; ++i)
{
int x = Random.Range(2, 6);
int y = Random.Range(-4, 4);
int z = Random.Range(-4, 4);
GameObject o = Instantiate(obj);
o.name = (objCount + i).ToString();
o.transform.localPosition = new Vector3(x, y, z);
listProp[i] = o;
}
}
然后我們?cè)赨pdate函數(shù)中響應(yīng)我們的操作,這里我使用按鍵上下健位來(lái)操作
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));
}
}
結(jié)論
然后我們?cè)賮?lái)看一下對(duì)比數(shù)據(jù)
從結(jié)果對(duì)比來(lái)看乒省,確實(shí)使用材質(zhì)屬性塊要快于使用材質(zhì)巧颈,其消耗將近是操作材質(zhì)耗時(shí)的四分之一。同時(shí)不管是材質(zhì)還是材質(zhì)屬性塊袖扛,第一次操作比后面的操作耗時(shí)要大砸泛,尤其是材質(zhì),可見在第一次使用材質(zhì)改變屬性操作時(shí)蛆封,其拷貝操作消耗還是非常大的唇礁。同時(shí)我也通過profiler的memory模塊,切換進(jìn)Detailed選項(xiàng)惨篱,對(duì)其進(jìn)行采樣盏筐,可以發(fā)現(xiàn)在Sence Memory下面會(huì)有material的拷貝(材質(zhì)操作導(dǎo)致,而材質(zhì)屬性操作不會(huì))
游戲中處理
正如官方文檔介紹材質(zhì)屬性塊一樣砸讳,unity地型引擎正是使用材質(zhì)屬性塊來(lái)繪制樹的琢融,所有的樹使用的是相同的材質(zhì),但是每棵樹有不同的顏色簿寂,縮放和風(fēng)因子漾抬。對(duì)于大場(chǎng)景大世界來(lái)說,我們肯定是采取動(dòng)態(tài)加載來(lái)處理地圖的常遂,這個(gè)時(shí)候我們可以配合Gpu Instance來(lái)進(jìn)一步的提高性能纳令,使用Gpu Instance一是可以省去實(shí)體對(duì)象本身的開銷,二是能夠起到減少Drawcall的作用,同時(shí)還能減少動(dòng)態(tài)合批的cpu開銷平绩,靜態(tài)合批的內(nèi)存開銷圈匆;可謂一舉多得,遺憾的只能在ES3.0以上的設(shè)備上使用捏雌。對(duì)于一些游戲中存在自定義皮膚顏色玩法的跃赚,材質(zhì)屬性塊的優(yōu)勢(shì)就可以發(fā)揮出來(lái)了,你想當(dāng)你100個(gè)不同玩家同屏?xí)r腹忽,如果使用材質(zhì)操作顏色屬性的話来累,那么首先就存在100份材質(zhì)拷貝的實(shí)例,其次窘奏,材質(zhì)操作屬性本身就比材質(zhì)屬性塊操作要慢那么點(diǎn)嘹锁,在性能優(yōu)化中一毫秒的優(yōu)化就是勝利,這了一毫秒那里一毫秒加起來(lái)就不得了了着裹。