原文链接及内容

: 由于个人的Cesium Ion中没有官方示例中的数据,因此仅在官方提供的在线编辑器中进行了学习、修改。

实现效果如下视频所示:

示例代码如下:

1
2
3
4
5
6
7
8
9
<style>
@import url(../templates/bucket.css);
.cesium-performanceDisplay-defaultContainer {
top: 10px;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>拼命加载数据中...</h1></div>
<div id="toolbar"></div>
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
// Aerometrex 提供的旧金山渡轮大厦摄影测量模型
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
fullscreenButton: false,
selectionIndicator: false,
skyBox: false,
shouldAnimate: true,
infoBox: false,
orderIndependentTranslucency: false,
terrain: Cesium.Terrain.fromWorldTerrain(),
});

viewer.cesiumWidget.creditContainer.style.display = "none"; //隐藏版权信息
viewer.scene.debugShowFramesPerSecond = true; //添加帧速显示

viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
"2021-11-09T20:27:37.016064475348684937Z"
);

const scene = viewer.scene;

// 初始化视角,俯瞰整个城市
viewer.camera.flyTo({
destination: new Cesium.Cartesian3(
-2703640.80485846,
-4261161.990345464,
3887439.511104276
),
orientation: new Cesium.HeadingPitchRoll(
0.22426651143535548,
-0.2624145362506527,
0.000006972977223185239
),
duration: 0,
});

let tileset;
try {
//个人的Cesium Ion中并不存在该数据,官方提供的数据。
tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2333904);

const translation = new Cesium.Cartesian3(
-1.398521324920626,
0.7823052871729486,
0.7015244410592609
);
//通过一个表示平移的Cartesian3来创建一个Mitrix4平移
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);

tileset.maximumScreenSpaceError = 8.0;
/**
* pickTranslucentDepth:如果为 true,则允许使用深度缓冲区拾取半透明几何体。
* 启用后,性能会下降。还有额外的绘制调用来写入半透明几何体的深度。
*/
scene.pickTranslucentDepth = true;
scene.light.intensity = 7.0; //设置光照强度

viewer.scene.primitives.add(tileset);
} catch (error) {
console.log(`tileset加载出错: ${error}`);
}

// Styles 样式 =============================================================================

const classificationStyle = new Cesium.Cesium3DTileStyle({
color: "color(${color})",
});

const translucentWindowsStyle = new Cesium.Cesium3DTileStyle({
color: {
conditions: [["${component} === 'Windows'", "color('gray', 0.7)"]],
},
});

// Shaders-着色器 ============================================================================

/**
* 设置无光照模式(UNLIT)的虚拟着色器,用于分类样式。
* UNLIT 是一种渲染模式,通常用于以下场景:
* - 无光照效果:物体不受光源影响,直接显示材质颜色或纹理。
* - 简化渲染:在不需要复杂光照计算的场景中,使用 UNLIT 模式可以提高渲染性能。
* - 艺术化效果:适用于卡通渲染、风格化渲染等非写实效果。
*/
const emptyFragmentShader =
"void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {}";
const unlitShader = new Cesium.CustomShader({
lightingModel: Cesium.LightingModel.UNLIT,
fragmentShaderText: emptyFragmentShader,
});

const materialShader = new Cesium.CustomShader({
lightingModel: Cesium.LightingModel.PBR,
fragmentShaderText: `
const int WINDOW = 0;
const int FRAME = 1;
const int WALL = 2;
const int ROOF = 3;
const int SKYLIGHT = 4;
const int AIR_CONDITIONER_WHITE = 5;
const int AIR_CONDITIONER_BLACK = 6;
const int AIR_CONDITIONER_TALL = 7;
const int CLOCK = 8;
const int PILLARS = 9;
const int STREET_LIGHT = 10;
const int TRAFFIC_LIGHT = 11;

void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
int featureId = fsInput.featureIds.featureId_0;

if (featureId == CLOCK) {
// Shiny brass 光亮的黄铜色
material.specular = vec3(0.98, 0.90, 0.59);
material.roughness = 0.1;
} else if (
featureId == STREET_LIGHT ||
featureId == AIR_CONDITIONER_BLACK ||
featureId == AIR_CONDITIONER_WHITE ||
featureId == AIR_CONDITIONER_TALL ||
featureId == ROOF
) {
// 暗哑铝色
material.specular = vec3(0.91, 0.92, 0.92);
material.roughness = 0.5;
} else if (featureId == WINDOW || featureId == SKYLIGHT) {
// 半透明,但也设置一个橙色的自发光颜色,这样看起来就像从里面被照亮了
material.emissive = vec3(1.0, 0.3, 0.0);
material.alpha = 0.5;
} else if (featureId == WALL || featureId == FRAME || featureId == PILLARS) {
// 把墙壁和柱子涂成白色,与黄铜钟色形成对比
material.diffuse = mix(material.diffuse, vec3(1.0), 0.8);
material.roughness = 0.9;
} else {
// brighten everything else 照亮其他的一切
material.diffuse += 0.05;
material.roughness = 0.9;
}
}
`,
});

