[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
然後試了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
感覺有參考價值。