webgl模型卡顿主因是未启用draco解码、lod距离计算错误及geometry重复引用;需显式引入dracoloader并setdracoloader,lod距离按相机空间欧氏距离设置且手动调用update,gltf加载后用setmeshoptimization或手动去重复用geometry。

WebGL模型加载卡顿,先看是不是没用 draco 解码
很多模型(尤其是 glTF)体积大、顶点多,直接加载会阻塞主线程,表现就是卡在 loading 或白屏几秒。不是网络慢,是 CPU 在拼命解压网格数据。
glTF 官方推荐用 DRACO 压缩几何体,压缩率常达 5–10 倍,但必须显式启用解码器——Three.js 默认不带,THREE.DRACOLoader 需单独引入并注册到 loader.setDRACOLoader。
- 漏掉
DRACOLoader实例化或没调用setDRACOLoader,模型就走原始解析路径,卡得毫无悬念 -
draco_decoder.js要从官方 repo 或 CDN 加载,路径错、版本不匹配(比如用 r128 的 loader 配 r148 的 decoder)会抛DRACOLoader: Decoder not available - 压缩只对
POSITION、NORMAL、TEXCOORD等属性生效,若模型用了自定义 attribute 且未标记为可压缩,解码会静默失败或回退
LOD 切换不生效?检查 distance 是不是按相机空间单位算的
WebGL 里 LOD 不是“远小近大”的直觉逻辑,而是严格按相机到物体中心的欧氏距离(单位:世界坐标系下的米/单位),和模型自身 scale、相机 fov、near/far 都有关。设成 10,结果模型在 50 米外还在渲染高模,很常见。
Three.js 的 LOD 对象靠 update 方法触发切换,但它不自动调用——你得在渲染循环里手动传入相机位置,否则永远停在 level 0。
立即学习“前端免费学习笔记(深入)”;
-
lod.addLevel( mesh, distance )中的distance是“切换阈值”,不是“显示范围”;比如addLevel( lowRes, 20 )意味着:距离 ≤ 20 用上一级,> 20 才切到这一级 - 多个 LOD 级别要按距离升序添加,否则
update会选错层级 - 若模型做了
scale缩放,距离阈值得同比例调整,否则判断失效(例如模型 scale 0.1,实际 200 米才等效原距离 20 米)
glTF 加载后内存暴涨,警惕 mesh.geometry.attributes 重复引用
同一个 glTF 文件里多个 mesh 共享 geometry(比如一堆相同椅子),Three.js 默认会为每个 mesh 创建独立的 BufferGeometry 实例,哪怕它们底层 buffer 完全一样。一个 50MB 的模型,可能瞬间吃掉 500MB 内存。
根本原因在于 glTF loader 的 parser 默认开启 clone 行为,防止修改影响其他实例——安全但奢侈。
- 用
gltfLoader.setMeshOptimization( true )(r148+)或手动 patchparser.assignFinalMaterial可复用 geometry - 更稳的方式是加载后遍历
scene.traverse,用geometry.id或geometry.attributes.position.array引用比对,主动去重 - 注意:去重后若需单独 transform 某个 mesh,得用
instancedMesh或Object3D包裹,不能直接改 shared geometry 的 matrix
移动端 WebGL 渲染掉帧,requestAnimationFrame 里别做模型解析
加载完 glTF 后立刻调 scene.add,看似没问题,但在低端 Android 或 iOS Safari 上,geometry 创建 + attribute upload 是同步 CPU 密集操作,一帧干不完就掉帧,用户感觉“卡一下”。尤其当模型含大量 mesh 或 animation 时。
真正该进 rAF 循环的只有渲染和动画更新,模型解析、材质创建、buffer 上传这些必须前置完成,或拆成微任务分帧。
- 用
Promise.all( meshes.map( m => m.material?.onBeforeCompile?.() ) )提前编译 shader,避免首帧卡 - 对超大模型,用
setTimeout或queueMicrotask把scene.add推到下一帧,给浏览器喘息时间 - iOS WebKit 有 texture upload 限制,单次上传 > 4MB 的
ArrayBuffer可能触发WebGL warning: INVALID_VALUE: texImage2D: width or height out of range,需提前 split geometry
LOD 和压缩不是开了就灵,关键在距离单位怎么算、decoder 怎么配、geometry 怎么复用——这些地方错一点,优化就全白搭。










