压缩纹理

阅读时间:6分钟更新于 2026-03-25 15:19

「导出」暂不支持压缩纹理格式

简介

KTX2 (Khronos Texture Container version 2.0) 是 Khronos 推出的现代纹理容器格式。KTX2 会根据设备平台支持,在运行时转码到对应格式的压缩纹理(ASTC/ETC/BC/PVRTC等)。压缩纹理在移动设备上的内存占用比传统图片格式低很多。glTF 中使用 KTX2 需要包含 KHR_texture_basisu 扩展。Galacean Effects 自 2.8.0 版本开始支持 KTX2。

考虑到 ASTC 4×4 格式在移动设备上的广泛支持度(覆盖几乎所有现代移动设备),Galacean Effects 采用 UASTC 编码存储在 KTX2 容器中,在运行时转码为 ASTC 4×4 压缩纹理。这种方案简化了集成复杂度并保证了最佳兼容性。

原始图片 → UASTC 编码 → KTX2 容器 → Runtime 转码 → ASTC 4×4 → GPU

使用

1. 动效制作发布

在发布时,使用极速发布并勾选“使用压缩纹理”:

推荐使用体积更小策略

2. 插件引入

需要额外接入插件(请确保插件版本号与 @galcean/effects 版本号一致)。

安装依赖

$ npm i @galacean/effects-plugin-ktx2 --save

代码引入

import { Player } from '@galacean/effects';
// 确保在 @galacean/effects 引入之后再引入插件, 插件版本保持和 @galacean/effects 一致
import '@galacean/effects-plugin-ktx2';

3. 运行时使用

a. 方案 A:使用 AssetManager 预加载(推荐)

创建AssetManagerloadScene 时设置 useCompressedTexture: true

const assetManager = new AssetManager({ useCompressedTexture: true }); // ✅ 启用 KTX2

const loadedScene = await assetManager.loadScene(sceneData, player.renderer);

await player.loadScene(loadedScene, {
  useCompressedTexture: true,
});

b. 方案 B:直接加载场景

loadScene 时设置 useCompressedTexture: true

await player.loadScene('xx.json', {
  useCompressedTexture: true,
});

Runtime 会默认使用主线程解析 KTX2 文件,如若想使用 WebWorker 解析,可先注销后再注册:

import { unregisterKTX2Loader, registerKTX2Loader } 
  from '@galacean/effects-plugin-ktx2';

// 先注销 KTX2 加载器
unregisterKTX2Loader();
// 使用 WebWorker,worker 数为 2 个
registerKTX2Loader(2);

💡小知识

  • Worker 的装载有一定的耗时,因设备而异,经真机测试,图片资源较少时(如:支付宝 2025 年度账单),使用主线程解析加载耗时收益较高。如资源非常多,可使用 Worker 解析对比尝试。在实际使用中,建议根据项目实际资源量进行性能对比测试再做选择。

注意事项

  • KTX2 转码使用到了 WebAssembly 技术,需要保证使用 Chrome 57+,和 iOS 11.3+(11.0 ~ 11.2 以下的 WebAssembly 存在 bug)。
  • 使用 Worker 解析,需知晓:iOS 16 以下系统在通过 Worker 加载必要的 KTX2 解析文件时会概率发生无返回的情况。

为什么选择 KTX2 ?

传统纹理格式

以下图为例:前端习惯叫做雪碧图,在游戏界一般叫做 Atlas 图集,原始分辨率为 1024 × 1024,文件大小为 1.7 MB

一般情况下,浏览器会将 JPEG/PNG 图片文件解压,并解码成显卡可识别的 RGB(A) 位图格式纹理。若图片带有 Alpha 通道,则需要占用 1024 × 1024 × 4 = 4 MB 的内存,这 4MB 的数据同时也需要加载到 GPU 缓存中。

关键点: 无论将图片的存储体积压缩得多么小,其显存开销始终是固定的——引擎运行时纹理占用的显存大小,与图片本身的磁盘大小无关,只与图片的宽度和高度相关

因此,使用 JPEG/PNG 文件作为纹理时,存在以下问题:

  • 🕐 解码耗时:浏览器解压图片存在耗时,图片越大,耗时越长
  • 💾 内存占用高:除原始文件的存储外,浏览器和 GPU 还会各自保存一份位图数据(见图示 1)

