meshopt量化编码是移动端GPU显存带宽优化关键:先用meshopt_quantizeVertexFormat量化顶点(法线int10、位置int16+AABB归一化),再调meshopt_encodeVertexBuffer;索引需去重焊接后调meshopt_encodeIndexBuffer,解码必须用对应API并传入vcount及包围盒参数。

直接压顶点和索引,别只盯着 glTF 的 base64 或 gzip——移动端带宽瓶颈往往卡在 GPU 读取顶点缓冲区时的显存带宽,meshopt_encodeVertexBuffer 和 meshopt_encodeIndexBuffer 才是真正能砍掉 50%+ 传输量的关键。
先量化再编码,顺序错了就白压
顶点压缩必须分两步:先用 meshopt_quantizeVertexFormat 或手动量化(比如法线转 int10、位置转 half),再喂给 meshopt_encodeVertexBuffer。如果跳过量化直接编码,压缩率会暴跌——因为编码器依赖数据局部性,浮点数的微小差异会让熵编码失效。
- 位置建议量化到 16 位有符号整数(
int16_t),配合包围盒归一化:先算出 AABB,再把每个坐标映射到 [-32767, 32767] - 法线必须用带符号 10 位(
int10)或 8 位球面量化(meshopt_quantizeUnorm),别用 float 存 -
meshopt_encodeVertexBuffer输入必须是量化后的uint8_t*或uint16_t*,不是原始float*
索引编码必须保持三角形顺序
meshopt_encodeIndexBuffer 不是通用 LZ 压缩,它专为索引设计:允许每个三角形内顶点顺序旋转(如 [a,b,c] → [b,c,a]),但绝不打乱三角形之间的相对顺序。这是为了确保解码后仍能用 meshopt_optimizeVertexCache 重排缓存友好顺序。
- 输入索引缓冲必须已去重、已焊接(即调过
meshopt_generateVertexRemap),否则编码器会把重复顶点当成不同实体处理,压缩率归零 - 输出是变长字节流,解码时需传入原始顶点数
vcount,不能只靠长度推断 - 移动端解码务必用
meshopt_decodeIndexBuffer,别手写 memcpy —— 它内部有针对 ARM NEON 的向量化分支
移动端解码性能比压缩率更关键
Android 上解码耗时超过 1ms 就可能卡主线程,而 meshopt_decodeVertexBuffer 在中端芯片(如骁龙 7 Gen2)上实测约 0.3–0.6 ms / MB,前提是开启 -O2 -march=armv8-a+simd 编译。
立即学习“C++免费学习笔记(深入)”;
- 避免在主线程同步解码大网格;推荐预加载时用
std::async解码,或拆成 64KB 分块异步处理 - 解码后顶点数据不要立刻上传 GPU;先用
meshopt_optimizeVertexFetch洗一遍内存布局,减少后续 draw call 的 cache miss - 切记:
meshopt_decodeIndexBuffer输出的是 16 位或 32 位索引,根据硬件支持选——Adreno 偏好uint16_t,Mali-G78 对uint32_t更友好
最容易被忽略的是:量化后的顶点数据必须和包围盒元信息一起打包传给解码端,否则解码出来的坐标全是错的;这个包围盒不是可选字段,是量化逆变换的必要参数。


















