原文链接及内容

实现效果如下视频所示:

  1. 关于不同格式的3D Tiles数据介绍可以参考岭南灯火总结的一篇博客:https://www.cnblogs.com/onsummer/p/13128682.html
  2. 也可以翻阅我翻译的官方博客:

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
<template>
<CesiumMap @viewerCreated="viewerCreated" />
<!-- 参数调整面板 -->
<div class="panel">
<el-form :model="form" label-suffix=":" label-width="80px">
<el-form-item label="模型">
<el-select
v-model="form.model"
placeholder="请选择模型"
@change="handleChangeModel">
<el-option
v-for="item in models"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="启用阴影">
<el-switch
v-model="form.shadowsEnabled"
inline-prompt
active-text="开"
inactive-text="关"
@change="handleShadowsChange" />
</el-form-item>
<div style="text-align: right">
<el-button
size="small"
type="primary"
@click="handleRecovery"
:disabled="!hiddenFeatures.length">
恢复显示隐藏要素
</el-button>
</div>
</el-form>
</div>
</template>

<script setup>
/**
* 关于3dtiles涉及的瓦片数据类型请参考博客:
*/

//#region --------------------- 定义变量----------------
let viewer, scene, handler, tileset, inspectorViewModel;
const hiddenFeatures = ref([]);
const allProps = new Set();

let count = 0;

const form = reactive({
model: "Tileset",
shadowsEnabled: true,
});

const inspector = reactive({
pickTilesetActive: false,
pickActive: true,
colorize: false,
wireframe: false,
boundingVolumes: false,
contentVolumes: false,
requestVolumes: false,
pointCloudShading: false,
geometricErrorScale: 1,
maxAttenuation: 0,
baseResolution: 0,
eyeDomeLighting: true,
edlStrength: 1,
edlRadius: 1,
freezeFrame: false,
dynamicScreenSpaceError: true,
maxScreenSpaceError: 16,
screenSpaceErrorDensity: 0.0002,
screenSpaceErrorFactor: 24,
});

/**
* @type {Array} 要加载的模型列表
*/
const models = [
{ value: "Tileset", label: "Tileset的模型" },
/**
* Translucent:这是说模型具有透明或半透明效果的材质。
*/
{ value: "Translucent", label: "半透明样式的模型" },
{ value: "Translucent/Opaque", label: "半透明/不透明混合样式的模型" },
{ value: "Multi-color", label: "用多种颜色可视化模型" },
{ value: "Request Volume", label: "加载tileset时发起的HTTP请求数" },
{ value: "Batched", label: "Batched模型(.B3DM)" },
/**
* I3DM 是 3D Tiles 的一种格式,用于渲染多个实例化的对象,例如树木、路灯等。
*/
{ value: "Instanced", label: "Instanced模型(.I3DM)" },
{ value: "Instanced/Orientation", label: "不同朝向(方向)的实例模型" },
/**
* 在 Cesium 的 3D Tiles 规范中,Composite Tile 是一种特殊的瓦片格式(.cmpt 文件扩展名),
* 允许将多个类型子瓦片(例如 B3DM 、 I3DM或者pnts即点云)打包到一个文件中,
* 这种打包方式的主要目的是提高数据传输效率,减少浏览器需要发出的HTTP请求次数,从而加速大规模3D数据的加载和渲染。
*/
{ value: "Composite", label: "复合瓦片格式的模型" },
{ value: "PointCloud", label: "点云" },
{ value: "PointCloudConstantColor", label: "具有统一颜色的点云" },
{ value: "PointCloudNormals", label: "与点云中的点相关联的法线矢量" },
/**
* Batched:批处理是一种将多个数据项或操作组合在一起处理的技术,旨在提高效率。
* 在计算机图形学中,批处理常用于减少CPU和GPU之间的通信开销,从而优化渲染或计算性能。
* 例如,在3D渲染中,将多个小对象合并为一个批次,可以减少渲染调用的次数(draw calls),从而提升性能。
*/
{ value: "PointCloudBatched", label: "批量处理的点云" },
/**
* Draco 是由 Google 开发的一种开源压缩库,专门用于压缩和解压缩 3D 几何网格和点云数据。
* 它的核心目标是通过减小 3D 数据的大小,优化数据传输和存储效率。
*/
{ value: "PointCloudDraco", label: "使用Draco压缩的点云数据" },
];

