当前位置:首页>排行榜>Cesium官方矢量瓦片深度评测:我们能在Cesium里跑出MapLibre风格的地图吗?

Cesium官方矢量瓦片深度评测:我们能在Cesium里跑出MapLibre风格的地图吗?

  • 更新时间 2026-09-21 10:16:22
Cesium官方矢量瓦片深度评测:我们能在Cesium里跑出MapLibre风格的地图吗?

一个被社区追了十年的功能,Cesium 终于在 1.142 ~ 1.145 这四个版本里,用一套完全不同于 Mapbox/MapLibre 的技术路线把它做出来了。

这篇文章不讲入门教程,只讲四件事:它到底是什么它和 MVT 生态的异同在哪官方的贴地架构到底怎么实现的它能不能变成你想要的那个 MapLibre


楔子:一个求了十年的功能

如果你在 2016 年就在用 CesiumJS,大概率刷到过 GitHub 上的 issue #2132 —— 标题只有两个词:Vector Tiles

那条 issue 的理由写得很朴素:Google、Apple、Mapbox GL、OpenLayers 3 都已经转向矢量渲染了,Cesium 还在栅格瓦片和手工堆 Entity 之间打转。此后十年,社区反复提出同一个问题,得到的答案基本是同一个:用 3D Tiles,或者自己把矢量瓦片栅格化成 PNG 再贴上去。

于是我们看到了一整代"民间方案":

  • cesium-vector-provider
    (TerriaJS 团队,davenquinn):把 Mapbox style 规范吃进来,用 Canvas 逐瓦片栅格化,再作为 ImageryProvider 喂给 Cesium;
  • MVTImageryProvider
    (hongfaqiu):把 pbf 瓦片解出来画到 Canvas,做成 imagery layer;
  • 更硬核的做法:Puppeteer + 无头浏览器渲成标准 XYZ 位图瓦片,再当普通影像图层用。

这些方案的共同点也很明显:它们是"2D 贴图"。矢量只是中间产物,最终给 Cesium 的仍然是一张位图。线宽随缩放糊掉、文字不可拾取、属性查询无从谈起、相机倾斜时纹理被强行拉伸——所有 MVT 的价值,在栅格化的那一刻全部丢失。

而 2026 年 6 月 CesiumJS 1.142 发布之后,这条路被官方重新铺了一遍,铺法完全不一样。


时间线:Cesium 矢量能力的三个跃迁

把官方在矢量方向的动作按时间排开,脉络其实非常清晰。

时间
版本 / 事件
关键变化
2016 ~ 2025
社区自造轮子阶段
cesium-vector-provider、MVTImageryProvider 等,本质是栅格化兜底
2025-09-02
CesiumJS 1.133
支持 EXT_mesh_primitive_restart glTF 扩展——矢量线/面批量编码的前提
2025-09-09
3d-tiles issue #825《Vector data... in glTF?》
官方首次系统提出用 glTF 承载矢量数据的方案(POINTS / LINES / LINE_STRIP / TRIANGLES + 元数据扩展)
2025-11-03
CesiumJS 1.135
支持 EXT_mesh_primitive_edge_visibility;边可见性从 gl.LINES 改为四边形展开,绕开 WebGL 无法画粗线的问题
2026-04-01
CesiumJS 1.140
引入实验性高性能矢量图元 API:BufferPointCollection / BufferPolylineCollection / BufferPolygonCollection
2026-05-01
CesiumJS 1.141
矢量瓦片集开始支持 EXT_structural_metadata 属性
2026-06-01
CesiumJS 1.142MVTDataProvider
 首次落地;同时上线 GeoJsonPrimitive、ion 矢量瓦片切片器(vector tiler)、3DTILES_content_gltf_vector / EXT_mesh_polygon 两个草案扩展
