Unity iOS 使用 ASTC 格式纹理实践
时间:2020-08-26 14:20:12
浏览:次
点击:次
作者:编辑部
来源:游戏王国
立即下载
引言 上一篇文章描述了如何在不修改自定义渲染组件的前提下使用 alpha 分离的纹理来提升 iOS 的透明压缩纹理质量(见这里:上一篇的链接)。 在这个方案投入项目开始使用一段时间之后,UI 同学又来找我抱怨了:虽然一些贴图的不透明部分不会显得脏了,但是为什么有些纹理的半透明部分表现这么奇怪呀?这就涉及到上次方案中透明通道贴图格式的选择问题了。在上一篇文章中,我们的透明通道贴图在 iOS 上使用了 pvrtc4 的压缩方式,这样的做法造成了部分贴图半透明部分显示上的效果降低,在一些特定的情况下效果甚至没法接受。那么如何解决这个问题呢?这一次我们要来说明另一种贴图质量低下的解决方案:使用 ASTC 格式纹理,并且解释针对目前部分硬件的限制所作出的调整。 ASTC 关于纹理格式的一些基础知识可以在上一篇文章中开头的基础中看到,在此不做赘述。这里简单说明一下 ASTC 这种格式。 1. 概述 ASTC(Adaptive Scalable Texture Compression)是一种基于块的有损纹理压缩格式,完整细节在 2012 年被提出。这种压缩格式有多种压缩率可选,同时有着较高的压缩率和较好的压缩质量。 2. 压缩率
在上面的表中展示了从 ASTC 格式从 4x4 到 12x12 每一个像素所占的比特数(bpp),可以看到最大(也就是 4x4)的占用为每个像素一个字节,那么一张 1024x1024 的贴图,使用 ASTC 4x4 压缩后,他的大小就是 1MB,这个大小和 ETC2 RGBA 8bpp 格式的大小相同。 3. 纹理质量 从结果来看,在相同的内存占用前提下,ASTC 相较其他传统压缩格式的质量更优;而在相同的质量前提下,占用内存更小。关于 ASTC 的压缩类型选择和质量相关说明, 可以参考这里:https://developer.nvidia.com/astc-texture-compression-for-game-assets 4. 支持机型 iOS 上,苹果从 A8 处理器开始支持 ASTC 格式(即从 iphone6,iPad mini 4 开始支持),而以前的 iPhone 5s 和 iPad mini 3 及之前的设备都不支持。 Android 上,未来的压缩格式正在从 ETC2 转向 ASTC。但是因为 android 机多样性,目前的市场普及率我暂不可知。 基本使用 现在在 Unity 中要使用 ASTC 格式非常简单:选择纹理导入设置,选择 ASTC 格式,完成!
这就结束了吗?显然事情并没有这么简单,上面提到过的硬件的限制使得我们无法这么简单的选择,因为在不支持的硬件上使用 ASTC 格式,会被软解为 RGBA,从而数倍的增加其内存占用,让游戏瞬间被系统杀掉。 针对这些硬件限制,我们基于项目本身的特点一般有以下的考量:
根据硬件条件选择使用不同纹理格式 1. 基本思路 首先,对于直接引用打入包内的方式是不适合本方案的。本方案基于通过 AssetBundle 加载资源的项目(包括动态加载和依赖加载)。 基本思路为:在打包时生成一份完整的 AssetBundle 包以后,再针对需要使用 ASTC 的纹理所在的 AssetBundle 包重新打一份 ASTC 版本的;在运行时根据硬件支持情况等信息来选择加载哪一份 AssetBundle 包(PVRTC/ASTC)。 这样在游戏运行过程中,如某个 prefab 引用了一个 sprite,可以通过硬件情况选择不同的 AssetBundle 包,加载不同包中相同的 Sprite,然后引用到不同压缩格式的纹理上,就达到了我们的目的。 2. 构建测试工程 基于上面的思路,我们来构建一个测试工程,测试工程需要包含以下内容:
3. 实现细节 首先是打 AssetBundle 包流程,这块不会详细说,因为每个项目的实现都可能不同。在这里,我们的思路是:添加需要动态加载的 AB 包规则,最后计算出它们依赖的资源,按规则打成其他依赖 AB 包。下面的代码示意了这一过程。 var builder = new AssetBundleBuilder(); 其中重点在 UpdateSharedAssets() 方法中,其中不仅计算了所有的依赖关系,同时记录了 AtlasBundleMap 和 BundleMap。 下图为 BundleMap 的格式,最重要的几个字段是 BundleName(记录 AB 包名),AstcVariant(是否有其 ASTC 版本),AstcVariantBundleName(ASTC 版本的 AB 包名)。这个文件会打入最终的 AB 包内,在运行时加载资源时根据 AstcVariant 和 AstcVariantBundleName 来判断并加载对应的 ASTC 版本的包。
下图为 AtlasBundleMap 的格式。这个文件记录了需要再打一份 ASTC 版本 AB 包的包名列表,在后续的打包过程中会用到。
经过上面的过程,完整的 AB 包已经都打出来了,下面需要重新打一批 ASTC 版本的 AB 包。首先重新打一次图集: EditorSettings.spritePackerMode = SpritePackerMode.BuildTimeOnly 上面需要注意的是:两次打图集需要有一个不同的标志:MakeAstc,第一次为 false,第二次为 true,在自定义的图集打包规则中才可以判断出两次需要打的是不同的图集。自定义图集规则中的实现为: if (MakeAstc){ 更多的关于自定义图集规则可以参考官方文档:https://docs.unity3d.com/Manual/SpritePacker.html 之后使用同样的 BuildAssetBundleOptions,根据 AtlasBundleMap 中信息对相应资源再打一次 AB 包即可,打完后改名拷贝到之前的文件夹中,现在的 AB 包目录就像是这样:
其中 astc_ 开头的都是对应的 AB 包的 ASTC 版本。 接下来我们来到运行时。首先是判断硬件是否支持 ASTC 格式: public static bool DeviceSupportAstc(){ 其次是加载资源: private AssetBundle LoadBundleCore(string bundleName){ 这是所有加载 AB 包时都会调用到的方法(包括动态加载和依赖加载)。其中的过程也很简单:判断了硬件情况,根据 BundleMap 找到需要加载的 ASTC 版本的 AB 包名,并去加载。 到这里以后我们的测试工程就已经完成了(详细的代码可以看文末的代码仓库一节)。我们可以看到在这个 demo 中,我们可以显示当前动态加载和依赖加载的纹理格式,并切换到另一种纹理,实现了 ASTC 和传统格式之间的选择性加载。
我们再使用 Profiler 工具来对比一下两种不同格式的纹理的内存占用:
上图为 ETC2 格式(为了测试方便,没有使用 PVRTC,结果是一样的)的内存占用,64.2KB,即等于 256*256*8/8。
上图为 ASTC 6x6 格式的内存占用,29.1KB,即等于 256*256*3.56/8。 ASTC 6x6 的内存占用比 PVRTC 4bpp 还小了 10%,但是质量却要好的很多很多。 补充说明 1. 延伸一下,因为上面的方案多生成了一批图集相关的 AB 包,所以会让包体有所增大。如果你们项目中有资源更新,那么支持 ASTC 和不支持 ASTC 的设备就需要根据自己的情况来下载不同的资源包。如果要做的好的话这个问题也是需要考虑的。当然这里就不讨论了,有需要的同学可以自己研究。 2. 引言中提到的透明通道贴图 PVRTC RGBA 4bpp 格式造成了贴图的半透明区域显示质量差的问题。我之后也尝试使用 Alpha8 格式来代替,但是实际测试发现所有图都看不到了,通过看 Unity 的默认 spriterenderer 和 DefaultETC1 的 shader 才知道,他们都使用了 alpha 通道贴图的 R 通道,而 Alpha8 格式取不到,所以贴图都不显示了。要解决这个问题可能需要使用 R8 格式来代替 Alpha8 格式,但是因为我使用的 Unity2017 不支持,所以就没有继续尝试了。 结论 包括前一篇文章,在 iOS 上我们一共使用了 3 种贴图压缩格式的方案(android 同理):
这几种方案各有优劣和使用场景,我下面就试着总结一下。 1. 方案优缺点对比 PVRTC:
分离透明通道:
根据硬件选择使用 PVRTC 还是 ASTC:
2. 方案适用场景对比 PVRTC:
分离透明通道:
根据硬件选择使用 PVRTC 还是 ASTC:
来源:indienova 原文:https://mp.weixin.qq.com/s/jVnO6uDq7uEuA8cjjZVP3A |
平均评分
0人
- 5星
- 4星
- 3星
- 2星
- 1星
- 暂无评论信息

acker.SelectedPolicy = typeof(CustomSpritePackerPolicy).Name;CustomSpritePackerPolicy.MakeAstc = true;Packer.RebuildAtlasCacheIfNeeded(target, true, Packer.Execution.ForceRegroup);