Unity3D Mesh小課堂(四)MeshRenderer的material和sharedMaterial
MeshRenderer(網格渲染器)從MeshFilter(網格過濾器)獲得幾何形狀,並根據Mesh進行渲染,而渲染所需要的貼圖資訊就來自與Material。
而MeshRenderer的Material型別的變數有兩個:material和sharedMaterial。二者之間有什麼聯絡呢?我們做個試驗。
首先建立兩個Cube,CubeA和CubeB,並新建一個Material材質球,把兩個Cube裡MeshRenderer的第一個Material都改成這個新建的材質。然後新建一個程式碼檔案,把程式碼加到CubeA上,在start方法裡面加入以下程式碼:
MeshRenderer mr = GetComponent<MeshRenderer> (); mr.material.color = Color.red;
點選執行我們看到只有CubeA顏色變了。
如果換成
MeshRenderer mr = GetComponent<MeshRenderer> ();
mr.sharedMaterial.color = Color.red;
就會發現,CubeA和CubeB的顏色都變了,並且我們新建的Material(材質球)的顏色也變了。
由此我們可以知道:
sharedMaterial是公用的Material,所有用到這個材質的MeshRendered都會引用這個Material。改變sharedMaterial的屬性也會改變mat檔案。
material是獨立的Material,改變material的屬性不會影響到其他物件,也不會影響mat檔案。
但是,看問題不應該浮於表面。
我們繼續做實驗,刪掉之前的程式碼加入下面這段:
MeshRenderer mr = GetComponent<MeshRenderer> ();
Material smat = mr.sharedMaterial;
Material mat = mr.material;
Debug.Log (smat == mr.sharedMaterial);
Debug.Log (mat == mr.sharedMaterial);
Debug.Log (mat == mr.material);
列印結果分別為False,True,True。
我們知道sharedMaterial和material並不是變數,而是屬性(property),屬性有set和get方法,當給屬性賦值時會隱式的呼叫set方法,當獲取屬性值得時候會隱式的呼叫get方法。在這裡我們把material的值變數假設為_material,而sharedMaterial的值變數假設為_sharedMaterial。
Material _material;
Material _sharedMaterial;
由此,我們就可以得出結論:
material的get方法裡面,會判斷_material跟_sharedMaterial是否相同,如果相同,返回_material,如果不同,會新建一個_sharedMaterial的拷貝,並賦值給_material和_sharedMaterial。大概是下面這樣的邏輯:
public Material material {
get {
if (_sharedMaterial == _material) {
return _material;
}
_material = new Material (_sharedMaterial);
_sharedMaterial = _material;
return _material;
}
}
我們繼續做實驗,重新寫入下段程式碼:
MeshRenderer mr = GetComponent<MeshRenderer> ();
Material newMat = new Material (Shader.Find ("Standard"));
mr.material = newMat;
Debug.Log (newMat == mr.sharedMaterial);
Debug.Log (newMat == mr.material);
Material tmpMat = mr.material;
Debug.Log (newMat == tmpMat);
Debug.Log (tmpMat == mr.sharedMaterial);
列印結果分別為True,False,False,True。
由此,我們可以得出結論:
material的set方法裡面會把傳入的值賦給_sharedMaterial,並且會再新建一個_sharedMaterial的拷貝,並賦值給_material。(因為值不同,所以get的時候又會新建一個Material。)
public Material material {
get {
if (_sharedMaterial == _material) {
return _material;
}
_material = new Material (_sharedMaterial);
return _material;
}
set {
_sharedMaterial = value;
_material = new Material (_sharedMaterial);
}
}
至於SharedMaterial的get和set方法,似乎並不會修改變數的值,只是單純的取值賦值而已。最後總結一下二者的使用時機:
當只修改材質的引數的時候,使用material屬性,確保其他物件不會受影響。
當需要修改材質的時候,直接賦值給sharedMaterial,否則賦值給material會產生記憶體洩露。