Skip to content

KTX2 与 Draco 压缩方案 ​

Web 端 3D 的第一道生死线是下载体积:虚拟形象全套模型原始 22MB,4G 下首屏要等到用户流失完。用 KTX2 压纹理、Draco 压几何,全套压到 6MB,核心形象压进 3MB。

全套模型22MB → 6MB
纹理体积-60~80%
几何压缩率≈70%

背景 ​

Web 端虚拟形象项目的资源分两块:纹理(PNG/JPEG)和几何(GLB 里的顶点、法线、UV)。传统 WebGL 管线里这两块各有一个结构性浪费:纹理要先 CPU 解码成 RGBA32 再上传 GPU,显存里存的是未压缩位图;几何数据按 float32 原样存储,传输量随模型精度线性膨胀。

问题 ​

问题量化表现
下载体积全套模型 22MB,移动端首屏不可接受
显存占用PNG 解码成 RGBA32 上传,显存是文件体积的数倍
解压阻塞几何解压放主线程会卡交互

方案 ​

mermaid
flowchart TD
  A["美术导出 GLB(原始 20MB+)"] --> B["纹理: PNG → KTX2<br>toktx --bcmp --zcmp"]
  A --> C["几何: Draco level 10<br>gltf-pipeline"]
  B --> D["运行时: GPU 原生解码<br>无需 CPU 解压成 RGBA32"]
  C --> E["运行时: DRACOLoader<br>Web Worker 异步解压"]
  D --> F["最终 GLB 3MB 以内"]
  E --> F

两条压缩路径互相独立、各自闭环:KTX2 解决「显存 + 体积」,Draco 解决「体积 + 传输」。

关键实现 ​

KTX2:让 GPU 自己解压 ​

传统流程是 PNG/JPEG → CPU 解码 → 未压缩格式上传 GPU → 显存爆炸。KTX2 的思路是把压缩格式一路送到 GPU,由硬件原生解码——BC7(Desktop)、ETC2(Android)、ASTC(iOS/新 Android)都是 GPU 直接支持的块压缩格式,显存里存的还是压缩态。

转换命令:

bash
# PNG → KTX2(Basis UASTC/ETC1S + zstd 超压缩)
toktx --t2 --bcmp --zcmp 16 output.ktx2 input.png

代价是平台格式分裂,需要多份压缩格式加运行时检测:

平台BC7ETC2ASTC
Desktop✅——
Android—✅✅
iOS——✅

收益:纹理体积减少 60–80%,显存占用同比例下降,CPU 解码整个环节被删掉。

Draco:几何数据压到三成 ​

Draco 是 Google 开源的 3D 几何压缩库,专为 Web 传输优化,对顶点位置、法线、UV 做量化和熵编码:

bash
# GLB → Draco GLB
gltf-pipeline -i input.glb -o output.glb --draco.compressionLevel=10

Three.js 端用 DRACOLoader 异步解压。两个关键取舍:

  • 压缩级别拉到 10(最高):解压略慢,但放在 Web Worker 里跑,不阻塞主线程,用户无感知;
  • 机型分级:低端机跳过 Draco 解压直接用简化模型,高端机用最高压缩——解压算力和下载带宽哪个是瓶颈,按机型分别回答。

实测压缩效果 ​

模型原始 GLBDraco 压缩压缩率
虚拟形象身体8MB2.5MB69%
配件(头发)3MB0.9MB70%
全套(含 5 个配件)22MB6MB73%

叠加 KTX2 后,核心形象(身体 + 默认发型)压进 3MB 以内,达到移动端可秒开的量级。

指标 ​

指标优化前优化后
全套模型体积22MB6MB(-73%)
核心形象体积20MB+3MB 以内
纹理体积/显存基线-60~80%
解压线程模型主线程风险Web Worker 异步

复盘 ​

  1. Web 端 3D 优化先砍体积再谈帧率——用户等不到渲染那一帧,压缩是投入产出比最高的一步。
  2. KTX2 的真正收益在显存而不是下载体积,低端机 OOM 崩溃里有一大半能被它消掉。
  3. Draco 级别 10 + Web Worker 是固定搭配;在主线程跑高级别解压等于把下载省下的时间还给白屏。
  4. 压缩方案必须配机型分级,低端机的瓶颈是解压算力,高端机的瓶颈是带宽,一套参数打两边必然有一边吃亏。

罗子初 · AI 座舱专家 · 8年端侧研发

Lv.1