1. 程式人生 > >Unity3D Mesh小課堂(四)MeshRenderer的material和sharedMaterial

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會產生記憶體洩露。