关于Unity运行时动态修改材质的小秘密

一、问题背景

在以往的Unity项目中涉及到修改材质的需求时,也只是改改材质贴图,材质颜色等,也没遇到那么多动态修改材质的坑。最近在做Unity App Demo时也遇到了要修改材质的小需求,本以为几分钟就能完成了,却花费了我大半天时间才弄完。需求:美术那边给了三个材质球,让我动态加载物体时,切换该物体的材质,最开始想的方案是,直接把三个材质球加载的内存中,直接修改物体的材质球属性不就可以了吗。转念一想,这万一给我一百个材质球,或者更多,难道我都要加载到内存中吗?这显然是不行的。我一看这三个材质球只有一个地方是不一样的,那就是颜色和透明度。我就想了,那我直接获取物体材质球,直接修改材质球相关的属性不就完事了吗,于是就出现了本文中的一系列问题。

二、解决方案

1、修改材质颜色透明度不生效

场景中新建Cube,新建脚本MaterialTest.cs,挂载到Cube物体下,写代码实现修改Cube的材质属性:

public class MaterialTest : MonoBehaviour
{
    private Material material;
    
    void Start()
    {
        material = GetComponent<MeshRenderer>().material;
        material.color = new Color(1, 0, 0, 0.5f);
    }
}

运行发现颜色变了,但是透明度没变
在这里插入图片描述
打开Inspector面板一看,发现材质的Rendering Mode 还是Opaque(不透明的)。

2、修改材质透明不实时刷新

基于问题一,我们要通过代码修改材质的渲染模式,于是加了一行代码:

material.SetFloat("_Mode", 3);

运行发现物体还是没有变成透明形状,但是呢,只要一打开物体的Inspector面板,点一下材质组件,Game视图中的Cube就突然刷新了,变成透明了!
在这里插入图片描述
这令我百思不得其解,我在想,打包发布之后又没有Inspector面板让我去手动点一下再刷新材质啊(苦笑),看了材质相关的api,也尝试了好几张修改材质的方法,都没能实时刷新材质透明度,应该还是方法不对。

3、参考官方修改材质的示例成功修改透明材质

在这里插入图片描述
在Unity官网中每个版本的下载列表里会有一个Built in shaders,下载解压之后打开Editor–StandardShaderGUI.cs脚本,里面有详细的官方修改shader材质示例代码。
在这里插入图片描述
最终修改材质透明度的代码如下:

public class MaterialTest : MonoBehaviour
{
    private Material material;

    void Start()
    {
        material = GetComponent<MeshRenderer>().material;
        material.color = new Color(1, 0, 0, 0.5f);
        material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
        material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
        material.SetInt("_ZWrite", 0);
        material.DisableKeyword("_ALPHATEST_ON");
        material.DisableKeyword("_ALPHABLEND_ON");
        material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
        material.SetFloat("_Mode", 3);
        material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
    }
}

运行发现正确修改材质透明度并实时刷新。其中这个关键字最重要:

_ALPHAPREMULTIPLY_ON 表示开启 “透明”透明度渲染模式

4、安卓真机物体透明度失效。

猜测是因为打包时Unity没有将Standard的透明变体打包到apk中,本来是想将整个Standard标准shader放入到Graphics中的Always Included Shaders列表中去。但是一想到会造成打包时间变成,很多冗余shader都会打包进去。本着按需打包的策略,最后还是选择新建一个材质球Material,直接挂载到物体中去,将该材质球的Rendering Mode 改为Transparent,这样Unity就能识别到该透明shader将其打包到apk包体中,这样关联的其他物体需要透明材质时也能用到。
于是,最后打包apk,真机运行透明物体渲染正确。
在这里插入图片描述

三、总结

在使用Unity引擎时,还是需要多去官方文档或官方示例中寻找解决方案,这会让你少走弯路,也能让你从坑里感觉爬上来。某些情况下,真机上的表现也会跟编辑器有所差别甚至完全不一样,需要耐心定位问题和修改解决方案以完成需求。

四、Unity修改shader材质源码官方示例

		public enum BlendMode
        {
            Opaque,
            Cutout,
            Fade, 
            Transparent 
        }
		public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode)
        {
            switch (blendMode)
            {
                case BlendMode.Opaque:
                    material.SetOverrideTag("RenderType", "");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = -1;
                    break;
                case BlendMode.Cutout:
                    material.SetOverrideTag("RenderType", "TransparentCutout");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.EnableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
                    break;
                case BlendMode.Fade:
                    material.SetOverrideTag("RenderType", "Transparent");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.EnableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
                    break;
                case BlendMode.Transparent:
                    material.SetOverrideTag("RenderType", "Transparent");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
                    break;
            }
        }
  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周周的Unity小屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值