Vue.js - 集成Cesium构建三维可视化场景教程8(上传模型制作3D Tiles)
作者:hangge | 2026-07-14 08:48
Cesium 作为面向海量三维地理数据的可视化引擎,其核心优势之一就在于 3D Tiles 这一面向流式加载的开放规范。通过将原始三维模型切片为 3D Tiles,不仅可以显著提升大规模场景的加载效率,还能根据视距动态加载不同层级的细节,为 Web 端三维应用提供良好的性能与体验。
前文我演示了如何加载使用 Cesium ion 上提供的各种 3D Tiles 数据(点击查看)。但在实际项目中,我们常常需要将自己的摄影测量 / 建模 / 点云数据变成可以在 Cesium 中流式加载的 3D Tiles,下面演示如何上传模型到 Cesium ion 生成 3D Tiles。









(3)下面是完整的使用样例(这里我关闭了底图和地形的显示,只展示 3D Tiles):
八、上传模型到 Cesium ion 生成 3D Tiles
1,制作切片(3D Tiles)
(1)假设我们有一个科技馆的 glb 模型,需要制作成 3D Tiles 并在 Cesium 中进行加载显示。

(2)登录 Cesium ion,进入“My Assets” 页面后点击“Add data”按钮。

(3)接着选择“Add files...”,然后在弹出框中选择我们需要上传的模型数据,包括:
- 倾斜摄影(OSGB / OBJ / FBX)
- BIM(IFC)
- GLB / Gltf
- 点云(LAS / LAZ)

(4)文件选择完毕后出现参数配置页面,类型选择“3D Model(tile as 3D Tiles)”后点击“Upload”按钮。
- 如果 obj + mtl 格式的模型数据,我们也可以直接将整个文件夹拖进来。
- 可以看到文件夹内所有的文件都添加了进来。同样将类型选择“3D Model(tile as 3D Tiles)”后点击“Upload”按钮。

(5)随后模型文件就会开始上传,并且自动将其转换为 3D Tiles。

2,调整切片角度
(1)切片生成完毕后我们可能会发现预览框里面的模型是立起来的,需要调整角度。点击“Adjust Tileset Location”按钮

(2)然后在编辑页面中点击“Next”按钮。

(3)接着调整下旋转角度是模型平躺下来,然后点击“Save”按钮进行保存。
提示:如果需要让模型与平面地图进行融合,可以再设置一下经纬度和海拔高度。

(4)保存后在预览框可以看到模型角度已经摆正了。

3,加载切片
(1)当切片制作完毕后,我们在资源表格中找到该资源的 Asset ID。

(2)最后我们代码中通过 Asset ID 加载进来即可。
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(4396993) viewer.scene.primitives.add(tileset) viewer.zoomTo(tileset)
<template>
<div class="layer-container">
<div ref="cesiumContainer" class="cesium-container"></div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner"></div>
<p>加载三维场景中...</p>
</div>
</div>
</template>
<script>
// 引入 Cesium 核心类
import * as Cesium from "cesium";
export default {
name: 'CesiumViewer',
data() {
return {
viewer: null,
loading: true,
terrainEnabled: true,
}
},
mounted() {
// 确保 Cesium 已加载
if (typeof Cesium === 'undefined') {
console.error('Cesium 未加载,请检查配置')
this.loading = false
return
}
// 初始化 Cesium 场景
this.initCesium()
},
beforeDestroy() {
// 组件销毁时清理资源
this.destroyCesium()
},
methods: {
// 初始化 Cesium
async initCesium() {
try {
// 配置 Cesium 访问令牌
Cesium.Ion.defaultAccessToken = '你的token'
// 创建 Viewer 实例
this.viewer = new Cesium.Viewer(this.$refs.cesiumContainer, {
animation: false, // 是否显示动画控件
baseLayerPicker: false, // 是否显示图层选择控件
fullscreenButton: false, // 是否显示全屏按钮
geocoder: false, // 是否显示地理编码搜索控件
homeButton: false, // 是否显示Home按钮
infoBox: false, // 是否显示信息框
sceneModePicker: false, // 是否显示3D/2D选择器
selectionIndicator: false, // 是否显示选取指示器组件
timeline: false, // 是否显示时间轴
navigationHelpButton: false, // 是否显示帮助按钮
scene3DOnly: true, // 每个几何实例仅以3D渲染以节省GPU内存
shouldAnimate: true, // 自动播放动画
imageryProvider: false, // 不加载影像
terrainProvider: new Cesium.EllipsoidTerrainProvider(), //数学椭球不请求地形数据
})
const scene = this.viewer.scene
// 彻底关闭天空盒(有些版本必须用 undefined)
scene.skyBox = undefined
// 彻底关闭大气
scene.skyAtmosphere = undefined
// 关闭雾效(否则仍可能偏蓝)
scene.fog.enabled = false
// 如果 globe 存在,也关掉
scene.globe && (scene.globe.show = false)
// 设置背景色(最后设置)
scene.backgroundColor = Cesium.Color.fromCssColorString('#EDF2FA')
// 强制重新计算画布尺寸
this.viewer.canvas.width = this.viewer.container.clientWidth;
this.viewer.canvas.height = this.viewer.container.clientHeight;
this.viewer.resize(); // 核心方法:告知 Cesium 容器大小已变
// 加载自定义的切片数据
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(4396993)
this.viewer.scene.primitives.add(tileset)
this.viewer.zoomTo(tileset)
// 加载完成
this.loading = false
console.log('Cesium 场景初始化完成')
} catch (error) {
console.error('初始化 Cesium 失败:', error)
this.loading = false
}
},
// 设置初始视图
setInitialView() {
// 设置初始位置(例如:北京天安门)
const initialPosition = Cesium.Cartesian3.fromDegrees(116.3974, 39.9093, 1500)
this.viewer.camera.setView({
destination: initialPosition,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0
}
})
},
// 销毁 Cesium 资源
destroyCesium() {
if (this.viewer) {
this.viewer.destroy()
this.viewer = null
}
}
}
}
</script>
<style scoped>
.layer-container {
position: relative;
width: 100%;
height: 100%;
}
.cesium-container {
width: 100%;
height: 100%;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 1000;
color: white;
}
.loading-spinner {
border: 4px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top: 4px solid #42b983;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin-bottom: 15px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
(4)运行效果如下:

全部评论(0)