地形瓦片加载慢,首屏转了十几秒
影像早就铺满了,地形却一直有锯齿状的空洞,进度条停在一半,请求队列常年两百多条。
现象
初始化后 globe.tilesLoaded 持续为 false 超过 12 秒。
网络面板里 terrain/... 请求大量处于 pending,
RequestScheduler 的队列长度可以这样读:
console.log(Cesium.RequestScheduler.statistics);
// { numberOfAttemptedRequests: 412,
// numberOfActiveRequests: 6,
// numberOfCancelledRequests: 0,
// numberOfPendingRequests: 231 }
活跃请求只有 6,待处理却有 231,说明瓶颈在并发上限,而不是带宽。
原因
| 配置 | 影响 |
|---|---|
requestWaterMask: true | 每个地形瓦片额外带一张水面掩膜,请求数翻倍 |
requestVertexNormals: true | 额外拉取法线数据,瓦片体积增加约 30% |
maximumScreenSpaceError 默认 2 | 细分层级过高,远处也加载高精度瓦片 |
| HTTP/1.1 | 同域最多 6 个并发连接 |
| 无强缓存 | 二次进入仍全部重新请求 |
排查过程
- 读
RequestScheduler.statistics,区分是并发受限还是服务端响应慢。 - 看单个地形瓦片的响应时间与体积,正常应在 10–30 ms、几十 KB。
- 对比关闭
requestWaterMask前后的请求总量。 - 检查响应的
Cache-Control,没有max-age就会反复回源。
解决方案
只保留业务真正需要的能力。内陆项目通常不需要水面掩膜与顶点法线:
var terrain = await Cesium.CesiumTerrainProvider.fromUrl(
'https://terrain.example.cn/tiles/',
{
requestWaterMask: false,
requestVertexNormals: false
}
);
viewer.terrainProvider = terrain;
把屏幕空间误差放宽。默认 2 对大多数城市展示过细,4 在视觉上几乎看不出差别:
viewer.scene.globe.maximumScreenSpaceError = 4;
viewer.scene.globe.tileCacheSize = 200; // 默认 100,来回拖动时减少重复请求
服务端开 HTTP/2 与长缓存。地形瓦片内容不变,可以直接给一年:
location /tiles/ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin *;
gzip on;
gzip_types application/octet-stream application/json;
}
首屏只需要一个粗精度预览时,先用低层级瓦片占位,等相机稳定后再加载精细层级:
viewer.scene.globe.maximumScreenSpaceError = 8; // 初始粗
setTimeout(function () {
viewer.scene.globe.maximumScreenSpaceError = 3;
}, 2500);
验证
重新加载后 numberOfPendingRequests 应从 231 降到 60 以内,
tilesLoaded 在 3 秒内变为 true。
关闭网络限速后,第一次与第二次进入的瓦片请求数应有明显差异,说明缓存生效。
先分清瓶颈:如果活跃请求数远低于 6,且单瓦片响应超过 200 ms, 问题在服务端或网络,调前端参数效果有限。先测一个瓦片的 TTFB,再决定改哪一侧。
经纬志(2026)。《地形瓦片加载慢,首屏转了十几秒》。https://www.jingweizhi.com/cookbook/cesium-terrain-slow.html(访问日期 2026-11-03)。