Vue3+Cesium 3DTiles模型实时调节高度、旋转与经纬度偏移

作者:技术拾光时间:2026-06-12 08:30:01浏览:652

基于Vue3与Cesium开发的可视化控制面板,通过矩阵运算实时调节3DTiles模型的离地面高度、XYZ轴旋转角度及经纬度偏移,实现所见即所得的参数调整,解决了3DTiles无法直接修改位置与旋转属性的痛点。

做 Cesium 开发的朋友都知道,一碰到数字孪生、古建筑展示、三维地图这类项目,3DTiles 瓦片模型几乎是绕不开的格式。相比普通 glTF/GLB,3DTiles 对超大场景、高精度古建筑、城市级园区都有天然适配,也是现在三维 GIS 项目的主流选择。

但绝大多数开发者在实际调试时,都会卡在一个点上:

普通模型(gltf/glb)在 Cesium 里可以很方便地改位置、调角度,而 3DTiles 瓦片模型却没有 position / rotation 这样的属性暴露出来,直接修改位置与旋转参数这条路根本走不通。

针对这个痛点,这篇教程会带大家从零到一搭建一套可视化控制面板。面板上的参数可以实时调节,模型离地面高度、绕 XYZ 三轴的旋转角度、经纬度细微偏移,全部所见即所得,调完立刻生效,省去反复刷新页面的痛苦。

演示地址:3d.xiazhi.tech/3d-demos/#/…

最终实现效果

本案例基于 Vue3 + ElementPlus + Cesium 开发,右侧独立控制面板集成了 6 项核心调节参数,外加两个辅助功能按钮:

  • 模型离地面高度:正数上浮,负数下沉,支持正负数值
  • X 轴旋转:控制模型前后俯仰,矫正前倾或后仰
  • Y 轴旋转:控制模型左右倾斜,修复侧向歪斜
  • Z 轴旋转:水平面旋转,调整模型朝向
  • 经度偏移:东西方向精细化微调
  • 纬度偏移:南北方向精细化微调
  • 辅助功能:一键重置所有参数、相机复位至模型最佳观测视角

矩阵变换核心原理

Cesium 中所有 3DTiles 模型的姿态调节,底层都离不开矩阵运算。整体执行流程可以归纳为五步:

  1. 模型初始化加载时,获取模型原始中心点的经纬度、高度,作为后续调节的基准值;
  2. 监听控制面板参数变化,实时收集离地面高度、xyz 轴旋转角度、经纬度偏移数据;
  3. 基于 ENU(东-北-上)地球坐标系,分别生成模型平移矩阵和三轴独立旋转矩阵;
  4. 将多个矩阵合并运算,得到最终的变换矩阵;
  5. 把最终矩阵赋值给瓦片根节点的 _root.transform,模型状态立刻更新。

Demo 整体结构

整个页面拆为三大块,职责清晰,便于维护:

  1. HTML 结构:Cesium 渲染容器、功能按钮、参数控制面板;
  2. CSS 样式:全局布局、地球容器样式、右侧悬浮控制面板样式;
  3. Script 逻辑:Cesium 初始化、3DTiles 加载、矩阵运算、参数监听、辅助功能。

分步编码实现

编写 HTML 结构

页面基础布局使用 ElementPlus 的数字输入框双向绑定参数,参数变更时触发修改方法。所有输入框都支持正负数值,满足模型多角度、多方位调节需求。

复制代码

Script 逻辑实现(逐模块讲解)

1. 初始化响应式变量

定义双向绑定的参数变量和全局实例变量,用于存储模型状态与 Cesium 实例。

复制代码import { onMounted, nextTick, ref, onUnmounted } from 'vue';
import { token } from '../../utils/common.js';
import { ElMessage } from 'element-plus';

let heightVal = ref(0);
let rxVal = ref(0);
let ryVal = ref(0);
let rzVal = ref(0);
let tLon = ref(0);
let tLat = ref(0);
let params = ref({});

let longitude = ref(0);
let latitude = ref(0);

2. 初始化 Cesium 场景

初始化 Viewer 实例,关闭冗余控件、开启高性能渲染,限制默认可视范围为中国区域,减少资源占用。

复制代码onMounted(() => {
  nextTick(() => {
    initMap();
  });
});

const initMap = () => {
    Cesium.Ion.defaultAccessToken = token;
    Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(89.5, 20.4, 110.4, 61.2);
    window.viewer = new Cesium.Viewer('earth', {
      animation: false,
      timeline: false,
      infoBox: false,
      geocoder: false,
      homeButton: false,
      sceneModePicker: false,
      baseLayerPicker: false,
      na vigationHelpButton: false,
      fullscreenButton: false,
      selectionIndicator: false,
      shouldAnimate: false,
      contextOptions: {
        webgl: {
          powerPreference: "high-performance",
          preserveDrawingBuffer: false
        }
      }
    });
    let utc = Cesium.JulianDate.fromDate(new Date('2026/05/02 15:00:00'));
    addModel();
    flyTo();
};

3. 加载 3DTiles 模型并获取基准坐标

加载本案例中的古建筑 3DTiles 瓦片,获取模型包围球中心点经纬度,保存为基准坐标,后续所有偏移调节都基于该坐标计算。

