mcpField
C
cesiumjs-3d-tiles

cesiumjs-3d-tiles

github.com/cesiumgs开发193237 次安装6 周前更新
代码仓库

关于 cesiumjs-3d-tiles

面向 CesiumJS 开发者的 3D Tiles 技能,涵盖瓦片集加载、样式、元数据、裁剪和点云等功能。也介绍将 Mapbox 矢量瓦片转换为运行时 3D Tiles 的实验性路径。

主要功能

  1. 01加载与配置 3D Tiles 瓦片集
  2. 02按属性设置样式与显示状态
  3. 03拾取要素并读取元数据
  4. 04裁剪瓦片集几何
  5. 05加载点云、体素与 I3S

适用场景

  1. 01展示并调整 3D Tiles 内容
  2. 02按要素属性着色或筛选显示
  3. 03检查拾取要素的元数据
  4. 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);

关键构造函数选项

选项默认值用途
maximumScreenSpaceError16LOD 质量阈值(像素)
cacheBytes536870912瓦片缓存修剪目标(字节)
maximumCacheOverflowBytes536870912额外缓存余量
shadowsShadowMode.ENABLED投射/接收阴影
modelMatrixMatrix4.IDENTITY根变换
clippingPlanesundefinedClippingPlaneCollection
clippingPolygonsundefinedClippingPolygonCollection(WebGL 2)
enableCollisionfalse摄像机与 Tileset 表面的碰撞检测
pointCloudShadingundefined点衰减选项对象
classificationTypeundefinedTERRAIN、CESIUM_3D_TILE 或 BOTH
dynamicScreenSpaceErrortrue地平线 LOD 优化
foveatedScreenSpaceErrortrue屏幕中心瓦片优先级
preloadFlightDestinationstrue预取飞行目标处的瓦片
featureIdLabel"featureId_0"EXT_mesh_features ID 集标签
backFaceCullingtrue根据 glTF 材质剔除背面
edgeDisplayModeEdgeDisplayMode.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);

性能提示

  1. 尽可能将 maximumScreenSpaceError 设得较高(默认值为 16;移动设备可设为 32+)。
  2. 对于大型瓦片集的街道级视图,保持 dynamicScreenSpaceError: true。
  3. 保持 foveatedScreenSpaceError: true,以优先加载屏幕中央的瓦片。
  4. 根据设备内存设置 cacheBytes 和 maximumCacheOverflowBytes(默认值均为 512 MB)。
  5. 使用 preloadFlightDestinations: true,预取相机飞行目标处的瓦片。
  6. 对大型替换细化瓦片集启用 skipLevelOfDetail: true,以减少内存占用。
  7. 避免将 maximumScreenSpaceError 设为低于 4——收益递减,瓦片请求数量却会大幅增加。
  8. 对于点云,启用 attenuation 和 eyeDomeLighting,以填补空隙并增强深度感。
  9. 除非需要相机碰撞或对瓦片使用 CLAMP_TO_GROUND,否则保持 enableCollision: false。
  10. 使用 show: false 和 preloadWhenHidden: true 预加载隐藏的瓦片集。
  11. 尽可能避免使用半透明样式——它们会增加渲染通道并禁用优化。
  12. 监听 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、地形提供程序、大气、光照、阴影

评论

支持 Markdown