模型躺在地上,和地图方向对不上
模型在建模软件里是站着的,导进 three.js 后躺倒;按地图点位摆放后,楼的朝向又整体偏了 90 度。
现象
模型沿 Z 轴向上建模(多数 GIS 与 CAD 软件的习惯),导入后沿 Y 轴“躺平”。
给对象设置 rotation.y 之后,模型绕竖直轴旋转的直觉失效,
旋转的是另一个轴。位置坐标直接代入经纬度后,模型出现在画面外。
原因
three.js 使用 Y 轴向上的右手坐标系,glTF 规范也规定 Y 轴向上。 GIS 的东北天(ENU)局部坐标系是 Z 轴向上、X 轴向东、Y 轴向北。 两套坐标系差一个绕 X 轴的 -90° 旋转。
另外,three.js 的单位没有物理含义,1 个单位等于多少米取决于项目约定; 把米制的经纬度直接当 x/y 用,数值量级完全不对。
排查过程
用包围盒判断哪个轴是高度轴。正常建筑的高度方向尺寸应明显大于另外两轴:
var box = new THREE.Box3().setFromObject(model);
var size = box.getSize(new THREE.Vector3());
console.log('尺寸', size.x, size.y, size.z);
// 12.4 3.1 18.6 → Y 轴只有 3.1,说明高度在 Z 轴
解决方案
包一层容器做坐标轴转换,不要直接改模型自身的旋转, 这样后续业务代码仍然可以按模型本地坐标操作:
var wrapper = new THREE.Group();
wrapper.add(model);
wrapper.rotation.x = -Math.PI / 2; // Z-up 转 Y-up
wrapper.position.set(x, 0, z);
scene.add(wrapper);
需要按地图方位摆放时,绕竖直轴的旋转应作用在 wrapper 的 Y 轴上, 而不是模型的 Z 轴:
// 地图方位角(正北顺时针,单位度)转 three.js 的 Y 轴旋转
var heading = 35;
wrapper.rotation.y = -THREE.MathUtils.degToRad(heading);
经纬度先投影到局部米制平面,再按比例缩放,不要直接使用角度值:
var origin = { lng: 116.391, lat: 39.907 };
var metersPerDegLng = 111320 * Math.cos(origin.lat * Math.PI / 180);
var metersPerDegLat = 110574;
function toLocal(lng, lat) {
return {
x: (lng - origin.lng) * metersPerDegLng,
z: -(lat - origin.lat) * metersPerDegLat // 北向对应 -Z
};
}
var p = toLocal(116.395, 39.909);
wrapper.position.set(p.x, 0, p.z);
验证
加载后模型的“上”方向与场景 Y 轴一致,包围盒的 Y 尺寸等于建筑高度。 把方位角设为 0 与 90,模型分别朝向正北与正东,与底图上的指北针一致。 已知两点间的模型距离与实地距离误差在 1% 以内。
不要混用两套约定:项目里要么全部 Y-up,要么全部 Z-up 并在入口处转换。 一半模型转了、一半没转,会出现“有的站着有的躺着”,排查成本远高于统一规范。
经纬志(2026)。《模型躺在地上,和地图方向对不上》。https://www.jingweizhi.com/cookbook/three-y-up-vs-map.html(访问日期 2026-11-03)。