MaterialPropertyBlock是Unity提供的一个类,用于在运行时动态修改渲染器的材质属性,而无需为每个渲染器创建新的材质实例。它通过为每个渲染器设置独立的属性值,实现在共享材质基础上的个性化定制。这种方式既能满足灵活性需求,又能减少内存开销和渲染批次,是优化性能的重要工具。
MaterialPropertyBlock是一个轻量级容器,用于存储材质属性的覆盖值,而无需创建新的材质实例。它允许你:
- 为单个Renderer实例设置特定的材质属性值
- 保持原始材质的共享引用
- 维护GPU批处理的优势
在内部,Unity使用这些属性块作为渲染命令的一部分,在绘制特定对象时临时覆盖共享材质的属性。
MaterialPropertyBlock在以下场景中尤为实用:
- 批量物体的个性化定制
当场景中有大量相似物体(例如草地、树木或建筑),它们共享同一个材质,但需要略微不同的属性(如颜色、纹理偏移)时,MaterialPropertyBlock可以在不增加材质实例的情况下,为每个物体设置独特的属性。 - 动态效果
在运行时需要频繁调整材质属性的情况,例如:- 角色的高光效果随时间变化。
- UI元素的颜色渐变动画。
- 特效的透明度或亮度调整。
- 优化渲染性能
通过减少材质实例的数量,MaterialPropertyBlock可以降低Draw Call(渲染调用)次数,尤其在移动平台等性能敏感的环境中效果显著。
MaterialPropertyBlock与material属性的区别
在Unity中,渲染器的material属性和MaterialPropertyBlock都可以修改材质属性,但它们的工作方式和适用场景有很大不同。
material属性
- 行为:当你访问或修改renderer.material时,Unity会为该渲染器创建一个新的材质实例(如果之前没有独立实例)。
- 影响:对这个材质实例的修改只影响当前渲染器,但会增加内存开销和渲染批次。
- 适用场景:适合少量物体需要完全独特的材质属性时使用。
MaterialPropertyBlock
- 行为:通过SetPropertyBlock方法,MaterialPropertyBlock为渲染器设置独立的属性值,但渲染器仍然共享底层的材质资源,不会创建新实例。
- 影响:属性修改仅影响当前渲染器,且不会增加内存或批次开销。
- 适用场景:适合大量物体需要个性化属性但希望保持材质共享的情况。
区别总结
特性 | material属性 | MaterialPropertyBlock |
---|---|---|
材质实例 | 创建新的材质实例 | 不创建新实例,共享材质 |
内存开销 | 较高(每个实例占用内存) | 较低(共享材质) |
渲染批次 | 可能增加(不同材质实例) | 较少(共享材质,属性差异不影响批次) |
适用场景 | 少量物体的独特属性 | 大量物体的个性化属性 |
使用示例
基本使用
以下是一个简单的示例,展示如何使用MaterialPropertyBlock为物体设置独特的颜色:
cs
using UnityEngine;
public class ColorChanger : MonoBehaviour
{
private Renderer renderer;
private MaterialPropertyBlock propBlock;
void Start()
{
renderer = GetComponent<Renderer>();
propBlock = new MaterialPropertyBlock();
// 设置颜色
propBlock.SetColor("_Color", Color.red);
renderer.SetPropertyBlock(propBlock);
}
}
在这个例子中,物体的颜色被设置为红色,但不会创建新的材质实例。
动态修改属性
你可以在运行时动态更新MaterialPropertyBlock中的属性值,例如实现颜色渐变:
cs
void Update()
{
// 每帧随机改变颜色
Color newColor = new Color(Random.value, Random.value, Random.value);
propBlock.SetColor("_Color", newColor);
renderer.SetPropertyBlock(propBlock);
}
支持的属性类型
MaterialPropertyBlock支持多种属性设置方法,包括:
- SetColor:设置颜色。
- SetFloat:设置浮点数。
- SetVector:设置向量。
- SetTexture:设置纹理。
- SetMatrix:设置矩阵。
- SetBuffer: 设置缓冲区。
这些方法可以满足大多数材质属性的修改需求。
cs
// 各种属性类型设置示例
void SetVariousProperties()
{
// 常见标量和向量
propertyBlock.SetFloat("_Metallic", 0.8f);
propertyBlock.SetVector("_SpecColor", new Vector4(0.9f, 0.8f, 0.1f, 1.0f));
propertyBlock.SetColor("_EmissionColor", Color.red * 2.0f);
// 纹理属性
propertyBlock.SetTexture("_MainTex", customTexture);
propertyBlock.SetTexture("_BumpMap", customNormalMap);
// 矩阵属性
Matrix4x4 texTransform = Matrix4x4.TRS(
new Vector3(0.5f, 0.5f, 0),
Quaternion.Euler(0, 0, 45),
Vector3.one
);
propertyBlock.SetMatrix("_TextureMatrix", texTransform);
// 缓冲区属性 (Unity 2019.3+)
propertyBlock.SetBuffer("_InstanceData", computeBuffer);
// 使用属性ID(性能更优)
int emissionColorID = Shader.PropertyToID("_EmissionColor");
propertyBlock.SetColor(emissionColorID, Color.blue * 1.5f);
}
性能优化建议
- 批量处理
对于大量物体,使用MaterialPropertyBlock可以避免创建多个材质实例,从而减少Draw Call。确保所有物体共享同一个材质,仅通过MaterialPropertyBlock调整差异化属性。 - 重复使用实例
MaterialPropertyBlock对象可以重复使用,避免在循环或频繁调用中创建新实例,以减少性能开销。 - 结合sharedMaterial
如果需要对所有物体进行全局属性调整,可以使用renderer.sharedMaterial修改共享材质,而将个性化设置交给MaterialPropertyBlock。 - 检查属性名称
设置属性时,需确保使用Shader中定义的正确属性名称(如_Color),否则修改将无效。