原文链接及内容

该示例主要为了:获取场景的阴影贴图(shadowMap)对象,手动设置阴影的最大投射距离,以限制阴影计算范围,优化性能。

注:在3D渲染中,阴影分为两种主要类型:

  • 硬阴影(Hard Shadows):阴影边缘非常尖锐、清晰,就像在强光下物体投射的影子。这种效果假设光源是点光源(如一个小灯泡),光线直射,没有扩散。
  • 软阴影(Soft Shadows):阴影边缘柔和、渐变,类似于现实中阳光透过云层或大型光源(如天空)照射时产生的影子。这种效果模拟了光源的面积较大,光线从不同角度散射,导致阴影边界模糊。可以翻译为柔和阴影或自然阴影。

效果如下图所示:

示例代码如下:

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
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
homeButton: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
fullscreenButton: false,
skyBox: false,
sceneModePicker: false,
baseLayerPicker: false,

infoBox: false, //禁用选中实体时显示的信息框
selectionIndicator: false, //禁用选中实体的(绿色)指示器
shadows: true, //启用场景的阴影效果
shouldAnimate: true, //启用仿真时间动画,允许场景随时间变化
terrainShadows: Cesium.ShadowMode.ENABLED, //启用地形投射和接收阴影
terrain: Cesium.Terrain.fromWorldTerrain(),
});
viewer.cesiumWidget.creditContainer.style.display = "none";

//获取场景的阴影贴图对象,设置阴影的最大投射距离为 10,000 米,以限制阴影计算范围,优化性能。
const shadowMap = viewer.shadowMap;
shadowMap.maximumDistance = 10000.0;

const cesiumAir = viewer.entities.add({
name: "飞机",
height: 20.0,
model: {
uri: "../SampleData/models/CesiumAir/Cesium_Air.glb",
},
});

