原文链接及内容

这个示例主要为了说明:启用按需渲染模式,仅在场景内容更新或手动调用scene.requestRender()时渲染新帧,从而降低 CPU 使用率。具体解释请看代码注释。

效果如下图所示:

示例代码如下:

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
<style>
@import url(../templates/bucket.css);

#toolbar {
background: rgba(42, 42, 42, 0.8);
padding: 4px;
border-radius: 4px;
}

#toolbar h3,
h4 {
margin: 0 0 2px 0;
}

#toolbar i {
font-size: 85%;
}

#toolbar table {
margin: 2px 3px;
max-width: 280px;
}

#toolbar td {
padding-bottom: 10px;
}

#toolbar .cesium-button {
margin: 6px 0 0;
}

#toolbar input {
vertical-align: middle;
}

#toolbar input:disabled {
color: darkgray;
}

i {
color: #ffff00;
}

.cesium-performanceDisplay-defaultContainer {
top: 10px;
}

#toolbar table {
max-width: 310px !important;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay">
<h1>加载中...</h1>
</div>
<div id="toolbar">
<table>
<tbody>
<tr>
<td>
最后一帧渲染时的模拟时间:<code><span data-bind="text: lastRenderTime"></span></code>
<button type="button" class="cesium-button" data-bind="click: requestRender">
(手动渲染)渲染新帧
</button>
</td>
</tr>
<tr>
<td>
<h3>
<input value="true" data-bind="checked: requestRenderMode, valueUpdate: 'input'" type="checkbox">
启用按需渲染
</h3>
<i>当启用时,仅在场景更新或显式请求渲染时绘制新帧。</i>
</td>
</tr>
<tr data-bind="visible: showTimeOptions">
<td>
<h4>
<input value="true" data-bind="checked: timeChangeEnabled, valueUpdate: 'input'" type="checkbox">
仿真时间变化时渲染
</h4>
<i>当仿真时间按“最大时间差”(Max delta time)变化时自动渲染。调整动画控件中的仿真时间以及“最大时间差”(Max delta time)值以查看它们的关系。</i>
</td>
</tr>
<tr data-bind="visible: showTimeOptions">
<td>
<h4>最大时间差(Max delta time)</h4>
<input min="0" max="20" step="0.1"
data-bind="value: maximumRenderTimeChange, valueUpdate: 'input', enable: timeChangeEnabled" type="range">
<input size="2" data-bind="value: maximumRenderTimeChange, valueUpdate: 'input', enable: timeChangeEnabled"
type="text">
</td>
</tr>
</tbody>
</table>
</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
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
homeButton: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
fullscreenButton: false,
sceneModePicker: false,
baseLayerPicker: false,
infoBox: false,
/**
* 创建一个不会渲染新帧的Viewer,只有当场景更新需要时才渲染新帧,
* 否则它不会渲染新帧,以减少整体 CPU 使用率。
*/
requestRenderMode: true, //启用按需渲染模式
maximumRenderTimeChange: Infinity, //默认禁用基于仿真时间变化的自动渲染,即不因时间变化触发渲染
terrain: Cesium.Terrain.fromWorldTerrain(),
});
viewer.cesiumWidget.creditContainer.style.display = "none";

const scene = viewer.scene;
// 显示帧率(FPS)计数器,用于监控性能。
scene.debugShowFramesPerSecond = true;

let tileset;

const viewModel = {
requestRenderMode: true, //是否启用按需渲染,默认开启
showTimeOptions: false, //是否显示仿真时间相关选项,默认隐藏
timeChangeEnabled: false, //是否因仿真时间变化触发渲染,默认禁用
maximumRenderTimeChange: 0.0, //设置仿真时间变化的最大阈值,决定渲染频率。
lastRenderTime: "", //记录最后一次渲染的仿真时间,用于 UI 显示
requestRender: function () {
//手动触发渲染新帧的函数
scene.requestRender();
},
};

// 清除当前场景并恢复默认状态,为加载新场景做准备
let handler;
let loadingTileset = false;
function resetScene() {
// 清除跟踪实体、数据源、所有实体和瓦片集
viewer.trackedEntity = undefined;
viewer.dataSources.removeAll();
viewer.entities.removeAll();
viewer.scene.primitives.remove(tileset);
viewer.clock.shouldAnimate = false; // 停止仿真时间动画
handler = handler && handler.destroy(); //销毁事件处理器(handler)
// 显示默认天空盒并将相机视角重置到地球全景
scene.skyBox.show = true;
scene.camera.flyHome(0.0);
scene.requestRender(); //手动触发一次渲染
// 重置 viewModel 的时间相关设置
viewModel.showTimeOptions = false;
viewModel.timeChangeEnabled = false;
viewModel.maximumRenderTimeChange = 0;
loadingTileset = false;
}

