Unity iOS 使用 ASTC 格式纹理实践

本文介绍了在iOS和Android平台上,如何通过使用ASTC纹理格式提升游戏的图像质量和内存效率。文章指出,虽然PVRTC在iOS上广泛使用,但其透明通道质量不佳,而ASTC则提供了更高的压缩率和质量。作者分享了一种根据设备硬件条件动态选择使用PVRTC或ASTC的方案,详细阐述了实现过程,并对比了不同纹理格式的优缺点及适用场景。
部署运行你感兴趣的模型镜像


引言

上一篇文章描述了如何在不修改自定义渲染组件的前提下使用 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,从而数倍的增加其内存占用,让游戏瞬间被系统杀掉。

针对这些硬件限制,我们基于项目本身的特点一般有以下的考量:
 

  • 在 iOS 上如果项目是个大型游戏,可以放弃低端机器的用户,那么就直接使用 ASTC;
  • 在 android 上,如果可以得到准确的市场占有率,并确定可以放弃这一部分市场的前提下,可以直接使用 ASTC;
  • 现在立项的移动端游戏,且未来会有开发时长 2 年以上的,可以考虑只使用 ASTC;
  • 否则,如果你想考虑低端用户,那么还是需要考虑 ASTC 和传统格式的兼容性问题,我们接下来就会讲到这套实现方案:根据硬件条件选择使用不同的纹理格式。


根据硬件条件选择使用不同纹理格式

1. 基本思路

首先,对于直接引用打入包内的方式是不适合本方案的。本方案基于通过 AssetBundle 加载资源的项目(包括动态加载和依赖加载)。

基本思路为:在打包时生成一份完整的 AssetBundle 包以后,再针对需要使用 ASTC 的纹理所在的 AssetBundle 包重新打一份 ASTC 版本的;在运行时根据硬件支持情况等信息来选择加载哪一份 AssetBundle 包(PVRTC/ASTC)。

这样在游戏运行过程中,如某个 prefab 引用了一个 sprite,可以通过硬件情况选择不同的 AssetBundle 包,加载不同QQ靓号出售包中相同的 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.BuildTimeOnlyacker.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:
 

  • 适用于:对包体体积不那么敏感,有开发能力和时间,希望纹理质量高同时内存占用小
  • 不适用于:对包体体积敏感,开发能力和时间有限

您可能感兴趣的与本文相关的镜像

Yolo-v8.3

Yolo-v8.3

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

<think>好的,用户的问题是关于如何在Unity中开启ASTC纹理压缩的最佳设置代码示例。我需要先理解用户的需求,然后根据提供的引用资料来构建答案。 首先,用户提到他们想查找在Unity中开启ASTC纹理压缩的最佳设置代码。根据引用资料中的信息,ASTC是一种高质量的压缩格式,适用于移动设备,尤其是iOSAndroid。例如引用3和4提到ASTCiOS9及以上和部分Android设备上支持,并且能有效优化内存和渲染效率。 接下来,我需要确定Unity中如何通过代码或编辑器设置来配置ASTC压缩。引用3和4提到ASTC的块大小选择,比如ASTC 6x6或5x5,不同的块大小会影响压缩率和质量。例如,ASTC 4x4提供较高的质量但较大的尺寸,而6x6则更节省内存但质量稍低。 用户可能需要的是如何通过脚本或编辑器设置来批量修改纹理的压缩格式ASTC。根据Unity的文档,通常可以通过TextureImporter来设置纹理的压缩格式。因此,编写一个编辑器脚本可能是一个常见的解决方案。 然后,我需要考虑不同平台(AndroidiOS)的设置差异。引用3指出,Android需要OpenGL ES 3.1或Vulkan支持ASTC,而iOS从A8架构开始支持。因此,在代码中需要分别处理这两个平台的设置。 另外,用户可能还关心如何在Unity的Project Settings中配置全局的纹理压缩设置,确保所有纹理默认使用ASTC。这可能涉及到修改Player Settings中的纹理压缩选项。 最后,需要提醒用户测试不同的块大小,因为不同项目对画质和内存的需求不同。例如,引用4提到从ETC2转到ASTC 5x5带来的优化收益,说明块大小的选择需要根据具体情况调整。 总结步骤:1. 确认平台支持;2. 使用编辑器脚本批量设置纹理的压缩格式;3. 配置Project Settings中的默认压缩格式;4. 建议测试不同块大小。同时,需要提供代码示例,并引用相关资料中的信息来支持建议的合理性。</think>在Unity中开启ASTC纹理压缩的最佳设置需要结合平台特性和项目需求,以下是具体实现方法和代码示例: ### 一、ASTC配置核心代码 ```csharp using UnityEditor; using UnityEngine; public class ASTCCompressionSettings : AssetPostprocessor { void OnPreprocessTexture() { TextureImporter importer = (TextureImporter)assetImporter; // 安卓平台设置 importer.SetPlatformTextureSettings( new TextureImporterPlatformSettings { name = "Android", overridden = true, format = TextureImporterFormat.ASTC_6x6, // 平衡画质与内存 compressionQuality = 50 }); // iOS平台设置 importer.SetPlatformTextureSettings( new TextureImporterPlatformSettings { name = "iPhone", overridden = true, format = TextureImporterFormat.ASTC_5x5, // iOS推荐配置 compressionQuality = 50 }); } } ``` ### 二、关键设置说明 1. **块尺寸选择**(基于引用[3][4]) - ASTC_4x4:最高画质(内存占用较大) - ASTC_6x6:推荐平衡方案(内存减少33%) - ASTC_8x8:最低画质(内存节省50%) 2. **平台适配要求** ```csharp // Android需要开启GLES3.1支持 PlayerSettings.SetGraphicsAPIs( BuildTarget.Android, new[] { GraphicsDeviceType.OpenGLES3 }); ``` ### 三、编辑器全局设置 在`Project Settings > Player > Other Settings`: 1. 勾选`Allow ASTC HDR`(支持HDR纹理) 2. 设置`Texture Compression`为`ASTC` ### 四、性能优化建议 - 测试不同块尺寸的PSNR值(建议不低于30dB) - 使用Mipmap Streaming减少内存占用 - 对UI纹理使用ASTC_8x8,3D模型贴图使用ASTC_6x6[^3][^4]
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值