2026-06-29
官方博客《Help Shape Vector Data Support in 3D Tiles》
公开 3D Tiles 2.0 矢量方案与输入格式路线图,并向社区征集反馈
2026-07-01
CesiumJS 1.143
补齐 KHR_meshopt_compression,让矢量瓦片的 glTF 载荷可以走压缩通道
2026-08-01
CesiumJS 1.144
矢量线/面贴地形渲染,引入屏幕空间恒定线宽(screen-space-constant width)与逐要素样式(per-feature styling);底层落地 Core/VectorPipeline.js + Shaders/VectorCommon.glsl 的零几何 SDF 架构;补齐 KHR_mesh_primitive_restart 支持
2026-09-01
CesiumJS 1.145
矢量瓦片技术预览版(Vector Tiles Technology Preview)正式发布:贴地扩展到 3D TilesMVTDataProvider / GeoJsonPrimitive 新增 heightReferenceBufferPolylineCollection 新增 widthUnits(米制),贴地折线开启边缘抗锯齿

注意一个容易被忽略的关键节点:1.142 里 MVTDataProvider 和 3D Tiles 矢量渲染是同一天进仓库的。

这不是巧合。它直接决定了这个模块的性质——我们放到第五节展开。


MVTDataProvider 到底是什么:一次"运行时的翻译"

先看继承关系,这一行信息量最大:

MVTDataProvider extends  UrlTemplate3DTilesDataProvider

也就是说,MVTDataProvider 不是一个新的渲染器,也不是 ImageryProvider。它是一个「URL 模板 → 3D Tiles」的适配器,是 UrlTemplate3DTilesDataProvider 这个更通用的基类下面的第一个具体实现。

3.1 数据流

{z}/{x}/{y}.pbf  → Resource(带 query / header 鉴权)  → 按 extent + zoom 生成 3D Tiles    隐式瓦片树(implicit tiling)  → PBF 解码为要素(点 / 线 / 面 + 属性)  → 运行时转码(transcoding)为 glTF 图元  → 交给 Cesium3DTileset 标准渲染管线

官方文档的原话是:"Loads .mvt or .pbf tiles, converting tiles dynamically (at runtime) into 3D Tiles."

这一步"运行时转码"是整个设计里最聪明、也最值得吐槽的地方——聪明在于它彻底复用了已有的 3D Tiles 渲染栈,样式、拾取(picking)、元数据查询、贴地、LOD 全都白嫖;值得吐槽在于,MVT 那套为 2D 地图优化过的编码(相对整数坐标、命令流、局部坐标系)被翻译成了一套为 3D 几何优化过的编码(浮点顶点、glTF 图元),每次相机移动都在做这种转换。

3.2 API 全貌

