模型加载后只有指甲盖大小
一栋 60 米高的楼,导入后和 1 米的地面网格差不多大;换一个模型,又大得把相机包在里面。
现象
两个模型来自不同软件,分别导成 glTF。A 模型几乎看不见, B 模型把整个场景撑满。用同一套相机参数,无法同时看清两者。 打印包围盒可以看到尺寸差异在 100 倍量级:
// A:0.62 1.87 0.55 (单位为米时,只有 1.8 米高)
// B:62000 187000 55000 (单位是毫米)
原因
glTF 规范只规定单位是米,但导出工具不一定遵守。 Revit、3ds Max 常见导出单位为毫米或厘米; SketchUp 默认按英寸建模再转米;Cesium 导出的 3D Tiles 中也常有厘米单位。 模型文件本身不带“单位倍数”,three.js 只能原样读取顶点坐标。
另一个干扰项是父节点的 scale。直接对已经带缩放的子对象再设
scale,最终尺寸是两者相乘,容易出现 100 倍偏差。
排查过程
- 读包围盒尺寸,与设计图上的真实尺寸对比,算出倍率。
- 打印模型根节点的
scale,确认是否为 1。 - 检查相机
near/far,模型过大时可能整个在near之外或far之外而被裁掉。
var box = new THREE.Box3().setFromObject(gltf.scene);
var size = box.getSize(new THREE.Vector3());
console.log('包围盒尺寸', size);
console.log('根节点缩放', gltf.scene.scale.toArray());
解决方案
已知单位是厘米或毫米时,直接给一个固定倍率。把它作用在最外层容器上, 避免和模型自身缩放相乘:
var unitScale = 0.01; // 厘米转米;毫米用 0.001
var holder = new THREE.Group();
holder.add(gltf.scene);
holder.scale.setScalar(unitScale);
scene.add(holder);
不知道单位时,用目标尺寸自动归一。适合批量导入、尺寸不一的资产:
function fitToHeight(object3d, targetHeight) {
var box = new THREE.Box3().setFromObject(object3d);
var size = box.getSize(new THREE.Vector3());
var s = targetHeight / size.y;
object3d.scale.setScalar(s);
// 缩放后重新计算,把底面移到 y = 0
box.setFromObject(object3d);
object3d.position.y -= box.min.y;
return s;
}
fitToHeight(gltf.scene, 60); // 统一成 60 米高
模型过大导致相机看不见时,先用包围盒把相机摆到合适距离, 再核对真实尺寸:
var box = new THREE.Box3().setFromObject(gltf.scene);
var sphere = box.getBoundingSphere(new THREE.Sphere());
var dist = sphere.radius / Math.sin(camera.fov * Math.PI / 360);
camera.position.copy(sphere.center).add(new THREE.Vector3(dist * 1.5, dist, dist * 1.5));
camera.lookAt(sphere.center);
验证
导入后模型高度与设计值一致,误差小于 1%。 同一个 1 米网格放在模型旁边,比例肉眼可判断正确。 相机默认视角下模型完整出现在画面中央,而不是只有一角。
先确认再缩放:在建模软件里量一个已知尺寸的构件,
与包围盒数值相除得到倍率,比反复试 0.01、0.001 更快也更可靠。
经纬志(2026)。《模型加载后只有指甲盖大小》。https://www.jingweizhi.com/cookbook/three-gltf-scale.html(访问日期 2026-11-03)。