项目需要,角色进入草丛要半透明。 已用了 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
感觉有参考价值。