1. 从“换衣服”到“贴标签”:MaterialPropertyBlock到底是什么?
如果你刚开始接触Unity的渲染优化,MaterialPropertyBlock(后面我们简称MPB)这个名字听起来可能有点唬人。别怕,咱们用一个最生活化的例子来理解它。
想象一下,你是一个游戏里的角色,身上穿着一件基础款的白T恤(这就是你的材质Material)。现在,你需要让100个克隆体角色都穿上这件T恤,但每个人衣服上的印花图案要不一样。传统做法有两种:第一种,给每个人都重新做一件全新的、带不同印花的T恤(这就是创建新的Material Instance)。这显然很浪费布料(内存)。第二种,让100个人都穿同一件有固定印花的T恤(这就是使用SharedMaterial),这样虽然省布料,但大家就长得一模一样了,毫无个性。
那MaterialPropertyBlock是怎么做的呢?它说:别折腾衣服本身了!我们给每个人发一张可以随时更换的“贴纸”(这就是Property Block)。大家还是穿着同一件基础白T恤(SharedMaterial),但每个人可以把自己的专属图案“贴纸”贴在胸前。想换图案?撕掉旧的,贴张新的就行,完全不用换掉整件衣服。
这个“贴纸”就是MaterialPropertyBlock。它本质上是一个独立于材质球本身的数据块,专门用来存储和传递那些需要频繁变化的材质属性,比如颜色、纹理偏移、浮点参数等。它让你能在不创建新材质实例的前提下,为每个渲染器(Renderer)设置独特的属性,完美平衡了内存效率和视觉多样性。在2D的SpriteRenderer和3D的MeshRenderer上,这个技巧都同样管用,是优化游戏性能,特别是减少Draw Call和内存开销的利器。
2. 为什么不用Material实例?三种方式的深度对比
在深入代码之前,我们必须搞清楚MPB要解决什么问题,以及它比传统方案好在哪里。很多新手一上来就想着“动态改颜色”,于是直接GetComponent<Renderer>().material.color = Color.red,殊不知这一行代码就悄悄创建了一个新的材质实例,埋下了性能隐患。
2.1 Material实例化:最灵活,也最“吃”内存
当你通过renderer.material(注意是小写material)去获取或设置材质属性时,Unity会在背后为你做一件事:复制。它会从当前使用的材质(可能是项目中的一个Asset)复制一份全新的实例出来,然后让你修改这个副本。这个操作在编辑器里看起来很方便,但在运行时,特别是对于大量重复的对象(比如一片草地、一群士兵、满天繁星),就是灾难。
我踩过的坑:曾经做一个弹幕射击游戏,每个子弹我都想微调它的颜色和亮度。如果每个子弹都material.SetColor一下,游戏运行几分钟后,内存里就会多出成千上万个几乎一样的材质实例。Profiler里一看,Material的数量直线上升,内存占用蹭蹭涨,而且因为每个材质实例都是一个独立的渲染状态,很容易导致Unity无法进行动态合批(Dynamic Batching),Draw Call数量爆炸。
// 错误示范:这会创建新的材质实例!
MeshRenderer renderer = GetComponent<MeshRenderer>();
renderer.material.color = new Color(Random.value, Random.value, Random.value);
2.2 SharedMaterial:最省内存,但大家必须“整齐划一”
为了避免实例化,你可以使用renderer.sharedMaterial。这个属性直接指向项目中的原始材质Asset,或者最初分配给它的那个材质实例。通过它修改属性,会直接影响到所有使用这个sharedMaterial的物体。
这就像给所有士兵下了一道命令:“所有人,把衣服染成红色!”命令一下,全军一片红。这非常省内存,因为大家共用同一份材质数据。但问题也显而易见:你失去了对单个物体的独立控制能力。如果你的游戏里需要大量外观相似但又有细微差别的物体(比如不同血量的敌人身上有不同强度的红光预警),SharedMaterial就无能为力了。
2.3 MaterialPropertyBlock:鱼与熊掌,我都要!
MaterialPropertyBlock的出现,就是为了解决上面这个两难困境。它的核心思想是:将材质中“不变的基础属性”和“可变的个性参数”分离开。
- 基础材质(SharedMaterial):负责定义物体的基本质感,比如使用什么Shader、基础纹理(Albedo)、光滑度、金属度等。这部分数据在所有物体间共享,一份内存,多次使用。
- 属性块(MaterialPropertyBlock):负责定义每个物体独特的参数,比如
_Color(色调)、_MainTex_ST(纹理偏移和缩放)、自定义的_Health(用于在Shader中根据血量变化颜色)等。这些数据是每个物体独有的,但它的存储和传递效率远高于完整的材质实例。
Unity的



被折叠的 条评论
为什么被折叠?