// 加载瓦片集并设置视图。无需调用 scene.requestRender()
async function loadTilesetScenario() {
resetScene();

loadingTileset = true;
try {
tileset = await Cesium.Cesium3DTileset.fromIonAssetId(40866);
// 检查 loadingTileset 状态,防止在切换场景时加载废弃数据
if (!loadingTileset) {
return;
}
// 这里不需要显式调用 scene.requestRender(),因为添加瓦片集会自动触发渲染。
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);
} catch (error) {
console.log(`加载tileset出错: ${error}`);
}
}

// 加载动画模型并设置视角。无需调用 scene.requestRender()。
// 启用并调整最大仿真时间变化以以期望的速度查看动画。
function loadModelScenario() {
resetScene();
viewModel.timeChangeEnabled = true;
viewModel.showTimeOptions = true; //显示时间控制选项

const entity = viewer.entities.add({
name: "Aircraft",
position: Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 5000.0),
model: {
uri: "../SampleData/models/CesiumAir/Cesium_Air.glb",
minimumPixelSize: 128,
maximumScale: 20000,
},
});

viewer.trackedEntity = entity;
viewer.clock.shouldAnimate = true; //启用时间仿真以支持动画
}

// 加载 CZML 数据源并添加一个模型,设置 trackedEntity。无需调用 scene.requestRender()。
// 启用并调整最大模拟时间变化以以期望的速度查看动画。
function loadCzmlScenario() {
resetScene();
viewModel.showTimeOptions = true;
viewModel.timeChangeEnabled = true;
viewModel.maximumRenderTimeChange = 10.0;

viewer.dataSources.add(
Cesium.CzmlDataSource.load("../SampleData/simple.czml")
);
viewer.clock.shouldAnimate = true;
}

// 选择一个实体,仅在需要时进行渲染。
function pickingScenario() {
resetScene();

let color = Cesium.Color.CORNFLOWERBLUE;
const colorProperty = new Cesium.CallbackProperty(function () {
return color;
}, false);
const entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
box: {
dimensions: new Cesium.Cartesian3(1000000.0, 1000000.0, 30000.0),
material: new Cesium.ColorMaterialProperty(colorProperty),
},
});

scene.requestRender();

// 如果鼠标悬停在绘制的矩形上,则改变其缩放和颜色,然后请求一个新的渲染帧。
let lastPicked;
handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
const pickedObject = scene.pick(movement.endPosition);
if (Cesium.defined(pickedObject) && pickedObject.id === entity) {
if (Cesium.defined(lastPicked)) {
return;
}
color = Cesium.Color.YELLOW;
// 在颜色变化时触发渲染
scene.requestRender();
lastPicked = pickedObject;
} else if (Cesium.defined(lastPicked)) {
color = Cesium.Color.CORNFLOWERBLUE;
// 在颜色变化时触发渲染
scene.requestRender();
lastPicked = undefined;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}

// 通过 API 对场景进行更改,需要在变更时调用 requestRender()。
function setScenePropertiesScenario() {
resetScene();
// 隐藏天空盒并将背景色设置为蓝色
scene.skyBox.show = false;
scene.backgroundColor = Cesium.Color.CORNFLOWERBLUE;
scene.requestRender();
}

const toolbar = document.getElementById("toolbar");
Cesium.knockout.track(viewModel);
Cesium.knockout.applyBindings(viewModel, toolbar);

Cesium.knockout
.getObservable(viewModel, "requestRenderMode")
.subscribe(function (value) {
scene.requestRenderMode = value;
});

Cesium.knockout
.getObservable(viewModel, "timeChangeEnabled")
.subscribe(function (value) {
scene.maximumRenderTimeChange = value
? viewModel.maximumRenderTimeChange
: Infinity;
});

Cesium.knockout
.getObservable(viewModel, "maximumRenderTimeChange")
.subscribe(function (value) {
scene.maximumRenderTimeChange = value;
});

scene.postRender.addEventListener(function () {
const time = Cesium.JulianDate.toGregorianDate(scene.lastRenderTime);
const value = `${time.hour}:${time.minute}:${
time.second
}:${time.millisecond.toFixed(0)}`;
Cesium.knockout.getObservable(viewModel, "lastRenderTime")(value);
});

const scenarios = [
{
text: "默认场景视图",
onselect: resetScene,
},
{
text: "加载 3D 瓦片集并设置视图",
onselect: loadTilesetScenario,
},
{
text: "鼠标悬停拾取",
onselect: pickingScenario,
},
{
text: "加载时间动态的CZML",
onselect: loadCzmlScenario,
},
{
text: "动画模型",
onselect: loadModelScenario,
},
{
text: "使用API修改场景",
onselect: setScenePropertiesScenario,
},
];

Sandcastle.addToolbarMenu(scenarios);