复制代码const addModel = async () => {
    try {
        const tileset = await Cesium.Cesium3DTileset.fromUrl(
            'YOUR_3D_TILES_URL / tileset.json',
            {
                maximumScreenSpaceError: 48,
                maximumSimultaneousTileLoads: 16,
                preloadAncestors: false,
                preloadSiblings: true,
                maximumMemoryUsage: 512,
                skipLevelOfDetail: true,
                baseScreenSpaceError: 1024
            }
        );
        window.tileset = window.viewer.scene.primitives.add(tileset);
        const cartographic = Cesium.Cartographic.fromCartesian(
          window.tileset.boundingSphere.center
        );
        longitude.value = Cesium.Math.toDegrees(cartographic.longitude);
        latitude.value = Cesium.Math.toDegrees(cartographic.latitude);
        params.value = {
          tx: longitude.value,
          ty: latitude.value,
          tz: cartographic.height,
          rx: 0,
          ry: 0,
          rz: 0
        };
        changeModel();
    } catch (err) {
        console.error('3D Tiles 加载失败', err);
    }
};

4. 核心:矩阵合成方法

根据当前面板参数,分别生成 xyz 旋转矩阵,结合经纬度、离地面高度合成最终变换矩阵。这是整个案例最核心的部分。

复制代码    const update3dtilesMaxtrix = () => {
        let mx = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(params.value.rx));
        let my = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(params.value.ry));
        let mz = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(params.value.rz));
        let rotationX = Cesium.Matrix4.fromRotationTranslation(mx);
        let rotationY = Cesium.Matrix4.fromRotationTranslation(my);
        let rotationZ = Cesium.Matrix4.fromRotationTranslation(mz);
        let position = Cesium.Cartesian3.fromDegrees(params.value.tx, params.value.ty, heightVal.value);
        let m = Cesium.Transforms.eastNorthUpToFixedFrame(position);
        Cesium.Matrix4.multiply(m, rotationX, m);
        Cesium.Matrix4.multiply(m, rotationY, m);
        Cesium.Matrix4.multiply(m, rotationZ, m);
        return m;
    };

5. 参数监听与模型实时更新

监听输入框参数变化,更新偏移、旋转参数,调用矩阵方法,实时更新模型姿态。经纬度偏移增加了阻尼系数,避免幅度过大导致模型飞走。

复制代码const changeModel = () => {
    params.value.rx = rxVal.value;
    params.value.ry = ryVal.value;
    params.value.rz = rzVal.value;
    params.value.tx = longitude.value + tLon.value / 500;
    params.value.ty = latitude.value + tLat.value / 500;
    window.tileset._root.transform = update3dtilesMaxtrix();
};

const changeHeight = () => {
   const cartographic = Cesium.Cartographic.fromCartesian(
     window.tileset.boundingSphere.center
   );
   const surface = Cesium.Cartesian3.fromRadians(
     cartographic.longitude,
     cartographic.latitude
   );
   const offset = Cesium.Cartesian3.fromRadians(
     cartographic.longitude,
     cartographic.latitude,
     params.value.tz
   );
   const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
   window.tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
   update3dtilesMaxtrix();
};

6. 辅助功能方法

参数重置和相机复位两个辅助方法,方便快速调试。

复制代码const restart = () => {
  heightVal.value = 0;
  rxVal.value = 0;
  ryVal.value = 0;
  rzVal.value = 0
  tLon.value = 0
  tLat.value = 0
  changeModel();
};

const flyTo = () => {
  window.viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(121.43908371916447, 31.349189256570035, 2.5532527089816814),
    orientation: {
      heading: Cesium.Math.toRadians(25.51463348115896),
      pitch: Cesium.Math.toRadians(6.338469775354732),
      roll: Cesium.Math.toRadians(359.99999787825675)
    },
    duration: 6
  });
};

CSS 样式代码

复制代码* {
    margin: 0;
    padding: 0;
}
.main {
    width: 100%;
    height: 100vh;
    position: relative;
}
.content {
    width: 100%;
    height: 100%;
    position: relative;
    z-index: 1;
}
.map-control {
    width: 300px;
    height: calc(100vh - 60px);
    position: absolute;
    right: 20px;
    top: 20px;
    z-index: 2;
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: start;
    box-sizing: border-box;
}
.map-btn {
    width: 100%;
    display: flex;
    justify-content: end;
    align-items: center;
}
.model-black {
    width: 100%;
    flex: 1;
    min-height: 0;
    margin-top: 30px;
    background-color: rgba(31, 31, 31, 0.8);
    box-sizing: border-box;
    padding: 10px;
}
.model-tt {
    width: 100%;
    font-size: 18px;
    color: #FFF;
    height: 50px;
    line-height: 50px;
    box-sizing: border-box;
    padding-left: 14px;
}
.model-row-tt {
    width: 100%;
    color: #FFF;
    margin-top: 20px;
    font-size: 16px;
    padding-left: 14px;
    box-sizing: border-box;
}
.model-number-input {
    margin-left: 14px;
    margin-top: 14px;
}

参数功能详细解析

Vue3+Cesium 3DTiles模型实时调节高度、旋转与经纬度偏移

模型说明: 文中 3D 城市模型来源于 Sketchfab 免费共享库,本人仅作技术演示使用。