const groundVehicle = viewer.entities.add({
name: "地面车辆",
height: 0.0,
model: {
uri: "../SampleData/models/GroundVehicle/GroundVehicle.glb",
// 过 heightReference 确保模型紧贴地形
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});

const cesiumMan = viewer.entities.add({
name: "人物",
height: 0.0,
model: {
uri: "../SampleData/models/CesiumMan/Cesium_Man.glb",
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});

const woodTower = viewer.entities.add({
name: "木塔",
height: 0.0,
model: {
uri: "../SampleData/models/WoodTower/Wood_Tower.glb",
},
});

const simpleCity = viewer.entities.add({
name: "简单城市模型",
height: 0.0,
model: {
uri: "../SampleData/models/ShadowTester/Shadow_Tester_4.glb",
},
});

const boxEntity = viewer.entities.add({
name: "立方体",
height: 10.0,
box: {
dimensions: new Cesium.Cartesian3(10.0, 10.0, 10.0),
material: Cesium.Color.RED,
shadows: Cesium.ShadowMode.ENABLED, //支持阴影投射和接收
},
});

const checkerMaterial = new Cesium.CheckerboardMaterialProperty({
evenColor: Cesium.Color.RED.withAlpha(0.5),
oddColor: Cesium.Color.RED.withAlpha(0.0),
repeat: new Cesium.Cartesian2(5.0, 10.0),
});

const checkerEntity = viewer.entities.add({
name: "棋盘格纹理的立方体",
height: 10.0,
box: {
dimensions: new Cesium.Cartesian3(10.0, 10.0, 10.0),
material: checkerMaterial,
outline: true,
outlineColor: Cesium.Color.RED,
shadows: Cesium.ShadowMode.ENABLED,
},
});

const sphereEntity = viewer.entities.add({
name: "球体",
height: 20.0,
ellipsoid: {
radii: new Cesium.Cartesian3(15.0, 15.0, 15.0),
material: Cesium.Color.BLUE.withAlpha(0.5),
slicePartitions: 24,
stackPartitions: 36,
shadows: Cesium.ShadowMode.ENABLED,
},
});

const locations = {
"(宾夕法尼亚州)埃克斯顿": {
longitude: -1.31968,
latitude: 0.698874,
height: 74.14210186070714,
date: 2457522.154792,
},
"(加利福尼亚)半穹顶": {
longitude: -2.086267733294987,
latitude: 0.6587491773830219,
height: 2640.716312584986,
date: 2457507.247512,
},
"珠穆朗玛峰": {
longitude: 1.517132688,
latitude: 0.4884844964,
height: 8773.17824498951,
date: 2457507.620845,
},
"(宾夕法尼亚州)尖峰石阵": {
longitude: -1.3324415110874286,
latitude: 0.6954224325279967,
height: 179.14276256241743,
date: 2457523.04162,
},
"(西弗吉尼亚州)塞内卡岩": {
longitude: -1.3851775172879768,
latitude: 0.6778211831093554,
height: 682.5893300695776,
date: 2457522.097512,
},
"外太空": {
longitude: -1.31968,
latitude: 0.698874,
height: 2000000.0,
date: 2457522.154792,
},
};

let i;
const entities = viewer.entities.values;
const entitiesLength = entities.length;

function setLocation(location) {
const longitude = location.longitude;
const latitude = location.latitude;
const height = location.height;

for (i = 0; i < entitiesLength; ++i) {
const entity = entities[i];
entity.position = Cesium.Cartesian3.fromRadians(
longitude,
latitude,
height + entity.height
);
}

viewer.clock.currentTime = new Cesium.JulianDate(location.date);
viewer.clock.multiplier = 1.0;
}

function setLocationFunction(location) {
return function () {
setLocation(location);
};
}

const locationToolbarOptions = [];
for (const locationName in locations) {
if (locations.hasOwnProperty(locationName)) {
const location = locations[locationName];
locationToolbarOptions.push({
text: locationName,
onselect: setLocationFunction(location),
});
}
}

Sandcastle.addToolbarMenu(locationToolbarOptions);

// 实体切换
function setEntity(entity) {
for (i = 0; i < entitiesLength; ++i) {
entities[i].show = false;
}
entity.show = true;
viewer.trackedEntity = entity;
}

function setEntityFunction(entity) {
return function () {
setEntity(entity);
};
}

const entityToolbarOptions = [];
for (i = 0; i < entitiesLength; ++i) {
const entity = entities[i];
entityToolbarOptions.push({
text: entity.name,
onselect: setEntityFunction(entity),
});
}

Sandcastle.addToolbarMenu(entityToolbarOptions);

Sandcastle.addToggleButton(
"启用(场景)阴影",
viewer.shadows,
function (checked) {
viewer.shadows = checked;
}
);

Sandcastle.addToggleButton("启用实体阴影", true, function (checked) {
const entityShadows = checked
? Cesium.ShadowMode.ENABLED
: Cesium.ShadowMode.DISABLED;
for (i = 0; i < entitiesLength; ++i) {
const entity = entities[i];
const visual = entity.model || entity.box || entity.ellipsoid;
visual.shadows = entityShadows;
}
});

Sandcastle.addToggleButton(
"启用地形阴影",
viewer.terrainShadows === Cesium.ShadowMode.ENABLED,
function (checked) {
viewer.terrainShadows = checked
? Cesium.ShadowMode.ENABLED
: Cesium.ShadowMode.DISABLED;
}
);

Sandcastle.addToggleButton(
"启用Soft Shadows",
shadowMap.softShadows,
function (checked) {
shadowMap.softShadows = checked;
}
);

// 阴影贴图分辨率,值越大则越精细,但性能开销更大
Sandcastle.addToolbarMenu([
{
text: "贴图分辨率:2048",
onselect: function () {
shadowMap.size = 2048;
},
},
{
text: "贴图分辨率:1024",
onselect: function () {
shadowMap.size = 1024;
},
},
{
text: "贴图分辨率:512",
onselect: function () {
shadowMap.size = 512;
},
},
{
text: "贴图分辨率:256",
onselect: function () {
shadowMap.size = 256;
},
},
]);

setLocation(locations["(宾夕法尼亚州)埃克斯顿"]);
setEntity(cesiumAir);