const NOTHING_SELECTED = 12;
const selectFeatureShader = new Cesium.CustomShader({
uniforms: {
u_selectedFeature: {
type: Cesium.UniformType.INT,
value: NOTHING_SELECTED,
},
},
lightingModel: Cesium.LightingModel.PBR,
fragmentShaderText: `
const int NOTHING_SELECTED = 12;
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
int featureId = fsInput.featureIds.featureId_0;

if (u_selectedFeature < NOTHING_SELECTED && featureId == u_selectedFeature) {
material.specular = vec3(1.00, 0.85, 0.57);
material.roughness = 0.1;
}
}
`,
});

const multipleFeatureIdsShader = new Cesium.CustomShader({
uniforms: {
u_selectedFeature: {
type: Cesium.UniformType.FLOAT,
value: NOTHING_SELECTED,
},
},
lightingModel: Cesium.LightingModel.UNLIT,
fragmentShaderText: `
const int IDS0_WINDOW = 0;
const int IDS1_FACADE = 2;
const int IDS1_ROOF = 3;
const vec3 PURPLE = vec3(0.5, 0.0, 1.0);
const vec3 YELLOW = vec3(1.0, 1.0, 0.0);
const vec3 NO_TINT = vec3(1.0);

void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
int featureId0 = fsInput.featureIds.featureId_0; // fine features 优良要素
int featureId1 = fsInput.featureIds.featureId_1; // coarse features 粗糙要素

// 使用这两个要素 ID 集合来确定要素的位置
float isWindow = float(featureId0 == IDS0_WINDOW);
float isFacade = float(featureId1 == IDS1_FACADE);
float isRoof = float(featureId1 == IDS1_ROOF);

// 将屋顶窗户着色为黄色,将外墙窗户着色为紫色
vec3 tint = NO_TINT;
tint = mix(tint, YELLOW, isWindow * isRoof);
tint = mix(tint, PURPLE, isWindow * isFacade);
material.diffuse *= tint;
}
`,
});

// 示例方法 =====================================================================

function defaults() {
tileset.style = undefined;
tileset.customShader = unlitShader;
tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.HIGHLIGHT;
tileset.colorBlendAmount = 0.5;
tileset.featureIdLabel = 0;
}

const showPhotogrammetry = defaults;

function showClassification() {
defaults();
tileset.style = classificationStyle;
tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.MIX;
}

function showAlternativeClassification() {
showClassification();
// This dataset has a second feature ID texture.
// 此数据集具有第二个特征 ID 纹理。
tileset.featureIdLabel = 1;
}

function translucentWindows() {
defaults();
tileset.style = translucentWindowsStyle;
}

function pbrMaterials() {
defaults();
tileset.customShader = materialShader;
}

function goldenTouch() {
defaults();
tileset.customShader = selectFeatureShader;
}

function multipleFeatureIds() {
defaults();
tileset.customShader = multipleFeatureIdsShader;
}

// 拾取处理程序 ======================================================================

// HTML overlay 用于在鼠标悬停时显示要素名称
const nameOverlay = document.createElement("div");
viewer.container.appendChild(nameOverlay);
nameOverlay.className = "backdrop";
nameOverlay.style.display = "none";
nameOverlay.style.position = "absolute";
nameOverlay.style.bottom = "0";
nameOverlay.style.left = "0";
nameOverlay.style["pointer-events"] = "none";
nameOverlay.style.padding = "4px";
nameOverlay.style.backgroundColor = "black";
nameOverlay.style.whiteSpace = "pre-line";
nameOverlay.style.fontSize = "12px";

let enablePicking = true;
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
if (enablePicking) {
const pickedObject = viewer.scene.pick(movement.endPosition);
if (pickedObject instanceof Cesium.Cesium3DTileFeature) {
nameOverlay.style.display = "block";
nameOverlay.style.bottom = `${
viewer.canvas.clientHeight - movement.endPosition.y
}px`;
nameOverlay.style.left = `${movement.endPosition.x}px`;
const component = pickedObject.getProperty("component");
const message = `Component: ${component}\nFeature ID: ${pickedObject.featureId}`;
nameOverlay.textContent = message;
} else {
nameOverlay.style.display = "none";
}
} else {
nameOverlay.style.display = "none";
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

const clickHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
if (enablePicking) {
const pickedObject = scene.pick(movement.position);
if (
Cesium.defined(pickedObject) &&
Cesium.defined(pickedObject.featureId)
) {
selectFeatureShader.setUniform(
"u_selectedFeature",
pickedObject.featureId
);
} else {
selectFeatureShader.setUniform("u_selectedFeature", NOTHING_SELECTED);
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// UI ============================================================================

Sandcastle.addToggleButton("启用拾取", enablePicking, function (checked) {
enablePicking = checked;
});

const demos = [
{
text: "显示分类",
onselect: showClassification,
},
{
text: "显示另一种分类",
onselect: showAlternativeClassification,
},
{
text: "(半)透明窗户",
onselect: translucentWindows,
},
//PBR 是 Physically Based Rendering 的缩写,中文翻译为 基于物理的渲染。
{
text: "风格化的PBR材质",
onselect: pbrMaterials,
},
{
text: "金色材质(效果)",
onselect: goldenTouch,
},
{
text: "多要素ID集合",
onselect: multipleFeatureIds,
},
{
text: "无分类",
onselect: showPhotogrammetry,
},
];
Sandcastle.addDefaultToolbarMenu(demos);
showClassification();