[unity] 5.5.2 Standard Specular shader 真机上代码调整半透明无效问题。


项目需要,角色进入草丛要半透明。 已用了 untiy 自带shader (Standard Specular)的Fade 模式。  用代码电脑上调试没问题,结果上了真机就无法半透明。

原因是 unity打包时只会打 shader 被引用到的变体。 如果代码里调用 EnableKeyword 来开关宏, 如果对应的变体没有,则就不起作用了。。。


先附上 调整透明代码:

    public enum BlendMode
    {
        Opaque,
        Cutout,
        Fade,       // Old school alpha-blending mode, fresnel does not affect amount of transparency
        Transparent // Physically plausible transparency mode, implemented as alpha pre-multiply
    }

    public static BlendMode GetMaterialWithBlendMode(Material material)
    {
        if (material.IsKeywordEnabled("_ALPHATEST_ON"))
        {
            return BlendMode.Cutout;
        }
        if (material.IsKeywordEnabled("_ALPHABLEND_ON"))
        {
            return BlendMode.Fade;
        }
        if (material.IsKeywordEnabled("_ALPHAPREMULTIPLY_ON"))
        {
            return BlendMode.Transparent;
        }
        return BlendMode.Opaque;
    }

    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 = 2450;
                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 = 3000;
                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 = 3000;
                break;
        }
    }


    public void RestoreSneakAlpha(GameObject resourceObject)
    {
        Renderer[] renderers = resourceObject.GetComponentsInChildren<Renderer>(true);
        for (int i = 0; i < renderers.Length; i++)
        {
            Material m = renderers[i].material;
            if (GetMaterialWithBlendMode(m) == BlendMode.Fade)
            {
                Color timerColor = new Color(m.color.r, m.color.g, m.color.b, 1);
                m.color = timerColor;
            }
            SetupMaterialWithBlendMode(m, BlendMode.Opaque);
        }
    }

    public void SetSneakAlpha(GameObject resourceObject, float alpha)
    {
        Renderer[] renderers = resourceObject.GetComponentsInChildren<Renderer>(true);
        for (int i = 0; i < renderers.Length; i++)
        {
            Material m = renderers[i].material;
            if (GetMaterialWithBlendMode(m) == BlendMode.Opaque)
            {
                Color timerColor = new Color(m.color.r, m.color.g, m.color.b, alpha);
                m.color = timerColor;
            }
            SetupMaterialWithBlendMode(m, BlendMode.Fade);
        }
    }


于是试了几个办法,把相关变体打印进去:

1. 把开启 fade的材质球 打包进去,

结果:测试项目OK。  实际项目只有个别人物能透明,其他的还是一样, 原来实际项目每个角色 打成了单独的AssetBuddle, shader没有形成依赖,造成每个里面打了一个shader 。  当然想办法调整一下也许是可以的, 但总的感觉不太好。

所以这个方案应该可行,不过需要放几个不相干的 材质球项目里。


2.参照官网 用 ShaderVariantCollection

http://blog.csdn.net/ynnmnm/article/details/44674211
http://www.seven-fire.cn/archives/174
先 到Edit->Project Settings->Graphics里把 用到的shader变体 保存成 一个 ShaderVariantCollection文件。

然后试了2个方法
A.添加到GraphicsSettings的Preload Shaders列表中 
B.放到Resources目录下, 通过代码创建ShaderVariantCollection,并调用WarmUp接口
都无效

奇怪了,和 http://blog.csdn.net/sparrowfc/article/details/50389238 这边文章 问题差不多。


看下面评论, 好像早期版本Standard 用 ShaderVariantCollection 有问题, 可能5.6 之后才修正了?这个可以去官方查一查。

然后 说把对应的 开关 不要用 shader_feature  而用  multi_compile

正好我们项目把 StandardSpecular.shader自己改过一些, 所以方案3


3. 修改 StandardSpecular.shader(我们自己提取标准shader 改了一下), 

把几处(主要是  "LightMode" = "ForwardBase"), 

#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON

改成
#pragma multi_compile _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON


编译时开销多一些,   怕 运行时占内存太多, profile了一下, 发现 也还好,占用内存挺小的。


暂时选用这个方案了。



对了,附带一个 Unity5.x shader打包AssetBundle总结
http://www.2cto.com/kf/201612/578404.html

感觉有参考价值。


猜你喜欢

转载自blog.csdn.net/zhenmu/article/details/77408184