原文链接及内容

效果如下图所示:我们可以通过第一个下拉框切换不同(数据源或同一数据源但参数设置不同)的地形数据,然后分别切换至珠穆朗玛峰,位于美国的半圆顶及旧金山湾区分别查看开启光照或雾后的地形加载效果。我们还可以为珠穆朗玛峰的地形进行采样并用带有biilboard和label的实体进行高度标记。这里我将切换至珠穆朗玛峰视角的时间设置为6月1日的上午10点,这时候有太阳,方便查看光照效果,雾的效果可以自行调整时间,例如调整到晚上。另外两个地点就按代码中原来的时间凌晨0点,日期统一用6月1日。这个日期是我自己改的,没有特别意义。

示例代码如下:

注:关于Heightmap(高度贴图,这里map应该翻译为贴图)请阅读维基百科的讲解:https://en.wikipedia.org/wiki/Heightmap

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
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
fullscreenButton: false,
skyBox: false,
shouldAnimate: true,
baseLayerPicker: false,
shadows: true,
terrain: Cesium.Terrain.fromWorldTerrain({
requestWaterMask: true, //请求水体掩膜,渲染海洋和湖泊的动态水面
requestVertexNormals: true, //请求顶点法线,支持光照和阴影效果
}),
});
viewer.cesiumWidget.creditContainer.style.display = "none";

// 启用地形光照,基于太阳位置计算阴影
viewer.scene.globe.enableLighting = true;

// 设置场景时间为 2023-01-01 00:00:00,确保光照效果(太阳位置)适合地形展示。
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2023-01-01T00:00:00");

// 创建椭球地形提供者(EllipsoidTerrainProvider),表示无高度变化的平滑地球椭球(WGS84)
const ellipsoidProvider = new Cesium.EllipsoidTerrainProvider();

// 生成波浪状地形
const customHeightmapWidth = 32;
const customHeightmapHeight = 32;
// 自定义高度贴图(heigthmap)地形,基于正弦函数生成地形高度
const customHeightmapProvider = new Cesium.CustomHeightmapTerrainProvider({
width: customHeightmapWidth,
height: customHeightmapHeight,
// 根据瓦片坐标(x,y)和缩放级别level生成高度数据
callback: function (x, y, level) {
// 高度贴图分辨率为32×32像素
const width = customHeightmapWidth;
const height = customHeightmapHeight;
const buffer = new Float32Array(width * height);

for (let yy = 0; yy < height; yy++) {
for (let xx = 0; xx < width; xx++) {
const u = (x + xx / (width - 1)) / Math.pow(2, level);
const v = (y + yy / (height - 1)) / Math.pow(2, level);

// 计算高度,范围 0 到 4000 米。
const heightValue = 4000 * (Math.sin(8000 * v) * 0.5 + 0.5);

const index = yy * width + xx;
buffer[index] = heightValue;
}
}

return buffer;
},
});

Sandcastle.addToolbarMenu(
[
{
text: "带水体掩膜和顶点法线的世界地形",
onselect: function () {
viewer.scene.setTerrain(
Cesium.Terrain.fromWorldTerrain({
requestWaterMask: true,
requestVertexNormals: true,
})
);
viewer.scene.globe.enableLighting = true;
},
},
{
text: "无水体掩膜和顶点法线的世界地形",
onselect: function () {
viewer.scene.setTerrain(Cesium.Terrain.fromWorldTerrain());
},
},
{
text: "带顶点法线和光照的世界地形",
onselect: function () {
viewer.scene.setTerrain(
Cesium.Terrain.fromWorldTerrain({
requestVertexNormals: true,
})
);
viewer.scene.globe.enableLighting = true;
},
},
{
text: "仅带水体掩膜的世界地形",
onselect: function () {
viewer.scene.setTerrain(
Cesium.Terrain.fromWorldTerrain({
requestWaterMask: true,
})
);
},
},
{
text: "无高度变化的平滑地球椭球",
onselect: function () {
viewer.terrainProvider = ellipsoidProvider;
},
},
{
text: "自定义高度贴图的地形",
onselect: function () {
viewer.terrainProvider = customHeightmapProvider;
},
},
// -------------该项数据已无法访问-----------
// {
// text: "从VR-TheWorld服务加载地形",
// onselect: function () {
// viewer.scene.setTerrain(
// new Cesium.Terrain(
// Cesium.VRTheWorldTerrainProvider.fromUrl(
// "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/",
// {
// credit: "Terrain data courtesy VT MÄK",
// }
// )
// )
// );
// },
// },
{
text: "从ArcGIS地形服务加载高程数据",
onselect: function () {
viewer.scene.setTerrain(
new Cesium.Terrain(
Cesium.ArcGISTiledElevationTerrainProvider.fromUrl(
"https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
)
)
);
},
},
],
"terrainMenu"
);