//#endregion

function viewerCreated(v) {
viewer = v;
initViewer();
initEvents();
handleChangeModel(form.model);
}

//#region --------------------- 方法区域----------------
function initViewer() {
scene = viewer.scene;
viewer.clock.currentTime = new Cesium.JulianDate(2457522.154792);
viewer.shadows = true;
/**
* Cesium3DTilesInspector是一个小部件。
* 这个小部件是一个交互式调试工具,专门用于检查和调试 3D Tiles 数据集。
*/
viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);
inspectorViewModel = viewer.cesium3DTilesInspector.viewModel;
}

function initEvents() {
handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);

handler.setInputAction(function (movement) {
const feature = inspectorViewModel.feature;
if (Cesium.defined(feature)) {
const propertyIds = feature.getPropertyIds();
const length = propertyIds.length;
let propTbl = "";
for (let i = 0; i < length; ++i) {
const propertyId = propertyIds[i];
propTbl += `<b>${propertyId}</b>: ${feature.getProperty(
propertyId
)}<br>`;
}
ElMessage({
dangerouslyUseHTMLString: true,
message: propTbl,
});
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

handler.setInputAction(function (movement) {
const feature = inspectorViewModel.feature;
if (Cesium.defined(feature)) {
feature.show = false;
hiddenFeatures.value.push(feature);
}
}, Cesium.ScreenSpaceEventType.MIDDLE_CLICK);
}

//切换模型
function handleChangeModel(value) {
switch (value) {
case "Tileset":
loadTileset("./SampleData/Cesium3DTiles/Tilesets/Tileset/tileset.json");
break;
case "Translucent":
loadTileset(
"./SampleData/Cesium3DTiles/Batched/BatchedTranslucent/tileset.json"
);
break;
case "Translucent/Opaque":
loadTileset(
"./SampleData/Cesium3DTiles/Batched/BatchedTranslucentOpaqueMix/tileset.json"
);
break;
case "Multi-color":
loadTileset(
"./SampleData/Cesium3DTiles/Batched/BatchedColors/tileset.json"
);
break;
case "Request Volume":
loadTileset(
"./SampleData/Cesium3DTiles/Tilesets/TilesetWithViewerRequestVolume/tileset.json"
);
break;
case "Batched":
loadTileset(
"./SampleData/Cesium3DTiles/Batched/BatchedWithBatchTable/tileset.json"
);
break;
case "Instanced":
loadTileset(
"./SampleData/Cesium3DTiles/Instanced/InstancedWithBatchTable/tileset.json"
);
break;
case "Instanced/Orientation":
loadTileset(
"./SampleData/Cesium3DTiles/Instanced/InstancedOrientation/tileset.json"
);
break;
case "Composite":
loadTileset(
"./SampleData/Cesium3DTiles/Composite/Composite/tileset.json"
);
break;
case "PointCloud":
loadTileset(
"./SampleData/Cesium3DTiles/PointCloud/PointCloudRGB/tileset.json"
);
break;
case "PointCloudConstantColor":
loadTileset(
"./SampleData/Cesium3DTiles/PointCloud/PointCloudConstantColor/tileset.json"
);
break;
case "PointCloudNormals":
loadTileset(
"./SampleData/Cesium3DTiles/PointCloud/PointCloudNormals/tileset.json"
);
break;
case "PointCloudBatched":
loadTileset(
"./SampleData/Cesium3DTiles/PointCloud/PointCloudBatched/tileset.json"
);
break;
case "PointCloudDraco":
loadTileset(
"./SampleData/Cesium3DTiles/PointCloud/PointCloudDraco/tileset.json"
);
break;
default:
break;
}
}

/**
* Cesium3DTileset 的 enableDebugWireframe 属性是一个用于调试的布尔值属性。
* 启用 enableDebugWireframe 后,图块集中的几何内容(如建筑模型或地形)将以线框形式显示,展示构成模型的三角形边缘。
* 这一功能主要用于开发和调试过程中,例如有如下的一些用途:
* 1. 检查模型的网格是否存在问题,例如错误的几何形状。
* 2. 可视化不同细节层次(LOD)的几何变化。
* 3. 排查渲染问题,如深度冲突(z-fighting)或剔除错误。
*/

async function loadTileset(resource) {
reset();
try {
tileset = await Cesium.Cesium3DTileset.fromUrl(resource, {
//仅用于调试。此条件必须为真,以便在 WebGL1 中使 debugWireframe 生效。在创建瓦片集之后无法设置此条件。
enableDebugWireframe: true,
});
viewer.scene.primitives.add(tileset);
inspectorViewModel.tileset = tileset;

tileset.tileLoad.addEventListener(function (tile) {
processTile(tile);
});

tileset.allTilesLoaded.addEventListener(function () {
console.log("所有的瓦片已加载完毕!");
console.log("瓦片集合中包含的所有属性如下:", ...allProps);
});

viewer.zoomTo(
tileset,
new Cesium.HeadingPitchRange(
0,
-2.0,
Math.max(100.0 - tileset.boundingSphere.radius, 0.0)
)
);
const properties = tileset.properties;
if (Cesium.defined(properties) && Cesium.defined(properties.Height)) {
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
["${Height} >= 83", "color('purple', 0.5)"],
["${Height} >= 80", "color('red')"],
["${Height} >= 70", "color('orange')"],
["${Height} >= 12", "color('yellow')"],
["${Height} >= 7", "color('lime')"],
["${Height} >= 1", "color('cyan')"],
["true", "color('blue')"],
],
},
});
}
} catch (error) {
ElMessage.error(`tileset数据加载出错: ${error}`);
}
}