可以看出,在大体量纹理应用场景下,上述第二个问题的瓶颈会越来越明显,在移动设备上很容易造成 OOM(内存溢出)。

硬件压缩纹理格式

压缩纹理是游戏领域常用的纹理压缩技术,依赖特定硬件实现。经过特定算法压缩后的纹理可直接存储在 GPU 中,只有当 Shader 进行纹理查询(Texture Lookup)时才会触发解压操作,GPU 通常会对解压过程进行优化以提升性能。

相比 JPEG/PNG,压缩纹理在内存上具有质的优势:

  • 显存占用极低:GPU 直接保存压缩后的纹理数据,无需解码为完整位图(见图示 2)
  • 省去解码耗时:无需像 JPEG/PNG 那样进行完整的图片解码
  • 内存可控:如图示 2 所示,JS Heap 中的占用可以手动控制释放

约束与代价

压缩纹理并非没有限制,使用时需注意以下几点:

约束项

说明

有损压缩

压缩纹理属于有损压缩,会对图片质量造成一定损失

传输体积

压缩纹理的传输体积可能比 JPG/PNG 高 1~4 倍

平台差异

不同平台支持的压缩纹理格式不同,若需降级兼容,可能需要准备多份资源

尺寸要求

压缩纹理要求 POT(Power Of Two),即长宽均为 2 的幂次;PVRTC 格式还额外要求长宽相等

KTX2 的优势

尽管压缩纹理有着显著的性能优势,但其平台差异性始终是工程实践中的一大难题:

  • iOS 设备普遍支持 PVRTC / ASTC
  • Android 设备普遍支持 ETC / ASTC
  • PC 端 普遍支持 BC 系列格式

不同平台需要分别打包对应格式的压缩纹理资源,若要做到全平台兼容,通常需要维护多套资源,这极大地增加了资源管理和发布的复杂度。

需要维护多套资源:
├─ iOS 设备     → texture.pvr (PVRTC)
├─ 高端 Android → texture.astc (ASTC)
├─ 老 Android   → texture.ktx (ETC2)
├─ PC/WebGL     → texture.dds (BC7)
└─ 兜底方案     → texture.png

构建脚本:
if (platform === 'ios') {
  compile('pvrtc');
} else if (isModernAndroid) {
  compile('astc');
} // ...复杂的判断逻辑

发布产物:
resources/
├─ texture.png      
├─ texture.pvr      
├─ texture.astc    
└─ texture.etc2 

KTX2 正是为了解决这一问题而生。 它以 UASTC 作为统一的中间格式存储在 KTX2 容器中,在运行时根据当前设备的硬件能力动态转码为对应的压缩纹理格式。它有以下优势:

  • 🗜️ 超级压缩:传统压缩纹理(如ASTC/ETC 文件)虽然节省了显存,但其传输体积往往比普通纹理格式还要大。KTX2 在此基础上引入了超级压缩机制,在 GPU 压缩格式之上额外套一层通用压缩,大幅减小文件的传输体积。
  • 📦 一份资源,全平台适配:无需为不同平台单独维护多套压缩纹理资源,研发侧只需关注 KTX2 一种格式,无需处理繁琐的平台分支逻辑,降低集成复杂度。
  • 保持压缩纹理的全部优势传输体积显著小于传统压缩纹理格式,CPU 解压速度快,不影响运行时性能,GPU 侧依然使用原始压缩格式,显存优势完全保留。
  • 📋 标准化容器:相比各厂商私有格式(.dds/.pvr 等),KTX2 是 Khronos 制定的开放标准,元数据更丰富,
    Mipmap 支持统一,工具链一致(toktx/ktxtools)。
  • 🔗 glTF 生态集成:KTX2 已被 glTF 2.0 官方扩展采用(KHR_texture_basisu),Three.js、Babylon.js 等主流 Web 引擎均已内置支持。

在 Galacean Effects 中,考虑到 ASTC 4×4 格式在现代移动设备上的广泛覆盖,运行时统一将 KTX2 转码为 ASTC 4×4 格式,再不支持的设备上会自动降级为 WebP 格式,在保证兼容性的同时实现最佳的内存与性能表现。

Preview