Sandcastle.addDefaultToolbarMenu(
[
{
text: "珠穆朗玛峰",
onselect: function () {
lookAtMtEverest();
},
},
{
text: "(美国约塞米蒂国家公园的)半圆顶",
onselect: function () {
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
"2023-06-01T00:00:00"
);
const target = new Cesium.Cartesian3(
-2489625.0836225147,
-4393941.44443024,
3882535.9454173897
);
const offset = new Cesium.Cartesian3(
-6857.40902037546,
412.3284835694358,
2147.5545426812023
);
viewer.camera.lookAt(target, offset);
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
},
},
{
text: "旧金山湾区",
onselect: function () {
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
"2023-06-01T00:00:00"
);
const target = new Cesium.Cartesian3(
-2708814.85583248,
-4254159.450845907,
3891403.9457429945
);
const offset = new Cesium.Cartesian3(
70642.66030209465,
-31661.517948317807,
35505.179997143336
);
viewer.camera.lookAt(target, offset);
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
},
},
],
"zoomButtons"
);

let terrainSamplePositions;

function lookAtMtEverest() {
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
"2023-06-01T10:00:00"
);
const target = new Cesium.Cartesian3(
300770.50872389384,
5634912.131394585,
2978152.2865545116
);
const offset = new Cesium.Cartesian3(
6344.974098678562,
-793.3419798081741,
2499.9508860763162
);
viewer.camera.lookAt(target, offset);
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
}

// 地形高度采样
function sampleTerrainSuccess(terrainSamplePositions) {
const ellipsoid = Cesium.Ellipsoid.WGS84;

//启用地形深度测试,确保标记不被地形遮挡。
viewer.scene.globe.depthTestAgainstTerrain = true;

viewer.entities.suspendEvents(); //优化性能,防止实体更新触发多余事件。
viewer.entities.removeAll();

for (let i = 0; i < terrainSamplePositions.length; ++i) {
const position = terrainSamplePositions[i];

viewer.entities.add({
name: position.height.toFixed(1),
position: ellipsoid.cartographicToCartesian(position), //将采样点的经纬度(Cartographic)转换为笛卡尔坐标
billboard: {
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, //底部对齐
scale: 0.7,
image: "../images/facility.gif",
},
label: {
text: position.height.toFixed(1),
font: "10pt monospace",
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
pixelOffset: new Cesium.Cartesian2(0, -14),
fillColor: Cesium.Color.BLACK,
outlineColor: Cesium.Color.BLACK,
showBackground: true,
backgroundColor: new Cesium.Color(0.9, 0.9, 0.9, 0.7),
backgroundPadding: new Cesium.Cartesian2(4, 3),
},
});
}
viewer.entities.resumeEvents();
}

// 以珠穆朗玛峰为中心生成 41x41 的采样网格,为地形采样提供点集
function createGrid(rectangleHalfSize) {
const gridWidth = 41;
const gridHeight = 41;
const everestLatitude = Cesium.Math.toRadians(27.988257);
const everestLongitude = Cesium.Math.toRadians(86.925145);
const e = new Cesium.Rectangle(
everestLongitude - rectangleHalfSize,
everestLatitude - rectangleHalfSize,
everestLongitude + rectangleHalfSize,
everestLatitude + rectangleHalfSize
);
const terrainSamplePositions = [];
for (let y = 0; y < gridHeight; ++y) {
for (let x = 0; x < gridWidth; ++x) {
// lerp:线性插值计算网格点的经度和纬度
const longitude = Cesium.Math.lerp(e.west, e.east, x / (gridWidth - 1));
const latitude = Cesium.Math.lerp(e.south, e.north, y / (gridHeight - 1));
const position = new Cesium.Cartographic(longitude, latitude);
terrainSamplePositions.push(position);
}
}
return terrainSamplePositions;
}

Sandcastle.addToggleButton(
"启用光照",
viewer.scene.globe.enableLighting,
function (checked) {
viewer.scene.globe.enableLighting = checked;
}
);

Sandcastle.addToggleButton(
"开启雾",
viewer.scene.fog.enabled,
function (checked) {
viewer.scene.fog.enabled = checked;
}
);

Sandcastle.addToolbarButton(
"珠穆朗玛峰地形采样(级别9)",
function () {
const terrainSamplePositions = createGrid(0.005);
Promise.resolve(
Cesium.sampleTerrain(viewer.terrainProvider, 9, terrainSamplePositions)
).then(sampleTerrainSuccess);
lookAtMtEverest();
},
"sampleButtons"
);

Sandcastle.addToolbarButton(
"珠穆朗玛峰地形采样(最高精度)",
function () {
if (!Cesium.defined(viewer.terrainProvider.availability)) {
window.alert("当前地形提供者不支持最高精度地形采样");
return;
}
const terrainSamplePositions = createGrid(0.0005);
Promise.resolve(
Cesium.sampleTerrainMostDetailed(
viewer.terrainProvider,
terrainSamplePositions
)
).then(sampleTerrainSuccess);
lookAtMtEverest();
},
"sampleButtons"
);