从事 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 免费共享资源库,本文仅用作技术演示。
