Unity iOS使用ASTC格式纹理实践
引言
上一篇文章描述了如何在不修改自定义渲染组件的前提下使用 alpha
分离的纹理来提升 iOS 的透明压缩纹理质量(见这里:https://indienova.com/indie-game-development/unity-alpha-separate/)。
在这个方案投入项目开始使用一段时间之后,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
,从而数倍的增加其内存占用,让游戏瞬间被系统杀掉。
针对这些硬件限制,我们基于项目本身的特点一般有以下的考量:
- 在 iOS 上如果项目是个大型游戏,可以放弃低端机器的用户,那么就直接使用
ASTC
; - 在 android 上,如果可以得到准确的市场占有率,并确定可以放弃这一部分市场的前提下,可以直接使用
ASTC
; - 现在立项的移动端游戏,且未来会有开发时长 2 年以上的,可以考虑只使用
ASTC
; - 否则,如果你想考虑低端用户,那么还是需要考虑
ASTC
和传统格式的兼容性问题,我们接下来就会讲到这套实现方案:根据硬件条件选择使用不同的纹理格式。
根据硬件条件选择使用不同纹理格式
1. 基本思路
首先,对于直接引用打入包内的方式是不适合本方案的。本方案基于通过 AssetBundle 加载资源的项目(包括动态加载和依赖加载)。
基本思路为:在打包时生成一份完整的 AssetBundle 包以后,再针对需要使用 ASTC
的纹理所在的 AssetBundle 包重新打一份 ASTC
版本的;在运行时根据硬件支持情况等信息来选择加载哪一份 AssetBundle 包(PVRTC
/ASTC
)。
这样在游戏运行过程中,如某个 prefab 引用了一个 sprite,可以通过硬件情况选择不同的 AssetBundle 包,加载不同包中相同的 Sprite,然后引用到不同压缩格式的纹理上,就达到了我们的目的。
2. 构建测试工程
基于上面的思路,我们来构建一个测试工程,测试工程需要包含以下内容:
- 打 AssetBundle 包流程
- 生成 BundleMap,记录
ASTC
信息,供运行时使用 - 生成 AtlasBundleMap,记录需要重新打
ASTC
版本 AssetBundle 包的信息,供打包时使用 - 运行时资源加载,根据硬件条件来选择不同的 AssetBundle 包
3. 实现细节
首先是打 AssetBundle 包流程,这块不会详细说,因为每个项目的实现都可能不同。在这里,我们的思路是:添加需要动态加载的 AB 包规则,最后计算出它们依赖的资源,按规则打成其他依赖 AB 包。下面的代码示意了这一过程。
var builder = new AssetBundleBuilder();
builder.AddSceneBundle("Assets/Scenes/Test.unity", "scenes_");
builder.AddSceneBundle("Assets/Scenes/Loading.unity", "scenes_");
builder.AddDirBundle("Assets/Textures/Dynamic", "", "textures_");
builder.UpdateSharedAssets();
builder.BuildAssetBundles(abPath, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);
其中重点在 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;
Packer.SelectedPolicy = typeof(CustomSpritePackerPolicy).Name;
CustomSpritePackerPolicy.MakeAstc = true;
Packer.RebuildAtlasCacheIfNeeded(target, true, Packer.Execution.ForceRegroup);
上面需要注意的是:两次打图集需要有一个不同的标志:MakeAstc
,第一次为 false
,第二次为 true
,在自定义的图集打包规则中才可以判断出两次需要打的是不同的图集。自定义图集规则中的实现为:
if (MakeAstc)
{if (IsTransparentCompressed(settings.format)){settings.format = Util.ASTC_RGBA_FORMAT;}else if (IsOpaqueCompressed(settings.format)){settings.format = Util.ASTC_RGB_FORMAT;}
}
更多的关于自定义图集规则可以参考官方文档:https://docs.unity3d.com/Manual/SpritePacker.html
之后使用同样的 BuildAssetBundleOptions
,根据 AtlasBundleMap
中信息对相应资源再打一次 AB 包即可,打完后改名拷贝到之前的文件夹中,现在的 AB 包目录就像是这样
其中 astc_
开头的都是对应的 AB 包的 ASTC
版本。
接下来我们来到运行时。首先是判断硬件是否支持 ASTC
格式:
public static bool DeviceSupportAstc()
{var support = true;support = support && SystemInfo.SupportsTextureFormat(ASTC_RGB_FORMAT);support = support && SystemInfo.SupportsTextureFormat(ASTC_RGBA_FORMAT);return support;
}
其次是加载资源:
private AssetBundle LoadBundleCore(string bundleName)
{if (Util.UseAstc && m_bundleMap != null){BundleMapData bundleMapData = null;if (m_bundleMap.TryGetValue(bundleName, out bundleMapData)){if (bundleMapData.AstcVariant){bundleName = bundleMapData.AstcVariantBundleName;}}else{Debug.LogError("Cannot find bundle name in bundle map: " + bundleName);}}var bundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + bundleName);return bundle;
}
这是所有加载 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 同理):
- 直接使用
PVRTC
- 分离透明通道的
PVRTC+PVRTC
,或者PVRTC+Alpha8
- 根据硬件情况的不同来选择使用
PVRTC
还是ASTC
这几种方案各有优劣和使用场景,我下面就试着总结一下。
1. 方案优缺点对比
PVRTC:
- 优点:开发方便,不需要做额外处理,所有 iOS 设备统一支持
- 缺点:纹理质量很差
分离透明通道:
- 优点:不受硬件限制
- 缺点:开发繁琐(一次性),如果使用 pvrtc+pvrtc 方案,半透明区域质量不好;如果使用 pvrtc+alpha8 方案,内存占用会增加到 12bpp,且可能不支持所有 unity 版本
根据硬件选择使用 PVRTC 还是 ASTC:
- 优点:内存占用少,纹理质量高
- 缺点:开发繁琐(一次性),包体图集纹理占用存储空间多一倍
2. 方案适用场景对比
PVRTC:
- 适用于:不那么关注贴图质量,希望内存占用低,希望开发成本低
- 不适用于:有品质要求的项目
分离透明通道:
- 适用于:希望透明贴图的非透明部分不脏,不希望包体增大,2018 以上版本 unity 项目,对于内存占用不那么敏感
- 不适用于:2018 以下版本 unity 项目,希望内存占用低
根据硬件选择使用 PVRTC 还是 ASTC:
- 适用于:对包体体积不那么敏感,有开发能力和时间,希望纹理质量高同时内存占用小
- 不适用于:对包体体积敏感,开发能力和时间有限
参考资料
- ASTC 纹理压缩格式详解:
https://zhuanlan.zhihu.com/p/158740249 - Using ASTC Texture Compression for Game Assets:
https://developer.nvidia.com/astc-texture-compression-for-game-assets
代码仓库
https://github.com/RayRiver/UnityAstcSupportDemo
参考链接:
ASTC纹理压缩格式详解
UI优化方案(2)——RGBA,压缩格式,纹理大小计算
Unity iOS使用ASTC格式纹理实践相关推荐
- Unity iOS 使用 ASTC 格式纹理实践
引言 上一篇文章描述了如何在不修改自定义渲染组件的前提下使用 alpha 分离的纹理来提升 iOS 的透明压缩纹理质量(见这里:上一篇的链接). 在这个方案投入项目开始使用一段时间之后,UI 同学又来 ...
- Unity iOS打开AppStore评星页面,浅谈Application.OpenURL()方法。
http://fairwoodgame.com/blog/?p=38 Unity iOS打开AppStore评星页面,浅谈Application.OpenURL()方法. Posted in Uni ...
- iOS即时语音聊天技术实践
CMDN Club第十五期活动已于3月15日顺利举行,本次活动以"移动平台语音技术的应用与实践"主题,以语音技术开发为焦点,从语音基础服务.语音产品开发.语音技术实现等多个维度,探 ...
- ios图像和图形最佳实践(三)
没有前面进度的同学还是从(二)开始,否则会感觉比较突兀 ios图像和图形最佳实践(二) - 对于我们的app所附带的图片 苹果强烈建议我们使用图像素材来存储 这其中有很多原因 图像素材针对基于名称和基 ...
- Unity中的音频格式
日常使用中常见的音频格式 WAV:微软公司开发的一种声音文件格式 特点:简单的编/解码.普遍的认同/支持以及无损耗存储,目前Windows上最流行的声音文件格式 AIFF:是APPLE公司开发的一种音 ...
- Go语言国际电子表格文档格式标准实践
在 Gopher Meetup 北京站上,阿里巴巴高级开发工程师.前百度 Go 语言编程委员会成员续日进行了主题为<Go语言国际电子表格文档格式标准实践>的演讲. Excelize 是 G ...
- unity iOS 微信支付 教程
时间:2018.6 Unity版本:5.6.2 平台:Unity+iOS 微信SDK版本:2018.6月 1.8.2版本 适用人群:unity 开发,oc基础较弱 食用前提:请确保已经清楚官方的整个支 ...
- 微信小程序IOS机型时间格式显示NaN异常问题
new Date()转换时间时,IOS机型时间格式显示NaN 错误原因: ios不支持时间为2020-05-29这种格式的日期,必须转换为2020/05/29这种格式才能使用new Date()进行转 ...
- Unity iOS内购
前言:最近项目需要切换到iOS平台做一些提交审核和支付对接相关的工作,上一篇刚分享了最新的iOS10提交审核的一些坑,这篇分享一些内购相关的流程. Unity iOS内购 思路: Unity调用iOS ...
- 微信小程序解决ios端时间格式兼容的问题
微信小程序获取当时时间new Date时,会在ios真机上出现问题,具体表现为时间格式会报错并显示出NaN.这个bug并不会在模拟器和安卓系统出现,只会在ios系统上出现,具体原因是ios不支持符号 ...
最新文章
- java try catch嵌套_解析Java中未被捕获的异常以及try语句的嵌套使用
- JS组件系列——BootstrapTable 行内编辑解决方案:x-editable
- Python 计算机视觉(十四)—— OpenCV 进行霍夫变换
- application/x-www-form-urlencoded与application/json区别以及遇到的坑
- C/C++ list链表的理解以及使用
- 《女士品茶》与统计检验
- JS生成uuid的四种方法
- xamp环境搭建Pikachu实验环境搭建
- C++线性表(单链表)的应用算法(附源码)
- 安全好用的快充头,iPhone 13的好搭档,RAVPOWER 20W充电器上手
- 安卓手机投屏软件_直播教程 | 安卓手机投屏队伍语音解决方案1
- 考研350什么水平计算机,考研350分的难度相当于高考考什么水平?很多人都不知道...
- CCCP(convex-concave procedure)优化算法的一些理解
- 关于表格分页缓存数据
- 华为机试——翻译电话号码
- 还在为无线困扰?思博伦STC WiFi测试方案助力体验提升
- 基于miu小波变换的人体步态数据检测和识别算法matlab仿真
- 前端开发,富文本编辑器推荐quilljs,tiptap
- chosen.jquery.js 、chosen-select 源码修改控制 chosen:updated 方法动态更新下拉框选项不更新搜索框值 ,chosen 实现远程搜索加载下拉选项
- 关于申请个人域名和虚拟主机---虚拟主机篇
热门文章
- Java项目:ssm实验室设备管理系统
- [项目分享]JSP+Servlet+JDBC实现的shine网上书城
- EXCEL冻结窗口只想冻结前两行
- 《Windows安全机制》之DEP(数据执行保护)
- Ubuntu(debian) 程序 dep 打包
- 作为一个大学才开始入门学计算机编程的孩子想要的东西-----听我扯,你蛋疼,他菊紧,我开心...
- 2020全国各地男女身高排行出炉,从数据看南北差异到底有多大?
- 在linux下 配置内网ip`
- Python @property详解及底层实现介绍
- 【Unity】在Scene窗口中发射射线