cesiumjs-3d-tiles について
3D Tiles を扱う CesiumJS 開発者向けスキルで、タイルセットの読み込み、スタイル、メタデータ、クリッピング、点群などを解説します。Mapbox ベクタータイルをランタイム 3D Tiles に変換する実験的な方法も扱います。
主な機能
- 013D Tiles セットの読み込みと設定
- 02プロパティに応じたスタイルと表示
- 03フィーチャの選択とメタデータ取得
- 04タイルセットジオメトリのクリッピング
- 05点群、ボクセル、I3S の読み込み
活用シーン
- 013D Tiles コンテンツの表示と調整
- 02プロパティによるフィーチャの着色や絞り込み
- 03選択したフィーチャのメタデータ確認
- 04Mapbox ベクタータイルの読み込み
インストール
コマンドをコピーしてターミナルで実行します。
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 モジュールのインポート、非同期ファクトリーメソッド)。
タイルセットの読み込み
非同期ファクトリーメソッドを必ず使用し、コンストラクターを直接呼び出さないでください。
公開されていてトークンが不要な例では、CesiumGS のサンプルタイルセットなど、URL ベースのタイルセットを優先してください。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 では、タイルセットに埋め込まれた glTF にスタンドアロンモデルの読み込みが適用されます。自動の KHR_meshopt_compression、CAD 拡張機能の動作、サポートされていない平面塗りつぶしの境界については、glTF の互換性マトリックスをお読みください。
// 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 | カメラとタイルセット表面の衝突判定 |
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 に変換します。ベクターデータが元々タイル化されており、単一の GeoJSON プリミティブではなく、3D Tiles のスタイリング、メタデータのピッキング、LOD を利用したい場合に使用してください。
メモリー内または URL ベースの GeoJSON オブジェクトを 1 つ使用する場合は、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は、生成されたタイルセットの表示状態を中継します。- ランタイムのベクター glTF コンテンツは、ドラフト版の
EXT_mesh_polygonと3DTILES_content_gltf_vectorのサポートを使用します。この経路は実験的なものとして扱ってください。
地形へのドレープ(1.144 以降): 地形にクランプされたベクタータイルのポリラインとポリゴンは自動的に地形にドレープされ、線幅は画面空間で一定に保たれます。フィーチャーごとのスタイリングは引き続き Cesium3DTileStyle によって制御されます。有効化フラグは不要です。クランプされたベクターコンテンツは、その下にある地形表面に沿います。
カスタムベクタータイル形式(1.144 以降): MVTDataProvider は、任意の {z}/{x}/{y} URL テンプレート形式のベクターソースをランタイム生成の Cesium3DTileset に変換する公開基底クラス UrlTemplate3DTilesDataProvider を継承するようになりました。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);
読み込み中の UI には loadProgress、キャッシュの動作には 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;
フィーチャーのピッキングとプロパティ
Scene.pick は、3D Tiles のフィーチャーに対して 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 オブジェクト、IntegratedMesh、Building Scene Layer)を読み込みます。
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、タイルセット向けのポストプロセスステージ
- cesiumjs-interaction -- フィーチャー選択用の Scene.pick、drillPick、ScreenSpaceEventHandler
- cesiumjs-terrain-environment -- Globe、地形プロバイダー、大気、ライティング、シャドウ