iOS下拆分Unity图集的透明通道(不用TP)

iOS下拆分Unity图集的透明通道(不用TP)

在写这篇文章之前,我先给大家展示下,下图是iOS目前PVRTC对透明图的压缩效果。

请输入图片描述

有人说是Unity没有用“正版”的PVRTC压缩库的原因,但是我专门去下载了独立版的PVRTexTool,对比图如下,大家觉得哪个是独立版的效果呢?是不是感觉差异并不明显?

请输入图片描述

实际上,Unity在安卓下已经自带图集ETC1+Alpha了,ETC2的效果也都还不错,回过头看,发现苹果系统才是目前的症结所在。其实苹果的透明图压缩质量一直都很差,只是为了满足安卓系统,透明图一般都会拆通道(或者干脆不压缩),不想对iOS单独处理就干脆全平台一起做了,使得iOS的问题并不明显。

然而现在安卓平台的问题都解决了,iOS的问题就显露了出来,虽然iOS也有了新格式ASTC,苦于普及率远不如ETC2(貌似是IP6以上才支持),想要遗弃掉PVRTC可能还得再等上一年。所以现阶段,我们只能选择单独对iOS平台拆分UI图集的透明通道,来解决UI压缩后完全不能看的问题。


一、工程文件

链接地址:https://github.com/flashyiyi/UGUIAlphaAtlas

  • Editor/CreateAlphaAtlas.cs 创建透明通道图的编辑器脚本
  • Editor/CustomPackerPolicy.cs 自定义图集生成规则
  • AlphaAtlasManager.cs 透明通道图管理类
  • SplitImage.cs 可以显示透明通道的Image

二、生成Alpha图集

首先需要切换到iOS平台,然后把图集里的图片格式设置为PVRTC RGBA(脚本现在只会影响这个格式的图集)

请输入图片描述

再执行Tools下的这个命令:

请输入图片描述

便会在Resources/TextureAlphaAtlas目录下生成透明图集图片和配置文件:

请输入图片描述
请输入图片描述
透明图是通过GL函数重新由未压缩的散图拼接而成的,相比直接从压缩过的图集中获取,质量会好很多

注意,执行这个命令之后,图集生成规则文件会自动更换为CustomPackerPolicy。它唯一的作用只是让原本是PVRTC RGBA的零散Sprite,在生成最终图集的自动转换成PVRTC RGB。

if (settings.format == TextureFormat.PVRTC_RGBA4 && forceIOSOpaque) //强制生成不透明图集
       settings.format = TextureFormat.PVRTC_RGB4;

必须保留这个配置文件,iOS下才是正确的。而在其他平台下,CustomPackerPolicy和默认的DefaultPackerPolicy并没有区别。但如果你曾经修改过图集规则,就需要注意下这个变化。

请输入图片描述


三、让透明图集生效

方法1:将之前生成的图集文件打入包内,再将所有的Image替换成专门的SplitImage.cs。这个SplitImage.cs重写了Image两个函数,会在运行期间加载透明图集。

public class SplitImage : Image  {
        public override Material material
        {
            get
            {
                if (m_Material != null)
                    return m_Material;
                if (overrideSprite && (overrideSprite.associatedAlphaSplitTexture != null || AlphaAtlasManager.GetInstance().GetAlphaTexture(overrideSprite) != null))
                    return defaultETC1GraphicMaterial;
                return defaultMaterial;
            }
            set
            {
                base.material = value;
            }
        }
        protected override void UpdateMaterial()
        {
            base.UpdateMaterial();
            Texture2D alphaTex = AlphaAtlasManager.GetInstance().GetAlphaTexture(overrideSprite);
            if (alphaTex != null)
                canvasRenderer.SetAlphaTexture(alphaTex);
        } }

结果:

请输入图片描述未压缩 / 拆通道后 / 使用原始Image显示的情况

目前文件都是用Resources.Load直接加载的,可以修改AlphaAtlasManager里的加载逻辑以便引入AssetBundles机制。

方法2(推荐)

请输入图片描述

PackAlphaAltas命令相当于一个打包AssetBoundles的预处理,它会在打包前手动修改Sprite的RD属性,将透明图挂在Sprite上。这样直接用原始的Image就能显示拆分通道后的Sprite了。

SerializedObject so = new SerializedObject(sprite);
so.FindProperty("m_RD.textureRect").rectValue = GetAltasTextureRect(sprite, atlasTexture);
so.FindProperty("m_RD.texture").objectReferenceValue = atlasTexture;
Texture2D alphaTexture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/" + AlphaAtlasManager.TEXTURE_ALPHA_ATLAS_PATH + atlasTexture.name + "_alpha.png");
so.FindProperty("m_RD.alphaTexture").objectReferenceValue = alphaTexture;
so.ApplyModifiedProperties();

请输入图片描述

不过这会影响到项目原有的打包流程。可以把方法内的BuildPipeline.BuildAssetBundles部分替换成项目的打包方法,再调用PackAlphaAltas来打包,把两部分合并在一起。

EditorSettings.spritePackerMode = SpritePackerMode.Disabled;
BuildPipeline.BuildAssetBundles(Application.dataPath + "/AssetBundles", BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.iOS);
EditorSettings.spritePackerMode = SpritePackerMode.AlwaysOn;

这个方案和安卓平台下的ETC1+Alpha表现是一致的,加载策略也相同。并不需要将图集文件手动打入包内,也不需要做额外的管理。AlphaAtlasManager.cs,SplitImage.cs这两个文件实际上可以删除。这样可以保证多平台逻辑的一致性。


四、延伸思考

虽然我们在这里使用的是Unity的自动图集,但在最后的打包过程里,实际上已经把自动图集功能关掉了,是靠手动修改数据文件,修改图片链接和textureRect来产生的图集效果。所以,实际上这个做法也可以延用到TP上,修改赋值代码即可。

Unity自动图集最主要的优点,是可以一开始的时候不打图集,在最后才考虑图集的事情。也因为这个原因,修改图集归属是无成本的,而这正是TP的缺点。所以用同样的方法,在最后才接入TP的图集和结构数据,就能够综合两者的优点,扬长避短。

不过,TP比Unity图集多的功能也就是精灵的90度旋转了,紧密布局则是双方共有的(Unity2017的新版图集 & 自带图集实现TP的Polygon布局https://zhuanlan.zhihu.com/p/32591450)。而且这些功能都需要修改UI组件才能获得支持,也并不必要。

本来就不使用TP的项目,倒也没必要引用一个大部分功能重复的三方库进来。


这是侑虎科技第366原创文章,感谢作者flashyiyi供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

作者知乎:https://zhuanlan.zhihu.com/p/32674470,同时,作者也是U Sparkle活动参与者哦,UWA欢迎更多开发朋友加入U Sparkle开发者计划,这个舞台有你更精彩!

  • Crazy_Liu 发表在 2018年03月13日 回复

    IOS不是用的RGBA Compressed PVRTC 4bit吗?