1. 程式人生 > >[unity] 5.5.2 Standard Specular shader 真機上程式碼調整半透明無效問題。

[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

感覺有參考價值。