关于 cesiumjs-3d-tiles
面向 CesiumJS 开发者的 3D Tiles 技能,涵盖瓦片集加载、样式、元数据、裁剪和点云等功能。也介绍将 Mapbox 矢量瓦片转换为运行时 3D Tiles 的实验性路径。
主要功能
- 01加载与配置 3D Tiles 瓦片集
- 02按属性设置样式与显示状态
- 03拾取要素并读取元数据
- 04裁剪瓦片集几何
- 05加载点云、体素与 I3S
适用场景
- 01展示并调整 3D Tiles 内容
- 02按要素属性着色或筛选显示
- 03检查拾取要素的元数据
- 04加载 Mapbox 矢量瓦片
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cesiumgs/ cesiumjs-skills --skill cesiumjs-3d-tiles安装后在 Claude Code 中输入/cesiumjs-3d-tiles确认已加载。
详细介绍
CesiumJS 3D Tiles
基准版本:CesiumJS v1.144(ES 模块导入、异步工厂方法)。
加载 Tileset
始终使用异步工厂方法——切勿直接调用构造函数。
对于公开/无需令牌的示例,优先使用基于 URL 的 Tileset,例如 CesiumGS 示例
Tileset。fromIonAssetId、createOsmBuildingsAsync 和 Google
Photorealistic 3D Tiles 需要外部授权;只有在调用方明确要求使用这些服务且运行时已配置好时才使用。
import { Cesium3DTileset, HeadingPitchRange, Math as CesiumMath } from "cesium";
// From a URL
const tileset = await Cesium3DTileset.fromUrl(
"https://example.com/tileset.json",
{ maximumScreenSpaceError: 16 }, // lower = higher quality
);
viewer.scene.primitives.add(tileset);
await viewer.zoomTo(tileset, new HeadingPitchRange(
0.0, CesiumMath.toRadians(-25.0), tileset.boundingSphere.radius * 2.0,
));
CesiumJS 1.143 将独立模型加载机制应用于 Tileset 中嵌入的 glTF。
请阅读 glTF 兼容性矩阵,了解自动处理 KHR_meshopt_compression、CAD 扩展的行为,以及不支持的平面填充边界。
// From Cesium ion
const tileset = await Cesium3DTileset.fromIonAssetId(75343);
viewer.scene.primitives.add(tileset);
// Google Photorealistic 3D Tiles
import { createGooglePhotorealistic3DTileset } from "cesium";
const google3D = await createGooglePhotorealistic3DTileset({
onlyUsingWithGoogleGeocoder: true,
});
viewer.scene.primitives.add(google3D);
// OSM Buildings
import { createOsmBuildingsAsync } from "cesium";
const osmBuildings = await createOsmBuildingsAsync();
viewer.scene.primitives.add(osmBuildings);
关键构造函数选项
| 选项 | 默认值 | 用途 |
|---|---|---|
maximumScreenSpaceError | 16 | LOD 质量阈值(像素) |
cacheBytes | 536870912 | 瓦片缓存修剪目标(字节) |
maximumCacheOverflowBytes | 536870912 | 额外缓存余量 |
shadows | ShadowMode.ENABLED | 投射/接收阴影 |
modelMatrix | Matrix4.IDENTITY | 根变换 |
clippingPlanes | undefined | ClippingPlaneCollection |
clippingPolygons | undefined | ClippingPolygonCollection(WebGL 2) |
enableCollision | false | 摄像机与 Tileset 表面的碰撞检测 |
pointCloudShading | undefined | 点衰减选项对象 |
classificationType | undefined | TERRAIN、CESIUM_3D_TILE 或 BOTH |
dynamicScreenSpaceError | true | 地平线 LOD 优化 |
foveatedScreenSpaceError | true | 屏幕中心瓦片优先级 |
preloadFlightDestinations | true | 预取飞行目标处的瓦片 |
featureIdLabel | "featureId_0" | EXT_mesh_features ID 集标签 |
backFaceCulling | true | 根据 glTF 材质剔除背面 |
edgeDisplayMode | EdgeDisplayMode.SURFACES_ONLY | 存在 glTF 边缘可见性数据时进行渲染 |
将 Mapbox Vector Tiles 用作运行时 3D Tiles(实验性,1.142+)
MVTDataProvider 加载 {z}/{x}/{y} Mapbox Vector Tile .mvt/.pbf
模板,并将瓦片载荷转换为运行时 3D Tiles。当矢量数据天然按瓦片组织,且你希望使用 3D Tiles 样式、元数据拾取和 LOD,而不是单个 GeoJSON 图元时,可使用它。
对于单个内存中或基于 URL 的 GeoJSON 对象,优先使用
cesiumjs-primitives 中的 GeoJsonPrimitive。对于 Entity/DataSource 便捷功能,优先使用 cesiumjs-entities 中的
GeoJsonDataSource。
import {
Cesium3DTileStyle,
MVTDataProvider,
Rectangle,
} from "cesium";
const provider = await MVTDataProvider.fromUrl(
"https://example.com/tiles/{z}/{x}/{y}.pbf",
{
minZoom: 4,
maxZoom: 14,
extent: Rectangle.fromDegrees(-125, 24, -66, 50),
featureIdProperty: "id",
},
);
viewer.scene.primitives.add(provider);
// The provider owns a generated Cesium3DTileset.
provider.tileset.style = new Cesium3DTileStyle({
color: {
conditions: [
["${kind} === 'park'", "color('seagreen', 0.65)"],
["${kind} === 'water'", "color('steelblue', 0.55)"],
["true", "color('white', 0.45)"],
],
},
});
要素属性以 EXT_structural_metadata 编码,因此可使用标准
3D Tiles 样式和拾取模式:
const picked = viewer.scene.pick(windowPosition);
if (picked && typeof picked.getProperty === "function") {
console.log(picked.getProperty("name"));
}
说明:
- URL 模板必须包含
{z}、{x}和{y}占位符;瓦片 URL 从/z/x/y中解析。 - 空的 204/404 瓦片会被视为缺失,而不会导致硬性失败。
provider.show会将可见性代理到生成的 Tileset。- 运行时矢量 glTF 内容使用草案版
EXT_mesh_polygon和3DTILES_content_gltf_vector支持;请将此路径视为实验性功能。
**地形贴合(1.144+):**贴地的矢量瓦片折线和多边形会自动贴合地形,线宽保持屏幕空间恒定,且每个要素的样式仍由 Cesium3DTileStyle 控制。无需启用选项;贴地矢量内容会沿其下方的地形表面延伸。
自定义矢量瓦片格式(1.144+):MVTDataProvider 现在继承自
UrlTemplate3DTilesDataProvider。这是一个公开基类,可将任意
{z}/{x}/{y} URL 模板矢量源转换为运行时生成的
Cesium3DTileset。其 fromUrl、tileset、show、extent 和
minZoom/maxZoom 选项的行为与 MVTDataProvider 相同;你可以继承此类并实现其受保护的编解码器钩子,以支持 MVT 以外的分块矢量格式。
瓦片集事件与渲染就绪状态
fromUrl 会在瓦片集元数据可用时解析;这并不意味着当前相机视图中的瓦片已完成渲染。initialTilesLoaded 仅在首次视图加载完成时触发,而 allTilesLoaded 和 tilesLoaded 则取决于当前视图。在调用 zoomTo、flyTo、setView 或交互式移动相机后,请再次检查就绪状态。不要用固定延迟代替这一语义条件。
function waitForTilesetView(viewer, tileset, timeoutMs = 30_000) {
return new Promise((resolve, reject) => {
const scene = viewer.scene;
let readyFrames = 0;
const remove = scene.postRender.addEventListener(() => {
readyFrames = tileset.tilesLoaded ? readyFrames + 1 : 0;
if (readyFrames < 2) {
scene.requestRender();
return;
}
clearTimeout(timeoutId);
remove();
resolve(tileset);
});
const timeoutId = setTimeout(() => {
remove();
reject(new Error(`Tileset did not load within ${timeoutMs} ms`));
}, timeoutMs);
scene.requestRender();
});
}
await viewer.zoomTo(tileset);
await waitForTilesetView(viewer, tileset);
使用 loadProgress 显示加载 UI,使用 tileLoad/tileUnload 跟踪缓存活动,并使用 tileFailed 进行诊断。不要将单个 tileLoad 事件视为当前视图已加载完成的证明。
import { Color } from "cesium";
// Per-frame manual styling
tileset.tileVisible.addEventListener((tile) => {
const content = tile.content;
for (let i = 0; i < content.featuresLength; i++) {
content.getFeature(i).color = Color.fromRandom();
}
});
运行时属性
import { Matrix4, Cartesian3 } from "cesium";
tileset.show = false; // toggle visibility
tileset.maximumScreenSpaceError = 8; // increase quality
const { center, radius } = tileset.boundingSphere;
tileset.modelMatrix = Matrix4.fromTranslation(new Cartesian3(0, 0, 100));
声明式样式
将 Cesium3DTileStyle 赋给 tileset.style。表达式使用 ${PropertyName} 引用要素属性。
样式 DSL 限制:
- 样式表达式语言不支持
defined();使用它会导致渲染错误。 - 如果引用了瓦片集数据中不存在的属性(例如,在没有高度属性的瓦片集中使用
${Height}),样式求值会中止并触发 Cesium 错误面板。请始终将["true", "..."]作为最后一个条件,以便捕获其他情况。 - 要重置样式,请赋值
tileset.style = undefined。
import { Cesium3DTileStyle } from "cesium";
// Color by height conditions -- requires tileset to have a 'Height' property
tileset.style = new Cesium3DTileStyle({
color: {
conditions: [
["${Height} >= 100", "color('purple', 0.5)"],
["${Height} >= 50", "color('red')"],
["true", "color('blue')"], // catch-all: always include this
],
},
show: "${Height} > 0",
});
// Safe constant style -- works on any tileset regardless of metadata
tileset.style = new Cesium3DTileStyle({
color: {
conditions: [
["true", "color('cyan', 1.0)"],
],
},
});
// Use defines to simplify repeated sub-expressions
tileset.style = new Cesium3DTileStyle({
defines: { material: "${feature['building:material']}" },
color: {
conditions: [
["${material} === null", "color('white')"],
["${material} === 'glass'", "color('skyblue', 0.5)"],
["${material} === 'brick'", "color('indianred')"],
["true", "color('white')"],
],
},
});
// Show/hide by property
tileset.style = new Cesium3DTileStyle({
show: "${feature['building']} === 'office'",
});
// Point cloud styling
tileset.style = new Cesium3DTileStyle({
color: "vec4(${Temperature})",
pointSize: "${Temperature} * 2.0",
});
tileset.style = undefined; // reset to default appearance
颜色混合模式
import { Cesium3DTileColorBlendMode } from "cesium";
tileset.colorBlendMode = Cesium3DTileColorBlendMode.REPLACE; // HIGHLIGHT | REPLACE | MIX
tileset.colorBlendAmount = 0.5; // only used with MIX
边缘显示模式(实验性,1.142+)
edgeDisplayMode 控制由草案 glTF 扩展 EXT_mesh_primitive_edge_visibility 提供的边缘。无论此设置如何,没有该扩展的瓦片都会正常渲染。
import { Cesium3DTileset, EdgeDisplayMode } from "cesium";
const tileset = await Cesium3DTileset.fromUrl("/cad/tileset.json", {
edgeDisplayMode: EdgeDisplayMode.SURFACES_AND_EDGES,
});
viewer.scene.primitives.add(tileset);
// CAD-style wireframe for content that carries edge-visibility data.
tileset.edgeDisplayMode = EdgeDisplayMode.EDGES_ONLY;
// Default rendering: hide extension-provided edges.
tileset.edgeDisplayMode = EdgeDisplayMode.SURFACES_ONLY;
要素拾取与属性
对于 3D Tiles 要素,Scene.pick 会返回 Cesium3DTileFeature。所做的修改会一直保留,直到所属瓦片从缓存中移除。
import {
ScreenSpaceEventHandler, ScreenSpaceEventType,
Cesium3DTileFeature, Color,
} from "cesium";
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
// Hover: read properties
handler.setInputAction((movement) => {
const feature = viewer.scene.pick(movement.endPosition);
if (feature instanceof Cesium3DTileFeature) {
const ids = feature.getPropertyIds();
for (const id of ids) console.log(`${id}: ${feature.getProperty(id)}`);
feature.color = Color.YELLOW; // highlight
}
}, ScreenSpaceEventType.MOUSE_MOVE);
// Click: inspect a single property
handler.setInputAction((movement) => {
const feature = viewer.scene.pick(movement.position);
if (feature instanceof Cesium3DTileFeature) {
console.log("Height:", feature.getProperty("Height"));
feature.setProperty("selected", true); // write custom property
feature.show = false; // hide individual feature
}
}, ScreenSpaceEventType.LEFT_CLICK);
继承的元数据(3D Tiles 1.1 / EXT_structural_metadata)
// Searches: batch table -> content -> tile -> subtree -> group -> tileset
const value = feature.getPropertyInherited("semanticOrPropertyName");
裁剪平面
ClippingPlaneCollection 使用瓦片集局部坐标系中的半空间平面进行裁剪。
import {
ClippingPlane, ClippingPlaneCollection,
Cartesian3, Color, Matrix4,
} from "cesium";
const clippingPlanes = new ClippingPlaneCollection({
planes: [new ClippingPlane(new Cartesian3(0.0, 0.0, -1.0), 0.0)],
edgeWidth: 1.0,
edgeColor: Color.WHITE,
unionClippingRegions: false, // false = intersection (AND); true = union (OR)
});
const tileset = await Cesium3DTileset.fromUrl(url, { clippingPlanes });
// Or: tileset.clippingPlanes = clippingPlanes;
// Offset the clip boundary at runtime
clippingPlanes.modelMatrix = Matrix4.fromTranslation(new Cartesian3(0, 0, 50));
clippingPlanes.get(0).distance = 25.0;
裁剪多边形
ClippingPolygonCollection 使用任意多边形进行裁剪。仅限 WebGL 2。
import { ClippingPolygon, ClippingPolygonCollection, Cartesian3 } from "cesium";
const polygon = new ClippingPolygon({
positions: Cartesian3.fromDegreesArray([
-105.0077, 39.7519, -105.0095, 39.7504,
-105.0071, 39.7513, -105.0077, 39.7519,
]),
});
tileset.clippingPolygons = new ClippingPolygonCollection({
polygons: [polygon],
inverse: false, // false = clip inside polygon; true = clip outside
});
// Also works on the globe
viewer.scene.globe.clippingPolygons = new ClippingPolygonCollection({
polygons: [polygon],
});
点云着色
const tileset = await Cesium3DTileset.fromUrl(pointCloudUrl, {
pointCloudShading: {
attenuation: true, // scale points by geometric error
geometricErrorScale: 1.0,
maximumAttenuation: 10, // max pixel size; undefined = maximumScreenSpaceError
eyeDomeLighting: true, // depth-aware edge enhancement
eyeDomeLightingStrength: 1.0,
eyeDomeLightingRadius: 1.0,
backFaceCulling: false, // requires normals in point data
normalShading: true,
},
});
viewer.scene.primitives.add(tileset);
// Runtime adjustment
tileset.pointCloudShading.eyeDomeLightingStrength = 2.0;
体素图元
VoxelPrimitive 使用 Cesium3DTilesVoxelProvider 渲染体积数据。形状:BOX、CYLINDER、ELLIPSOID(参见 VoxelShapeType)。
import {
VoxelPrimitive, Cesium3DTilesVoxelProvider,
CustomShader, viewerVoxelInspectorMixin,
} from "cesium";
const provider = await Cesium3DTilesVoxelProvider.fromUrl("voxel/tileset.json");
const voxelPrimitive = new VoxelPrimitive({
provider,
customShader: new CustomShader({
fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
material.diffuse = fsInput.metadata.a.rgb;
material.alpha = fsInput.metadata.a.a;
}`,
}),
});
viewer.scene.primitives.add(voxelPrimitive);
voxelPrimitive.nearestSampling = true;
viewer.camera.flyToBoundingSphere(voxelPrimitive.boundingSphere, { duration: 0 });
// For voxel shader authoring — struct availability, raymarching semantics, metadata
// access — see the cesiumjs-custom-shader skill. This skill covers VoxelPrimitive setup.
// Optional inspector widget
viewer.extend(viewerVoxelInspectorMixin);
viewer.voxelInspector.viewModel.voxelPrimitive = voxelPrimitive;
I3S 数据提供程序
加载 Esri I3S 场景图层(3D 对象、集成网格、建筑场景图层)。
import { I3SDataProvider, ArcGISTiledElevationTerrainProvider, Ellipsoid, Rectangle } from "cesium";
const geoidService = await ArcGISTiledElevationTerrainProvider.fromUrl(
"https://tiles.arcgis.com/tiles/.../EGM2008/ImageServer",
);
const i3sProvider = await I3SDataProvider.fromUrl(
"https://tiles.arcgis.com/tiles/.../SceneServer/layers/0",
{ geoidTiledTerrainProvider: geoidService },
);
viewer.scene.primitives.add(i3sProvider);
const center = Rectangle.center(i3sProvider.extent);
center.height = 5000.0;
viewer.camera.setView({
destination: Ellipsoid.WGS84.cartographicToCartesian(center),
});
高斯泼溅
作为标准 3D Tiles 加载;CesiumJS 会自动处理 KHR_gaussian_splatting。
const splats = await Cesium3DTileset.fromIonAssetId(3667783);
viewer.scene.primitives.add(splats);
viewer.zoomTo(splats);
分类
将瓦片集几何体作为分类叠加层投射到地形或其他瓦片集上。
import { Cesium3DTileset, ClassificationType } from "cesium";
const classified = await Cesium3DTileset.fromUrl(url, {
classificationType: ClassificationType.BOTH, // TERRAIN | CESIUM_3D_TILE | BOTH
});
viewer.scene.primitives.add(classified);
调整瓦片集高度
import { Cartographic, Cartesian3, Matrix4 } from "cesium";
const cartographic = Cartographic.fromCartesian(tileset.boundingSphere.center);
const surface = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
const offset = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
const translation = Cartesian3.subtract(offset, surface, new Cartesian3());
tileset.modelMatrix = Matrix4.fromTranslation(translation);
性能提示
- 尽可能将
maximumScreenSpaceError设得较高(默认值为 16;移动设备可设为 32+)。 - 对于大型瓦片集的街道级视图,保持
dynamicScreenSpaceError: true。 - 保持
foveatedScreenSpaceError: true,以优先加载屏幕中央的瓦片。 - 根据设备内存设置
cacheBytes和maximumCacheOverflowBytes(默认值均为 512 MB)。 - 使用
preloadFlightDestinations: true,预取相机飞行目标处的瓦片。 - 对大型替换细化瓦片集启用
skipLevelOfDetail: true,以减少内存占用。 - 避免将
maximumScreenSpaceError设为低于 4——收益递减,瓦片请求数量却会大幅增加。 - 对于点云,启用
attenuation和eyeDomeLighting,以填补空隙并增强深度感。 - 除非需要相机碰撞或对瓦片使用 CLAMP_TO_GROUND,否则保持
enableCollision: false。 - 使用
show: false和preloadWhenHidden: true预加载隐藏的瓦片集。 - 尽可能避免使用半透明样式——它们会增加渲染通道并禁用优化。
- 监听
tileFailed记录错误;相机大幅移动后调用trimLoadedTiles()。
另请参阅
- cesiumjs-models-particles —— 用于瓦片内容的 glTF 压缩和 CAD 扩展兼容性
- cesiumjs-custom-shader —— 为
Cesium3DTileset.customShader和VoxelPrimitive.customShader编写 GLSL(结构体参考、要素 ID、元数据) - cesiumjs-materials-shaders —— ImageBasedLighting、用于 tileset 的后期处理阶段
- cesiumjs-interaction —— 用于要素选择的 Scene.pick、drillPick、ScreenSpaceEventHandler
- cesiumjs-terrain-environment —— Globe、地形提供程序、大气、光照、阴影