不同平台不同命:压缩方案选型指南
前 4 篇分别讲了顶点压缩和纹理压缩的工具。但工具会用是一回事,该用哪个、在什么场景下用是另一回事。这篇就是来回答这个问题的——而且尽量让你看完就能直接拍板。
读完这一篇你应该能回答:我的项目跑在什么平台,首屏瓶颈是下载还是显存,纹理和顶点各该上哪套方案。
先定调:场景驱动,不是工具驱动
整篇文章只有一条核心原则,也是这个系列反复强调的:
没有「最好的」压缩方案,只有「最匹配场景」的方案。
影响选择的三个变量:
- 平台/设备:桌面 PC、移动浏览器、VR 头显、小程序——能力天差地别
- 使用方式:是一次性展示(电商产品页),还是长时间沉浸(VR 游戏)
- 首要瓶颈:是下载速度慢,还是显存不够,还是解码卡首屏
先把瓶颈想清楚,再回来选工具。下面这张矩阵就是把这套思路固化下来。
核心决策矩阵:平台 × 推荐方案
这是全文最重要的一张表。按平台分,给出纹理和顶点各自的推荐方案,以及理由。
| 平台 / 场景 | 纹理方案 | 顶点方案 | 主要瓶颈 | 关键理由 |
|---|---|---|---|---|
| 桌面 Web(PC 浏览器) | KTX2 或 WebP | MeshOpt / 量化 | 下载速度 | 显存宽裕,重点是文件小、加载快 |
| 移动 Web(手机浏览器) | KTX2(必上) | MeshOpt | 显存 | 手机显存紧,纹理必须块压缩 |
| WebXR / VR 头显 | KTX2(必上) | MeshOpt + LOD | 显存 + 帧率 | 显存爆炸会崩,掉帧会眩晕 |
| 微信小程序 | KTX2 / WebP | MeshOpt / 纯量化 | 包体 + 兼容性 | 包大小受限,避免重型解码器 |
| 电商产品展示 | WebP(轻)/ KTX2(精) | MeshOpt | 首屏速度 | 要求秒开,体积换速度 |
| 大型场景 / 数字孪生 | KTX2(必上) | MeshOpt + Draco + LOD | 显存 + draw call | 纹理多、模型大,全方位压 |
几个值得记住的判断:
- 只要显存是瓶颈,KTX2 就必上(移动、VR、大场景都是)
- 下载速度是瓶颈、显存宽裕时,WebP 也够用(桌面、电商)
- 小程序这类对包体敏感的环境,优先 MeshOpt 而不是 Draco——解码器小、兼容好
- 超大模型才考虑 Draco,绝大多数中小模型 MeshOpt 更均衡
纹理格式全维度对比
光知道「上 KTX2」还不够。具体到纹理格式,把所有维度摊开看:
| 格式 | 文件大小 | 显存占用 | 上传速度 | 兼容性 | 画质 | 适用场景 |
|---|---|---|---|---|---|---|
| PNG | 大 | 大(解压后) | 慢 | 极广 | 无损 | 需要精确数值/透明,或老平台兜底 |
| JPG | 极小 | 大(解压后) | 慢 | 极广 | 有损 | 颜色贴图、网络传输优先 |
| WebP | 很小 | 大(解压后) | 慢 | 较广 | 高 | 桌面 Web、追求下载速度 |
| AVIF | 更小 | 大(解压后) | 慢 | 渐进 | 高 | 新平台、极致压缩 |
| KTX2 (ETC1S) | 小 | 极小 | 快 | 需转码 | 中(颜色够用) | 颜色贴图、移动/VR |
| KTX2 (UASTC) | 中 | 小 | 快 | 需转码 | 高 | 法线/数据贴图 |
注意前三行(PNG/JPG/WebP/AVIF)的显存占用都是「大」——无论磁盘多小,进了显存都得解压回原始像素。这是传统格式的根本限制。
混合策略很常见:关键的颜色贴图用 KTX2 保证显存,次要贴图(比如一个小 emissive)用 WebP 省事。不必一刀切全 KTX2,按瓶颈分配。
决策流程图:一步步选
矩阵是结果,流程图告诉你怎么走到那个结果。
你的项目跑在哪?
│
├─ 桌面 Web(显存宽裕)
│ └─ 首屏慢吗?
│ ├─ 慢 → WebP(或 AVIF)+ MeshOpt [追求下载速度]
│ └─ 不慢,但模型多 → KTX2 + MeshOpt [为未来留余地]
│
├─ 移动 Web / VR / 大场景(显存紧)
│ └─ 纹理必上 KTX2(颜色 ETC1S,数据 UASTC)
│ └─ 模型顶点超密?
│ ├─ 是 → + Draco [极限压缩,能接受慢解码]
│ └─ 否 → + MeshOpt [均衡]
│
└─ 小程序 / 受限运行时
└─ 包体敏感吗?
├─ 敏感 → 纯量化或 MeshOpt + WebP [零/小解码器]
└─ 还行 → MeshOpt + KTX2 [常规组合]
顶点压缩 vs 纹理压缩:该投哪个
经常有人问:预算有限,先优化哪头?看模型构成:
| 模型特征 | 投入重点 | 原因 |
|---|---|---|
| PBR 角色/产品(贴图多) | 纹理压缩 | 纹理占 80%+,投顶点回报低 |
| CAD/扫描模型(顶点超密、贴图少) | 顶点压缩 | 顶点是主要体积 |
| 动画角色(骨骼+蒙皮) | 两头都投,但顶点优先 Draco 之外 | 动画数据也占体积,Draco 对动画支持弱 |
| 建筑/场景(大、中等贴图) | 纹理 + LOD | 纹理省显存,LOD 省 draw call |
一个粗略的收益排序:纹理 KTX2 > 顶点 MeshOpt/量化 > 几何简化(LOD) > 顶点 Draco。先做收益高的。
混合策略:关键贴图 KTX2,次要贴图 WebP
不是所有贴图都值得压成 KTX2。一个典型的 PBR 材质有 5-6 张贴图,全压 KTX2 工程量大,有时也不必要。
实用分法:
- 必 KTX2:albedo(大、颜色)、normal(精度敏感)、roughness/metallic(影响光照)
- 可 WebP/JPG:emissive(通常小)、AO(中低频)、细节法线(如果有的话)
判断标准:贴图大不大、是不是高频细节、会不会频繁采样。三者都中 → KTX2;都不中 → 传统格式省事。
自动化选型:gltf-transform 一键搞定
gltf-transform 的 optimize 命令是绝大多数场景的银弹——它会自动判断贴图类型、按推荐策略压缩纹理、可选地处理顶点。
# 安装
npm install -g @gltf-transform/cli
# 标准优化:纹理 KTX2 + 顶点 MeshOpt + 去冗余
gltf-transform optimize model.glb model-optimized.glb \
--texture-compress basisu \
--meshopt
参数说明:
| 参数 | 作用 | 默认 |
|---|---|---|
--texture-compress basisu | 纹理压成 KTX2(自动选 ETC1S/UASTC) | 关 |
--meshopt | 顶点启用 MeshOpt 压缩 | 关 |
--simplify | 几何简化(减少顶点,有损) | 关 |
--weld | 合并重复顶点 | 开 |
--prune | 移除未使用的节点/材质 | 开 |
一个适合移动端的「强力压缩」组合:
gltf-transform optimize model.glb model-mobile.glb \
--texture-compress basisu \
--meshopt \
--simplify --simplify-ratio 0.5 \
--prune --weld
桌面端「温和」组合(保留细节,只压纹理和顶点):
gltf-transform optimize model.glb model-desktop.glb \
--texture-compress webp \
--meshopt
编写成可复用脚本
把上面封装成按平台产出多个版本的脚本:
// compress.mjs —— 按 target 输出不同压缩版本
import { optimize } from "@gltf-transform/functions";
import { NodeIO } from "@gltf-transform/core";
import { KHRONOS_EXTENSIONS } from "@gltf-transform/extensions";
const io = new NodeIO().registerExtensions(KHRONOS_EXTENSIONS);
const targets = {
// 移动端:KTX2 + MeshOpt + 简化
mobile: { texture: "basisu", meshopt: true, simplify: 0.5 },
// VR:KTX2 + MeshOpt,不简化(VR 近距离观看)
vr: { texture: "basisu", meshopt: true, simplify: null },
// 桌面:WebP + MeshOpt,温和
desktop: { texture: "webp", meshopt: true, simplify: null },
};
const input = process.argv[2];
const doc = await io.read(input);
for (const [name, cfg] of Object.entries(targets)) {
const out = doc.clone(); // 每个目标独立副本
await optimize(out, {
textureCompression: cfg.texture,
meshCompression: cfg.meshopt ? "meshopt" : null,
simplify: cfg.simplify != null ? { ratio: cfg.simplify } : null,
});
await io.write(`model-${name}.glb`, out);
console.log(`✓ model-${name}.glb`);
}
node compress.mjs model.glb
# 输出 model-mobile.glb / model-vr.glb / model-desktop.glb
运行时按设备加载对应版本(用 UA 或 WebGL 能力探测区分),首屏体验最优。下一篇的端到端实战会把这套完整串起来。
一句话备忘
- 显存是瓶颈 → KTX2,没得选
- 下载是瓶颈、显存宽裕 → WebP/AVIF 也行
- 解码器体积敏感 → MeshOpt > 纯量化 > Draco
- 超大模型 → 才考虑 Draco
- 拿不准 →
gltf-transform optimize一键跑,错了再调
下一步
选型框架有了,最后一篇收官:从一个真实模型出发,走完 Blender 导出、纹理压缩、顶点压缩、引擎加载的全流程,并附上完整自动化脚本和系列速查表。