关于material和sharedMaterial的问题

本文详细解析了Unity3D中Material与SharedMaterial的特性与应用场景。通过实例对比,展示了两者在修改材质颜色时的不同表现及对内存的影响,并提供了一种结合使用的高效实践方法。

在unity3d中,Renderer组件有两个属性:material和sharedMaterial,它们都可以用来获取Renderer的材质属性。但是它们之间却又很大的区别,下面通过示例来讲解一下。

准备工作:unity3d中新建一个空场景;创建两个cube,分别命名为Cube0、Cube1;在Project中新建一个材质球,取名M0,shader选择Unlit/Color,shader的Main Color属性设为白色,即(255,255,255,255);将M0分别赋值给Cube0、Cube1;新建一个脚本TestMaterial,将脚本拖到Cube0上。如下所示:

1、测试material

TestMaterial脚本的内容如下所示:

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class TestMaterial : MonoBehaviour
 5 {
 6     Renderer thisRenderer;
 7     // Use this for initialization
 8     void Start()
 9     {
10         thisRenderer = GetComponent<Renderer>();
11         thisRenderer.material.color = Color.red;
12     }
13 }

运行,效果如下图所示:

注意,只有Cube0的颜色改变,在mesh renderer中,材质球的名字是M0(Instance)。

2、测试sharedMaterial

代码如下:

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class TestMaterial : MonoBehaviour
 5 {
 6     Renderer thisRenderer;
 7     // Use this for initialization
 8     void Start()
 9     {
10         thisRenderer = GetComponent<Renderer>();
11         thisRenderer.sharedMaterial.color = Color.red;
12     }
13 }

运行,效果如下所示:

注意,Cube0、Cube1的颜色均发生改变,但是在mesh renderer中,材质球的名字还是M0。但是,此时在Project中的M0的Main Color属性发生了改变,如下所示:

 

总结:使用material属性的时候,每次都会new一份新的material作用与它,但不会改变本地工程中的材质material;sharedMaterial是共享材质,无论使用多少次,内存中都只会占用一份内存,但是会影响其他使用同一材质球的对象。所以,从效率上来说,sharedMaterial的效率更高。

 

重点来了:在实际使用的时候,我们可以在开始的时候使用material产生一个新的材质球作用于该renderer,然后之后的操作都使用sharedMaterial,这样,效率更高,而且不会影响其他使用同一个材质球的renderer。

最后,附上示例代码:


 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class TestMaterial : MonoBehaviour
 5 {
 6     Renderer thisRenderer;
 7     float delay = 3f;
 8     float changeColorTm;
 9     bool isChangeOnce;
10 
11     void Awake()
12     {
13         thisRenderer = GetComponent<Renderer>();
14         thisRenderer.material.color = Color.red;
15         changeColorTm = Time.time + delay;
16         isChangeOnce = false;
17     }
18 
19     void Update()
20     {
21         if (!isChangeOnce && Time.time >= changeColorTm)
22         {
23             isChangeOnce = true;
24             thisRenderer.sharedMaterial.color = Color.green;
25         }
26     }
27 }

 

Unity 中,当你尝试直接访问预制体(Prefab)上的 `Renderer.material` 属性时,可能会遇到访问被拒绝或无法修改材质的错误。这是因为预制体本身是资源模板,而非场景中的实例对象。Unity 不允许直接修改预制体上的材质属性以避免对原始资源造成不可逆的更改[^3]。 ### 原因分析 - **只读资源**:预制体作为项目资源文件,在编辑器中被视为只读对象。 - **Material vs. sharedMaterial**: - 使用 `Renderer.material` 会尝试创建材质的一个副本用于运行时修改。 - 如果该操作在预制体上执行,则会失败并抛出异常。 - 正确的做法是使用 `sharedMaterial` 来获取材质,但不能直接修改其属性。 ### 解决方案 1. **克隆实例后再修改材质** 在代码中加载预制体并实例化为场景对象后,再访问 `material` 属性进行修改: ```csharp GameObject instance = Instantiate(prefab) as GameObject; Renderer renderer = instance.GetComponent<Renderer>(); Material material = new Material(renderer.sharedMaterial); // 创建新材质副本 renderer.material = material; // 替换材质 material.color = Color.red; // 修改颜色等属性 ``` 2. **使用 Material 创建副本** 直接从 `sharedMaterial` 创建新的 `Material` 对象,以避免修改原始资源: ```csharp Renderer renderer = GetComponent<Renderer>(); Material dynamicMaterial = new Material(renderer.sharedMaterial); renderer.material = dynamicMaterial; ``` 3. **通过脚本动态创建材质球** 如果需要多次修改材质属性,可以在运行时动态创建材质球并分配给对象: ```csharp public Material baseMaterial; void Start() { Renderer renderer = GetComponent<Renderer>(); Material runtimeMaterial = new Material(baseMaterial); renderer.material = runtimeMaterial; } ``` 4. **Editor脚本方式(仅限编辑器内修改)** 若确实需要在编辑器中批量修改预制体材质,可以通过 Editor 脚本来实现: ```csharp using UnityEditor; [MenuItem("Tools/Apply Material to Prefab")] static void ApplyMaterial() { GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/MyPrefab.prefab"); Renderer renderer = prefab.GetComponent<Renderer>(); Material newMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/Materials/NewMaterial.mat"); // 编辑器上下文中修改预制体 PrefabUtility.ApplyPropertyOverride(renderer, "m_Material", InteractionMode.UserAction); } ``` 5. **检查权限与版本兼容性** 某些 Unity 版本可能存在对预制体访问的限制,确保使用的是支持运行时实例化的版本(如 Unity 2018.3 及以上)。 ### 最佳实践建议 - 避免直接修改 `sharedMaterial`,因为这会影响所有使用该材质的对象。 - 实例化后使用 `material` 属性前应创建新材质副本。 - 对于 UI 元素或频繁变化的材质,考虑使用对象池材质缓存机制提升性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值