第10章 - 3D Tiles大规模数据
第10章:3D Tiles大规模数据
10.1 3D Tiles 概述
10.1.1 什么是 3D Tiles
3D Tiles 是 OGC 社区标准,专为 Web 端大规模三维地理数据的流式传输和渲染而设计。它是 CesiumJS 处理海量三维数据的核心技术。
┌─────────────────────────────────────────────────────────────────┐
│ 3D Tiles 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ tileset.json (根节点) │
│ ├── asset - 资产信息(版本等) │
│ ├── geometricError - 几何误差(LOD控制) │
│ └── root - 根瓦片 │
│ ├── boundingVolume - 包围体 │
│ ├── content - 瓦片内容(.b3dm/.i3dm/.pnts等) │
│ └── children - 子瓦片 │
│ │
│ 内容类型: │
│ ├── .b3dm - Batched 3D Model(批量三维模型) │
│ ├── .i3dm - Instanced 3D Model(实例化模型) │
│ ├── .pnts - Point Cloud(点云) │
│ ├── .cmpt - Composite(复合瓦片) │
│ └── .glb - glTF 模型(3D Tiles 1.1) │
│ │
└─────────────────────────────────────────────────────────────────┘
10.1.2 3D Tiles 特点
| 特点 | 描述 |
|---|---|
| LOD | 层次细节,根据距离动态加载不同精度 |
| 流式加载 | 按需加载,无需一次性加载全部数据 |
| 空间索引 | 基于包围体的高效空间查询 |
| 属性数据 | 支持 Batch Table 存储属性 |
| 样式化 | 支持条件样式渲染 |
10.2 加载 3D Tiles
10.2.1 基本加载
// 从 URL 加载
const tileset = await Cesium.Cesium3DTileset.fromUrl(
'https://your-server/tileset/tileset.json'
);
viewer.scene.primitives.add(tileset);
// 定位到 tileset
viewer.zoomTo(tileset);
// 从 Cesium ion 加载
const ionTileset = await Cesium.Cesium3DTileset.fromIonAssetId(75343);
viewer.scene.primitives.add(ionTileset);
viewer.zoomTo(ionTileset);
10.2.2 完整配置
const tileset = await Cesium.Cesium3DTileset.fromUrl(
'https://your-server/tileset/tileset.json',
{
// ===== 显示设置 =====
show: true,
// ===== LOD 设置 =====
maximumScreenSpaceError: 16, // 最大屏幕空间误差(越小越精细)
maximumMemoryUsage: 512, // 最大内存使用(MB)
// ===== 加载策略 =====
skipLevelOfDetail: true, // 跳过 LOD 级别
baseScreenSpaceError: 1024, // 基础屏幕空间误差
skipScreenSpaceErrorFactor: 16, // 跳过因子
skipLevels: 1, // 跳过级别数
immediatelyLoadDesiredLevelOfDetail: false, // 立即加载目标 LOD
loadSiblings: false, // 加载兄弟瓦片
// ===== 剔除设置 =====
cullWithChildrenBounds: true, // 使用子包围体剔除
cullRequestsWhileMoving: true, // 移动时剔除请求
cullRequestsWhileMovingMultiplier: 60.0,
// ===== 预加载 =====
preloadWhenHidden: false, // 隐藏时预加载
preloadFlightDestinations: true, // 预加载飞行目的地
preferLeaves: false, // 优先加载叶子节点
// ===== 动态调整 =====
dynamicScreenSpaceError: false, // 动态屏幕空间误差
dynamicScreenSpaceErrorDensity: 0.00278,
dynamicScreenSpaceErrorFactor: 4.0,
dynamicScreenSpaceErrorHeightFalloff: 0.25,
// ===== 模型矩阵 =====
modelMatrix: Cesium.Matrix4.IDENTITY,
// ===== 阴影 =====
shadows: Cesium.ShadowMode.ENABLED,
// ===== 拾取 =====
enablePick: true,
// ===== 调试 =====
debugShowBoundingVolume: false,
debugShowContentBoundingVolume: false,
debugShowViewerRequestVolume: false,
debugShowGeometricError: false,
debugShowRenderingStatistics: false,
debugShowMemoryUsage: false
}
);
viewer.scene.primitives.add(tileset);
10.2.3 位置调整
// 调整 tileset 位置
const tileset = await Cesium.Cesium3DTileset.fromUrl(url);
// 等待加载完成
tileset.readyPromise.then(function(tileset) {
// 获取当前位置
const boundingSphere = tileset.boundingSphere;
const cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
// 设置新位置
const newLongitude = Cesium.Math.toRadians(116.4);
const newLatitude = Cesium.Math.toRadians(39.9);
const newHeight = 0;
// 计算偏移
const surface = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
0
);
const offset = Cesium.Cartesian3.fromRadians(
newLongitude,
newLatitude,
newHeight
);
const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
});
// 使用辅助函数
function moveTileset(tileset, longitude, latitude, height = 0) {
const cartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
const surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0);
const offset = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
}
10.3 3D Tiles 样式
10.3.1 基本样式
// 单色样式
tileset.style = new Cesium.Cesium3DTileStyle({
color: 'color("red")'
});
// 透明度
tileset.style = new Cesium.Cesium3DTileStyle({
color: 'color("blue", 0.5)'
});
// 显示/隐藏
tileset.style = new Cesium.Cesium3DTileStyle({
show: true,
color: 'color("white")'
});
10.3.2 条件样式
// 基于属性的条件样式
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
['${height} >= 100', 'color("red")'],
['${height} >= 50', 'color("orange")'],
['${height} >= 20', 'color("yellow")'],
['true', 'color("white")']
]
}
});
// 多条件组合
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
['${type} === "residential" && ${height} > 50', 'color("blue")'],
['${type} === "commercial"', 'color("red")'],
['${type} === "industrial"', 'color("gray")'],
['true', 'color("white")']
]
},
show: '${height} > 0'
});
// 渐变色
tileset.style = new Cesium.Cesium3DTileStyle({
color: 'color("red") * vec4(1.0, ${height}/100.0, 0.0, 1.0)'
});
// HSL 颜色
tileset.style = new Cesium.Cesium3DTileStyle({
color: 'hsl(${height}/200.0, 1.0, 0.5)'
});
// RGB 混合
tileset.style = new Cesium.Cesium3DTileStyle({
color: 'rgb(${height}*2, 100, 200)'
});
10.3.3 点云样式
// 点云基本样式
tileset.style = new Cesium.Cesium3DTileStyle({
pointSize: 5,
color: 'color("cyan")'
});
// 基于属性的点大小
tileset.style = new Cesium.Cesium3DTileStyle({
pointSize: '${intensity} / 10',
color: {
conditions: [
['${classification} === 2', 'color("brown")'], // 地面
['${classification} === 6', 'color("green")'], // 建筑
['${classification} === 9', 'color("blue")'], // 水体
['true', 'color("white")']
]
}
});
// 基于高程的渐变
tileset.style = new Cesium.Cesium3DTileStyle({
pointSize: 3,
color: 'hsv(${POSITION}[2] / 100.0, 1.0, 1.0)'
});
10.4 3D Tiles 交互
10.4.1 拾取与属性查询
// 点击拾取
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(function(click) {
const pickedObject = viewer.scene.pick(click.position);
if (Cesium.defined(pickedObject) && pickedObject.primitive instanceof Cesium.Cesium3DTileset) {
console.log('选中 3D Tiles');
// 获取要素属性
if (Cesium.defined(pickedObject.getProperty)) {
const height = pickedObject.getProperty('height');
const name = pickedObject.getProperty('name');
console.log('高度:', height);
console.log('名称:', name);
}
// 获取所有属性名
if (Cesium.defined(pickedObject.getPropertyIds)) {
const propertyIds = pickedObject.getPropertyIds();
console.log('属性列表:', propertyIds);
propertyIds.forEach(id => {
console.log(`${id}:`, pickedObject.getProperty(id));
});
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
10.4.2 高亮选中
// 高亮选中的要素
let highlighted = {
feature: undefined,
originalColor: new Cesium.Color()
};
handler.setInputAction(function(movement) {
// 重置之前的高亮
if (Cesium.defined(highlighted.feature)) {
highlighted.feature.color = highlighted.originalColor;
highlighted.feature = undefined;
}
// 拾取新要素
const pickedObject = viewer.scene.pick(movement.endPosition);
if (Cesium.defined(pickedObject) && pickedObject.primitive instanceof Cesium.Cesium3DTileset) {
highlighted.feature = pickedObject;
Cesium.Color.clone(pickedObject.color, highlighted.originalColor);
pickedObject.color = Cesium.Color.YELLOW;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
10.5 性能优化
10.5.1 LOD 优化
// 根据设备性能调整
function optimizeForDevice(tileset) {
// 检测设备性能
const isLowEnd = !viewer.scene.context.webgl2;
if (isLowEnd) {
tileset.maximumScreenSpaceError = 32; // 降低精度
tileset.maximumMemoryUsage = 256; // 降低内存
tileset.skipLevelOfDetail = true;
tileset.skipLevels = 2;
} else {
tileset.maximumScreenSpaceError = 8;
tileset.maximumMemoryUsage = 1024;
tileset.skipLevelOfDetail = false;
}
}
// 动态调整(根据帧率)
let lastFrameTime = performance.now();
viewer.scene.postRender.addEventListener(function() {
const currentTime = performance.now();
const frameTime = currentTime - lastFrameTime;
lastFrameTime = currentTime;
const fps = 1000 / frameTime;
if (fps < 30) {
tileset.maximumScreenSpaceError = Math.min(tileset.maximumScreenSpaceError * 1.1, 64);
} else if (fps > 55) {
tileset.maximumScreenSpaceError = Math.max(tileset.maximumScreenSpaceError * 0.95, 4);
}
});
10.5.2 内存管理
// 监控内存使用
tileset.tileLoad.addEventListener(function(tile) {
console.log('瓦片加载:', tile.content.url);
});
tileset.tileUnload.addEventListener(function(tile) {
console.log('瓦片卸载:', tile.content.url);
});
tileset.tileFailed.addEventListener(function(error) {
console.error('瓦片加载失败:', error);
});
// 获取统计信息
viewer.scene.postRender.addEventListener(function() {
const stats = tileset._statistics;
console.log('加载瓦片数:', stats.numberOfLoadedTiles);
console.log('内存使用(MB):', stats.texturesByteLength / 1024 / 1024);
});
10.6 3D Tiles 调试
// 启用调试显示
tileset.debugShowBoundingVolume = true;
tileset.debugShowContentBoundingVolume = true;
tileset.debugShowViewerRequestVolume = true;
tileset.debugShowGeometricError = true;
tileset.debugShowRenderingStatistics = true;
tileset.debugShowMemoryUsage = true;
tileset.debugShowUrl = true;
// 使用 Inspector
viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);
// 显示帧率
viewer.scene.debugShowFramesPerSecond = true;
10.7 本章小结
本章详细介绍了 3D Tiles:
- 概念:OGC 标准、架构设计、内容类型
- 加载:URL 加载、Ion 加载、位置调整
- 样式:单色、条件样式、点云样式
- 交互:拾取、属性查询、高亮
- 性能:LOD 优化、内存管理
- 调试:调试选项、Inspector
在下一章中,我们将详细介绍数据格式与数据源。
10.8 思考与练习
- 加载并展示城市建筑 3D Tiles 数据。
- 实现基于建筑高度的渐变色样式。
- 开发点击建筑显示属性信息的功能。
- 优化大规模 3D Tiles 的加载性能。
- 实现 3D Tiles 的动态过滤(显示/隐藏特定类型)。

浙公网安备 33010602011771号