经纬志
首页 / 实战手册 / three.js 渲染

模型躺在地上,和地图方向对不上

先看结论

模型在建模软件里是站着的,导进 three.js 后躺倒;按地图点位摆放后,楼的朝向又整体偏了 90 度。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

模型沿 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)。