function handleShadowsChange(checked) {
viewer.shadows = checked;
}

function reset() {
if (Cesium.defined(tileset)) {
scene.primitives.remove(tileset);
inspectorViewModel.tileset = undefined;
}
if (allProps.size) {
allProps.clear();
}
hiddenFeatures.value = []; //清空数组
console.clear();
}

function handleRecovery() {
if (hiddenFeatures.value.length) {
hiddenFeatures.value.forEach((f) => {
f.show = true;
});
hiddenFeatures.value = []; //清空数组
}
}

function processTile(tile) {
const content = tile.content;
if (content instanceof Cesium.Model3DTileContent) {
if (Cesium.defined(content)) {
const featuresLength = content.featuresLength;
for (let i = 0; i < featuresLength; i++) {
const feature = content.getFeature(i);
const propertyNames = feature.getPropertyIds(); // 关键方法
propertyNames.forEach((p) => {
console.log(++count, "🚀 | p:", p);
allProps.add(p);
});
}
}
}
if (tile.children) {
tile.children.forEach((child) => processTile(child));
}
}
//#endregion
</script>

<style lang="scss" scoped>
.panel {
position: absolute;
width: 380px;
left: 10px;
top: 10px;
padding: 10px;
background-color: white;
border-radius: 4px;
z-index: 2;
opacity: 0.96;

:deep(.el-form-item) {
margin-bottom: 5px;
}

:deep(.el-form-item:last-child) {
margin-bottom: 0px;
}
}

:deep(.cesium-viewer-cesium3DTilesInspectorContainer) {
top: 10px;
}

:deep(.cesium-3DTilesInspector) {
width: 400px;
}
</style>

我看官方示例的3D Tiles Inspector展示了当前数据的所有属性名称,自己也尝试按照自己的思路来实现了该功能:通过给tileLoad和allTilesLoaded增加监听器去便利瓦片数据中所有属性名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
tileset.tileLoad.addEventListener(function (tile) {
processTile(tile);
});

tileset.allTilesLoaded.addEventListener(function () {
console.log("所有的瓦片已加载完毕!");
console.log("瓦片集合中包含的所有属性如下:", ...allProps);
});

function processTile(tile) {
const content = tile.content;
if (content instanceof Cesium.Model3DTileContent) {
if (Cesium.defined(content)) {
const featuresLength = content.featuresLength;
for (let i = 0; i < featuresLength; i++) {
const feature = content.getFeature(i);
const propertyNames = feature.getPropertyIds(); // 关键方法
propertyNames.forEach((p) => {
console.log(++count, "🚀 | p:", p);
allProps.add(p);
});
}
}
}
if (tile.children) {
tile.children.forEach((child) => processTile(child));
}
}

控制台输出结果如下: