基于Vue3与Cesium开发的可视化控制面板,通过矩阵运算实时调节3DTiles模型的离地面高度、XYZ轴旋转角度及经纬度偏移,实现所见即所得的参数调整,解决了3DTiles无法直接修改位置与旋转属性的痛点。
Vue3+Cesium 3DTiles模型实时调节高度、旋转与经纬度偏移
时间:2026-06-12
浏览:651
文章目录
做 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 模型的姿态调节,底层都离不开矩阵运算。整体执行流程可以归纳为五步:
- 模型初始化加载时,获取模型原始中心点的经纬度、高度,作为后续调节的基准值;
- 监听控制面板参数变化,实时收集离地面高度、xyz 轴旋转角度、经纬度偏移数据;
- 基于 ENU(东-北-上)地球坐标系,分别生成模型平移矩阵和三轴独立旋转矩阵;
- 将多个矩阵合并运算,得到最终的变换矩阵;
- 把最终矩阵赋值给瓦片根节点的
_root.transform,模型状态立刻更新。
Demo 整体结构
整个页面拆为三大块,职责清晰,便于维护:
- HTML 结构:Cesium 渲染容器、功能按钮、参数控制面板;
- CSS 样式:全局布局、地球容器样式、右侧悬浮控制面板样式;
- Script 逻辑:Cesium 初始化、3DTiles 加载、矩阵运算、参数监听、辅助功能。
分步编码实现
编写 HTML 结构
页面基础布局使用 ElementPlus 的数字输入框双向绑定参数,参数变更时触发修改方法。所有输入框都支持正负数值,满足模型多角度、多方位调节需求。
复制代码
参数恢复
初始位置
设计模型
高度
X轴旋转
Y轴旋转
Z轴旋转
经度平移
纬度平移
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;
}
参数功能详细解析

模型说明: 文中 3D 城市模型来源于 Sketchfab 免费共享库,本人仅作技术演示使用。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。