const provider = await Cesium.MVTDataProvider.fromUrl(  "https://example.com/tiles/{z}/{x}/{y}.pbf",  {    minZoom: 6,                    // 默认 0    maxZoom: 14,                   // 默认 14    extent: Cesium.Rectangle.fromDegrees(5.9, 45.8, 10.5, 47.8),  // 弧度矩形    featureIdProperty: "osm_id",   // 用源属性做要素 ID    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,      // 1.145 新增    scene: viewer.scene,           // 贴地时必须传  },);viewer.scene.primitives.add(provider);await viewer.zoomTo(provider.tileset);

几个必须知道的细节:

  • URL 模板必须是 {z}/{x}/{y}
    ,走标准 Web Mercator XYZ 方案。后缀 .mvt / .pbf / 没有后缀都行,只要服务器返回合法 MVT 二进制。
  • 要鉴权就传 Cesium.Resource
    ,可以挂 queryParameters 和请求头,不用自己拼 URL。
  • provider 实现了 primitive 接口
    ,直接进 scene.primitives;内层真正的 Cesium3DTileset 通过 provider.tileset 暴露,挂在它上面的样式、事件、统计口径和普通 3D Tiles 完全一致。
  • provider.show 只隐藏不卸载
    scene.primitives.remove(provider) 才是真正释放。

3.3 样式能力清单(非常关键,决定了它能做什么)

几何类型
支持的样式属性
Points
color
showpointSizepointOutlineWidthpointOutlineColor
Lines
color
showlineWidth
Polygons
color
show

就这么一张表,我认为是全文最重要的信息之一。面要素只有颜色和显隐,没有描边、没有填充图案、没有虚线。 这意味着哪怕到了 1.145,MVTDataProvider 能还原的 MVT 视觉表现力仍然是 MapLibre 的一个很小的子集。

lineWidth 支持两种单位:屏幕像素(默认)与widthUnits,1.145 随 BufferPolylineCollection 引入,可让贴地折线的宽度按地面长度度量)。像素模式下线宽在缩放中视觉恒定,米模式下宽度随距离自然缩放。

这里要澄清一个常见的误读。MapLibre 的 line-width 同样以像素为单位,但它画在透视投影里,相机倾斜后近大远小——渲染结果是会随距离变化的。而 Cesium 的像素模式是严格的屏幕空间恒定宽度,无论远近宽窄都一样;真正与"近大远小"语义对应的是 Cesium 的米制模式。所以两者单位相同、语义并不对齐,渲染结果差异明显。

Pick 走标准 3D Tiles 流程:

const picked = viewer.scene.pick(movement.position);if (Cesium.defined(picked) && picked.getPropertyIds) {  picked.getPropertyIds().forEach(id => console.log(id, picked.getProperty(id)));}

属性名大小写敏感,且严格照搬源瓦片里的字段名。

3.4 已经被踩过的坑:一次性构建整棵瓦片树

官方教程里罕见地用了整整一小节来警告这件事,措辞相当直白:

不传 extent / minZoom / maxZoom 时,层次树会覆盖全球 0 ~ 14 级,产生约 3.58 亿个瓦片节点,在渲染开始之前就能耗尽浏览器内存。

几个量化结论值得背下来:

  • 全球范围深到 8 级,大约 8.7 万个节点
  • 每多一级,最深层节点数最多 ×4;
  • 提高 minZoom 几乎不省内存
    ——它只裁掉树顶部那几十个粗瓦片;
  • 真正决定内存的是 maxZoom 与 extent 的乘积

同时,MVTDataProvider 目前是启动时一次性构建完整层次树,懒加载建树还挂在 GitHub issue #13535 上。这意味着现阶段它更适合区域级数据集,全球级数据集请老实用 ion 矢量瓦片切片。

另外两个实用细节:

  • 404 / 204 被当作空瓦片而不是错误
    ,稀疏数据集不用为空白区域生成占位文件;
  • 相机不在 extent 范围内时,请求根本不会发出——排查"没数据"时先看这一条,再看网络面板。

贴地:从一行 API 到一整套渲染架构

矢量数据绝大多数是二维采编的:路网、河流、地块、行政边界,压根没有高程。直接扔进 3D 场景的结果就是——线沉到山下、面穿出地面、在山脊处悬浮。

Cesium 的处理方式,和 MapLibre 有本质区别。先看 API:

// 通用:3D Tiles 矢量资产贴地const tileset = scene.primitives.add(  await Cesium.Cesium3DTileset.fromIonAssetId(assetId, {    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,    scene: scene,   // 必须传!  }),);// 专有:MVT 直连贴地(1.145+)const provider = await Cesium.MVTDataProvider.fromUrl(url, {  maxZoom: 14,  heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,  scene: scene,   // 必须传!});

heightReference 的四种取值:

取值
行为
NONE
(默认)
使用源几何自带的高程,不做任何贴附
CLAMP_TO_TERRAIN
贴到地形(河流、步道、地界、行政边界)
CLAMP_TO_3D_TILE
贴到 3D Tiles 与模型(建筑屋面、扫描网格)
CLAMP_TO_GROUND
地形 + 3D Tiles + 模型,可见即贴

几个工程上会立刻撞上的约束:

  1. heightReference 和 scene 必须成对出现
    ,只给一个无效。因为 scene 才是"谁提供被贴附表面"的入口。
  2. 接收方(比如 OSM Buildings)创建时也必须传同一个 scene
    ,否则它的几何根本不参与贴附计算——这是最容易 debug 半天的坑。
  3. 贴附目标在创建时确定,之后改不了。
     想换目标只能 remove 再重建。
  4. show 过滤不减少瓦片下载量
    ,它是在瓦片加载之后在浏览器端过滤的。

4.1 底层实现:零几何 SDF 的贴地渲染架构

讲到这儿必须往下钻一层,否则就是隔靴搔痒。1.144 的贴地不是把矢量几何往下压,而是换了一套渲染范式,核心落在两个文件上:

  • Source/Core/VectorPipeline.js
     —— CPU 侧的数据组织。把线段打包进一张 RGBA 浮点纹理一个 texel 存一段线(内容是该段的 ax, ay, bx, by 四个 UV 坐标);同时用 CSR(压缩稀疏行) 结构建立空间加速网格,网格边长取 ceil(sqrt(N/16)),头部存 [gridW, gridH, end_0, end_1, …],每段按膨胀包围盒分配到所有与之重叠的 cell,padding 取 max(0.35/gridSize, halfWidthInUv);没有数据的 cell 用 1×1 占位纹理作哨兵。
  • Source/Shaders/VectorCommon.glsl
     —— GPU 侧的绘制。它不生成任何几何,而是在已有表面的片元着色器末尾直接叠加线段。全库只有两个接入点:GlobeFS.glsl(地形)与 ModelVectorLookupStageFS.glsl(3D Tiles)。

四个关键设计:

① 线宽恒定靠雅可比矩阵。 线画在贴图的 UV 空间里,要保证屏幕宽度恒定,就得知道"UV 偏移一个单位,屏幕偏移多少像素":

mat2 screenFromUv = inverse(mat2(dFdx(uv), dFdy(uv)));

这一步天然处理了斜视压缩——地形被压扁时,UV 到屏幕的缩放比会自动变化。

② 抗锯齿在片元里算。coverage = 1 - smoothstep(-0.5, 0.5, edgeDistance),可通过 scene.vectorProvider.antialias 关闭以换取性能。

③ 只取最近段。 相邻段在共享顶点处会重叠,逐段合成会让接缝颜色失真(亮线 0.5 → 0.75 会变亮,暗线 0.5 → 0.25 会变暗)。取 min 距离既避免重复合成,又能提前 break。

④ 拐角与端点免费附送。 距离场的并集运算就是 min,所以斜接与圆头端点天然正确——传统宽线方案里的斜接几何、miterLimit 钳制、近平面裁剪、非透视插值修正,在这条路径上一行都不用写。

为什么必须换这套方案? 因为在 WebGL 里"画粗线"本身就是个历史遗留难题:gl.lineWidth 在多数实现里被硬编码为 1(ALIASED_LINE_WIDTH_RANGE 通常是 [1, 1]),而 WebGL 只有顶点/片元两级着色器,没有几何着色器可以拿来把线"吹"成四边形。所以传统宽线只能在顶点着色器里把线展开成四边形——但展开后的四边形无法跟随起伏的地形,贴地就无从谈起。

SDF 方案把问题从"生成正确的几何"换成了"判断每个像素离线段多远":复杂度是常数级的,而且只要能提供片元着色器,就能被叠加上去。这正是 1.145 能用同一个 heightReference 同时支持地形、3D Tiles 和模型的原因——它不是给三种表面各写一套算法,而是把线段"画"到了三种表面上。

1.145 在这套架构上又补了两件事:BufferPolylineCollection 新增 widthUnits 选项(米 / 像素),以及修复贴地矢量折线渲染宽度翻倍的问题并开启边缘抗锯齿。与此同时,BufferPrimitiveCollection 拿到了 heightReference 只读属性——贴地不再是瓦片管线里的离线预计算,而是运行时沿着 GPU 图元做的表面采样。 这就是题目里"新一套矢量贴地渲染架构"的实质。

值得一提的是,官方在 1.145 里还顺手把这套技术反向用回了 ClippingPolygons:新的裁剪多边形算法"基于矢量瓦片使用的技术",大幅改善了跨距离尺度的裁剪质量,并且支持在裁剪多边形内部打洞。一个功能的技术外溢能力,往往比功能本身更能说明架构的健康度。


定位解析:MVTDataProvider 是"兼容层",不是主航道

这是本文最想表达的一个判断。

看官方在《Help Shape Vector Data Support in 3D Tiles》里怎么描述这个模块的:

We have also begun supporting Mapbox Vector Tiles in CesiumJS, which will reuse the same rendering pipelines as 3D Tiles.

三个关键词:begun(刚起步)、reuse(复用)、as 3D Tiles(并入 3D Tiles 管线)。

再看官方文档对这个模块的标注:

Experimental — This feature is not final and is subject to change without Cesium's standard deprecation policy.

以及教程里那句坦率得近乎可爱的话:

MVTDataProvider currently builds its complete runtime tile hierarchy when it is initialized.(目前是初始化时构建完整瓦片树)

把这三条放在一起,定位就很清楚了:

Cesium 的技术主线是 3D Tiles 2.0 的原生矢量——用 glTF 承载点线面,用 EXT_mesh_features / EXT_structural_metadata 承载 ID 与属性,用 EXT_mesh_polygon 解决"三角形袋子"导致的拓扑丢失,用 KHR_mesh_primitive_restart 高效编码 LINE_STRIP / LINE_LOOP。官方宣称这两个扩展在常见场景下能把 glTF 体积压缩到 1/5。ion 负责把 GeoJSON、GeoPackage、Geodatabase、Shapefile 切片托管,运行时在 CesiumJS / Cesium Native / Cesium for Unreal 消费。

而 MVTDataProvider 是这条主线的侧翼:它服务的不是"未来的 3D 矢量应用",而是"你公司已经存在的那套 MVT 服务"。它存在的意义是让存量数据源零改造接入 Cesium 的 3D 场景,而不是在 Cesium 里复刻一个 MapLibre。

认清这一点,后面的很多"为什么它不支持 XXX"的疑问就自然有答案了——不是做不到,是优先级排在了 3D Tiles 2.0 后面

官方对时间点的表述也很说明问题:3D Tiles 2.0 计划在 2026 年内完成正式批准,矢量瓦片将成为其中的一部分;而矢量瓦片技术预览版里,官方自己列出的后续工作是"扩展样式能力、支持点要素的贴地与样式、增加更多输入格式"。


正面比较:Cesium 的 MVT 与 Mapbox / MapLibre 的 MVT

这是本文的核心章节。两者的差异远不止实现细节,而是两套世界观

维度
Mapbox / MapLibre GL
Cesium MVTDataProvider
瓦片格式
MVT(Protobuf),贯穿始终
MVT 仅作为输入,运行时转码为 glTF,输出是 3D Tiles
坐标系
Web Mercator 平面,瓦片局部坐标
球面/地心坐标,支持地形与真实 3D 高程
渲染方式
专用符号渲染器:栅格化 + 碰撞检测
复用 3D Tiles/glTF 的 GPU 图元管线
样式语言
Style Spec:sources + layers + paint/layout + 完整表达式(interpolate/step/zoom 函数)
Cesium3DTileStyle
color/show/lineWidth/pointSize + conditions 顺序匹配
文字标注
完整支持:glyph 字体栈、sprite 图标、智能避让、沿线排布
不支持
图标/sprites
支持,且是常见符号体系基础
不支持
面样式
填充、描边、图案、虚线、偏移、透明度分级
仅 color + show
数据驱动
表达式引擎 + 相机(zoom/pitch)函数
属性条件表达式,无相机函数
LOD 机制
瓦片金字塔 + maxzoom/minzoom 层级选择
3D Tiles 隐式切片 + 屏幕空间误差(SSE)细化
贴地terrain
 3D 拉伸,靠 DEM
heightReference
 运行时贴地形 和 3D Tiles
3D 能力fill-extrusion
,本质是 2.5D
原生 3D 坐标,厘米~毫米级精度要求可满足
生态定位
2D 地图的最终渲染器
3D 场景中的一个数据源/图层

展开说四个关键差异。

6.1 编码哲学:2D 优化 vs 3D 原生

MVT 的编码是为 2D 地图渲染极致优化的:坐标用相对整数(command 编码),线用 MoveTo/LineTo/ClosePath 指令流,几何在瓦片局部坐标系里以瓦片 extent(通常 4096)为基准。

glTF 承载矢量则是为 GPU 直接消费设计的:顶点是浮点坐标,线是 LINE_STRIP/LINE_LOOP 图元,面是三角化后的 TRIANGLES 加 EXT_mesh_polygon 还原拓扑。官方博客里那句"reduce glTF file size by up to 5x in common cases"说的正是 KHR_mesh_primitive_restart + EXT_mesh_polygon 带来的收益。

代价是体积与语义的取舍:MVT 通常比等价 glTF 更小(因为它放弃了拓扑和精确坐标),而 glTF 换来了"运行时直接喂 GPU"和"完整拓扑"。

6.2 样式语言:这是最深的一道鸿沟

MapLibre 的 style.json 是一个完整的地图外观描述语言:数据源、图层顺序、绘制顺序、字体、sprite、表达式、可见性缩放区间(minzoom/maxzoom)、数据过滤(filter)、要素状态(hover/selected)——一份 JSON 就是一张地图。

Cesium3DTileStyle 是一个逐要素的属性驱动外观控制器

provider.tileset.style = new Cesium.Cesium3DTileStyle({  color: {    conditions: [      ["${class} === 'motorway'", "color('#ff6b35')"],      ["${class} === 'primary'",  "color('#f7c59f')"],      ["true",                    "color('#cccccc', 0.6)"],    ],  },  lineWidth: "${class} === 'motorway' ? 4.0 : 2.0",});

能做的事:属性匹配 → 颜色/线宽/显隐。

做不到的事:interpolate 按数值连续插值、按 zoom 分级、图层叠加顺序、图案填充、虚线、文字。

注意 conditions 的语义是顺序匹配,第一个命中的生效——这和 MapLibre 的表达式求值模型是两种思维。

6.3 渲染路径:符号渲染器 vs 几何渲染器

Mapbox/MapLibre 的渲染器之所以是"世界最好",是因为它专门为地图符号服务:字体图集、图标图集、线宽三角化、斜接/圆角、虚线采样、标注碰撞与优先级、symbol 层的 placement 策略。这些能力没有一项属于通用 GPU 渲染管线,全是地图学专用逻辑。

Cesium 复用的是 3D Tiles 管线,它擅长的是:大规模几何的流式调度、屏幕空间误差细化、GPU 压缩(meshopt)、拾取与元数据。它天生不擅长符号学

所以一个务实的判断是:只要 Cesium 继续复用 3D Tiles 管线,MVT 的符号能力就不会自动补齐;反过来,如果 Cesium 为 MVT 单独写一套符号渲染器,那它就和 MapLibre 收敛成同一类东西了。

6.4 一个反向的维度:Cesium 独有的是 3D 贴附

换个角度看,Cesium 这边也有 MapLibre 给不了的东西:

  • 贴 3D Tiles
    :把设计线位贴到扫描桥上,把 AI 提取的裂缝面贴在实景网格上。官方示例里 Robert Street 桥的裂缝检测就是这个场景——裂缝横跨路面和路侧缘石,二维矢量可视化从原理上就表达不了。
  • 真 3D 坐标
    :官方反复强调的厘米~毫米级精度、非地球参考系(火星、月球)、非 Web Mercator 切片方案(八叉树、k-d 树、DGGS)在极区与垂直结构上的适用性。
  • 语义化 LOD
    :ion 切片时按属性生成 LOD,运行时按属性样式与过滤。

这才是 Cesium 押注矢量的真正理由——不是抢 2D 地图的饭碗,而是把矢量塞进 3D 场景


回到那个问题:Cesium 能渲染出 MapLibre 风格的矢量地图吗?

我给一个分层的答案,而不是一个 Yes/No。

L1|"配色分级 + 显隐过滤"级别 —— ✅ 现在就可以

路网按等级配色、行政边界按属性填色、按属性过滤要素。官方 Philadelphia OSM 路网示例就是这么做的。这已经是绝大多数"业务上要的矢量地图"。

L2|"多图层叠加 + 属性驱动 + 交互"级别 —— ✅ 基本可以,但有取舍

  • 多图层:挂多个 MVTDataProvider 即可,但图层叠加顺序不由 style 控制——Cesium 没有 style.json 那种显式的图层列表,绘制次序取决于 scene.primitives 中的添加顺序、图元自身的渲染顺序以及深度测试关系。矢量瓦片走的是常规前向渲染路径(贴地时是把线段写进纹理、在已有表面的片元着色器里叠加),全程没有离屏渲染环节。这是和 MapLibre 最大的体感差异之一。
  • 属性驱动:conditions 表达式够用,但缺少数值连续插值。
  • 交互:picking + featureIdProperty 让跨瓦片、跨层级的同一要素可被识别,这块做得比很多 2D 方案还干净
  • 性能:注意 maxZoom × extent 的内存约束,show 过滤不省流量。

L3|"直接把一份 MapLibre style.json 丢进去"级别 —— 官方做不到,但 Cesium 平台上已经有人做到了

先说官方 MVTDataProvider 的现状:它做不到。 具体卡在:

  • symbol
     层(文字 + 图标 + 避让)→ 无任何支持;
  • pattern
     填充、line-dasharrayline-offset → 无支持;
  • 相机函数(interpolate 按 zoom)→ 无支持;
  • fill-extrusion
     三维建筑 → Cesium 有自己的 3D Tiles 建筑方案,但不能由 style.json 驱动
  • 图层顺序、minzoom/maxzoom 的图层可见性区间 → 无等价物。

但"官方没做"不等于"Cesium 平台上做不到"。 官方的 MVTDataProvider 定位只是"数据接入 + 最小样式"的兼容层;把一份完整的 MapLibre 样式规范落到 Cesium 的 3D 场景里,需要的是一整套符号渲染器——这件工作在 Cesium 的路线图上排得很靠后,但已经有商业产品把它做完了。

那三条可行的路线

路线 A:等官方补齐(长期正道)

3D Tiles 2.0 年内批准,官方明确说会继续扩展样式能力、补点要素的贴地与样式。随着 3D Tiles 逐渐把矢量原生纳入标准,未来的商业 Tiler 和开源工具(比如已经支持 3D Tiles 输出的 FME、以及各类 GDAL 衍生工具链)都会往这个方向产出数据。这是一条需要耐心但确定性最高的路。

路线 B:写一个 style.json → Cesium3DTileStyle 转换器(中期务实)

把 MapLibre 样式降维翻译:layers 里的 paint 映射到 color/lineWidth/pointSizefilter 映射到 showlayout.visibility 映射到 show必须承认这是有损的——文字、图标、图案、虚线全部丢失。

但有个关键前提:图层对应的要素必须在同一份瓦片源里。MapLibre 的 style.json 引用的是一份复合瓦片源(比如 OpenMapTiles)里的多个 source-layer(water / transportation / place …),而 MVTDataProvider 目前是一个 provider 对应一个 URL 模板。你得为每个 source-layer 建一个 provider,再靠 show 做行级过滤来模拟图层。能跑,但笨重。

路线 C:选用已经实现了符号渲染的 Cesium 矢量引擎(当下可用)

如果 style.json 是硬需求,最务实的做法不是自己造一套符号渲染器,而是直接用已经把它做出来的产品——下一节展开。

已经在 Cesium 上跑通 MapLibre 样式的产品:Mesh-3D

商业产品 Mesh-3D 矢量地图引擎,已经完整实现了"在 Cesium 平台上加载 MVT 矢量瓦片 + 完整兼容 MapLibre 样式规范"这条路径。接入代码只有几行:

const tileset = new Mesh3D.VectorTileset({  style: 'style.json',});viewer.scene.primitives.add(tileset);

它之所以能在 Cesium 里跑通 MapLibre 样式,是因为它没有走"把 MVT 转码成 3D Tiles 图元"这条路,而是在 Cesium 的渲染体系内自建了一套矢量符号渲染管线:MVT 的 PBF 在运行时解码,style.json 的 layers / paint / layout / 表达式直接驱动符号化——字体 glyph、sprite 图标、线宽与虚线、图案填充、图层叠加顺序、按 zoom 的可见性区间,全都按 MapLibre 规范执行,而不是降维到 color + show

换句话说,它把"MapLibre 的样式语言"和"Cesium 的 3D 场景"两件事同时满足了:矢量数据仍然是矢量,样式仍然由 style.json 描述,而承载它的可以是倾斜的相机、起伏的地形和 3D Tiles 内容。

这就回答了本文标题那个问题:能在 Cesium 平台上渲染出 MapLibre 样式的矢量地图吗?——能。 只是这条路不是官方 MVTDataProvider 走出来的,而是需要在 Cesium 之上重建一层符号渲染能力。

选型建议

用 Cesium 原生矢量(ion 矢量瓦片切片 + 3D Tiles 2.0),如果你:

  • 有真 3D 需求:贴在实景网格/建筑上的线面、毫米级工程精度、非地球参考系;
  • 数据量大到必须靠语义化 LOD 流式调度;
  • 已经在用 Cesium 生态,不想引入第二套渲染引擎;
  • 数据源头自己可控(GeoJSON / Shapefile / GeoPackage / Geodatabase → 自己切片)。

用 MVTDataProvider 直连,如果你:

  • 已经有一批 MVT 服务,不想重建切片管线;
  • 需求停在 L1/L2 级别(配色、过滤、拾取);
  • 数据集是区域级的(这是硬约束,别硬扛全球级)。

用 MapLibre,如果你:

  • 业务上地图就是产品主体,不涉及 3D 场景(没有地形、3D Tiles、可以自由旋转的相机);
  • 只需要 2D 平面地图,不存在把矢量放进 3D 空间的需求;
  • 团队熟悉 MapLibre 生态,不想引入 Cesium。

用 Mesh-3D 这类 Cesium 矢量引擎,如果你:

  • 场景已经是 Cesium(有地形、3D Tiles、可自由旋转的相机),同时要求 MapLibre 级的制图效果;
  • 手里有存量 style.json 资产,需要零改造复用;
  • 需要文字标注、图标、图案填充、虚线、图层叠加顺序等符号能力。

附录:术语对照(中英)

文中出现的官方专有名词,首次出现时均标注英文原文,便于对照官方文档与搜索引擎检索。

中文
英文原文
备注
矢量瓦片技术预览版
Vector Tiles Technology Preview
2026-09-02 官方正式发布名
矢量瓦片切片器
vector tiler
Cesium ion 侧切片组件
隐式切片
implicit tiling
3D Tiles 空间索引方式
运行时转码
transcoding (at runtime)
MVT → glTF 的转换过程
拾取
picking
交互查询要素属性
逐要素样式
per-feature styling
Cesium3DTileStyle
 驱动
屏幕空间恒定线宽
screen-space-constant width lines
线宽单位为像素
屏幕空间误差
screen space error (SSE)
3D Tiles 细化阈值
贴附 / 披挂
clamping / draping
heightReference
 控制
零几何 SDF 方案
zero-geometry SDF
VectorCommon.glsl
 的线渲染范式
CSR 加速网格
Compressed Sparse Row (CSR) grid
VectorPipeline.js
 的空间索引
线宽单位
widthUnitspixels
 / meters,1.145 新增
矢量抗锯齿开关
scene.vectorProvider.antialias
关闭可换性能
矢量瓦片
Mapbox Vector Tiles (MVT)
输入格式,Protobuf 编码
贴附目标
terrain / 3D Tiles
CLAMP_TO_TERRAIN
 / CLAMP_TO_3D_TILE

参考资料

  • CesiumJS 1.133 ~ 1.145 版本说明与 CHANGES.md
  • CesiumJS 源码:Source/Core/VectorPipeline.jsSource/Shaders/VectorCommon.glslSource/Shaders/PolylineCommon.glsl
  • 《WebGL 没有几何着色器,那 Cesium 的粗线是怎么画出来的?》(灵境 2050,2026-09-02)——1.141 → 1.145 四条宽线实现路径的源码级拆解
  • Cesium 博客《Vector Tiles: A Technology Preview for Cesium and 3D Tiles》(2026-09-02)
  • Cesium 博客《Help Shape Vector Data Support in 3D Tiles》(2026-06-29)
  • Cesium 官方教程《Load Mapbox Vector Tiles in CesiumJS》《Drape and style vector data on terrain and 3D Tiles》
  • MVTDataProvider
     / UrlTemplate3DTilesDataProvider API 参考
  • CesiumGS/3d-tiles issue #825《Vector data... in glTF?》
  • CesiumGS/cesium issue #13535《MVTDataProvider: Optimizing tileset traversal》
  • CesiumGS/cesium issue #2132《Vector Tiles》(2016)
  • MapLibre Style Specification(sources / layers / expressions)

本文基于公开文档与源码接口整理,MVTDataProvider 仍标记为 Experimental,接口可能在不经过标准弃用流程的情况下变更,生产环境请锁定 CesiumJS 版本。

随机文章