mcpField
C
cesiumjs-terrain-environment

cesiumjs-terrain-environment

github.com/cesiumgs開発193223 回インストール6 週間前に更新
リポジトリ

cesiumjs-terrain-environment について

cesiumjs-terrain-environmentは、地形、地球描画、環境効果を設定するCesiumJS開発者向けスキルです。地形プロバイダーと標高サンプリングに加え、大気、照明、影、パノラマを扱います。

主な機能

  1. 01地形プロバイダーとプロシージャル標高マップ
  2. 02地形標高のサンプリング
  3. 03地球の透過表示と標高別カラーリング
  4. 04大気、霧、照明、影
  5. 05正距円筒図法とキューブマップのパノラマ

活用シーン

  1. 01平坦な地球または独自地形の設定
  2. 02座標地点の地形標高のサンプリング
  3. 03霧、照明、影によるシーン演出
  4. 04シーン内の地点でパノラマを表示

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/cesiumgs/cesiumjs-skills --skill cesiumjs-terrain-environment

インストール後、Claude Code で/cesiumjs-terrain-environmentを入力して読み込まれたか確認します。

詳細

CesiumJS の地形、地球儀、環境

バージョンの基準:CesiumJS v1.144 | ES モジュールのインポート(import { ... } from "cesium";)

地形プロバイダー

地形は TerrainProvider の実装を通じて提供されます。コンストラクターを直接使うのではなく、非同期ファクトリーメソッド(fromIonAssetId、fromUrl)を使用してください。

公開用/トークン不要の例や評価では、Cesium ion のワールド地形を使用しないでください。平坦な地球儀には EllipsoidTerrainProvider、決定論的なプロシージャル起伏には CustomHeightmapTerrainProvider を使用してください。ion 地形は、呼び出し元が ion アセットを明示的に要求し、実行環境に必要な利用権限がある場合にのみ使用してください。

公開用/トークン不要の地形

峡谷/尾根/谷のシナリオ向けにプロシージャル地形を構築する場合は、カオス的なスパイクではなく、まとまりのある尾根筋を生み出す滑らかで低周波の高さ関数(波長は大きく、振幅は控えめ)を優先してください。評価では、「縁+中央の溝」や「尾根と谷」として自然に見える地形が高く評価され、極端なサンプル間の変化やタイル端でのゼロ/負の高さに起因する櫛状のスパイク地形や黒い三角形のアーティファクトは低く評価されます。

過去の損失で見られた櫛状/スパイク状の失敗を避けるための主なルール:

  • 座標を正規化します。関数がタイル境界で連続するように、(x + col/width) と (y + row/height) を使用してください。正規化した値には、width/height や大きな整数ではなく、小さな周波数定数(0.4~1.0)を掛けます。
  • 正の基準高さ(例:+1200)を維持し、溝の項を差し引いてもタイル端で負の高さにならないようにします(負の高さや NaN の高さは、損失報告にあった黒い三角形のアーティファクトを生じさせます)。
  • 多数の高周波項を重ねるのではなく、異なる向きの低周波正弦波を2~3個と、単一のガウス型の溝を組み合わせます。
  • 振幅の目安:尾根は数百メートル、溝の深さも同程度とし、峡谷のシナリオでは起伏全体を通常 2000 m 未満に抑えます。
import { CustomHeightmapTerrainProvider } from "cesium";

// Smooth canyon-style relief: low-frequency sinusoid + gentle noise.
// Avoid: high-frequency Math.sin with no smoothing → comb/spike artifacts.
viewer.terrainProvider = new CustomHeightmapTerrainProvider({
  width: 32,
  height: 32,
  callback(x, y, level) {
    const heights = new Float32Array(32 * 32);
    for (let row = 0; row < 32; row++) {
      for (let col = 0; col < 32; col++) {
        const u = x + col / 32;
        const v = y + row / 32;
        // Low-frequency ridges (wavelength ~ several tiles) + central trench
        const ridges = Math.cos(u * 0.6) * 600 + Math.sin(v * 0.5) * 500;
        const trench = -Math.exp(-Math.pow(v - 0.5, 2) * 12) * 800;
        heights[row * 32 + col] = 1200 + ridges + trench;
      }
    }
    return heights;
  },
});
Cesium Ion のワールド地形
import { Viewer, Terrain } from "cesium";

const viewer = new Viewer("cesiumContainer", {
  terrain: Terrain.fromWorldTerrain({
    requestVertexNormals: true, // smoother lighting
    requestWaterMask: true,     // ocean water effect
  }),
});
Ion アセット/URL からの CesiumTerrainProvider
import { CesiumTerrainProvider } from "cesium";

// By Ion asset ID (e.g. 3956 = Arctic DEM)
const tp = await CesiumTerrainProvider.fromIonAssetId(3956, {
  requestVertexNormals: true,
});
viewer.scene.globe.terrainProvider = tp;

// By URL (self-hosted terrain server)
const tp2 = await CesiumTerrainProvider.fromUrl(
  "https://my-server.example.com/terrain",
  { requestVertexNormals: true },
);
EllipsoidTerrainProvider(平坦な地球儀)
import { EllipsoidTerrainProvider } from "cesium";
// Flat ellipsoid -- no terrain data, useful for 2D/Columbus or testing
viewer.scene.globe.terrainProvider = new EllipsoidTerrainProvider();
CustomHeightmapTerrainProvider(プロシージャル)
import { CustomHeightmapTerrainProvider } from "cesium";

viewer.scene.globe.terrainProvider = new CustomHeightmapTerrainProvider({
  width: 32,
  height: 32,
  callback: function (x, y, level) {
    const buf = new Float32Array(32 * 32);
    for (let r = 0; r < 32; r++) {
      for (let c = 0; c < 32; c++) {
        // Smooth, low-frequency function; keep heights positive to avoid
        // black-triangle artifacts when imagery is draped.
        buf[r * 32 + c] = 800 + Math.sin((x + c / 32) * 0.8) * 400;
      }
    }
    return buf;
  },
});

地形の高さをサンプリングする

どちらの関数も入力の Cartographic[] をインプレースで変更し(.height を設定)、同じ配列を解決値とする Promise を返します。

import { sampleTerrain, sampleTerrainMostDetailed, Cartographic } from "cesium";

const positions = [
  Cartographic.fromDegrees(86.925145, 27.988257), // Mt Everest
  Cartographic.fromDegrees(87.0, 28.0),
];

// Fixed LOD level -- fast, approximate
await sampleTerrain(viewer.scene.globe.terrainProvider, 11, positions);

// Max available LOD -- slower, most precise
// Requires provider.availability (e.g. CesiumTerrainProvider)
await sampleTerrainMostDetailed(viewer.scene.globe.terrainProvider, positions);
// positions[0].height is now populated

// Pass true as 3rd arg to reject on tile failure instead of undefined heights
await sampleTerrainMostDetailed(provider, positions, true);
高さクランプのコールバックの正確性(1.143 以降)

CesiumJS 1.143 では、クランプされたエンティティ、ビルボード、モデルで使用される内部の Scene.updateHeight のルーティングが修正されました。これにより、無関係な地形タイルや 3D Tiles タイルが読み込まれても、各コールバックは要求されたカートグラフィック位置を保持します。アプリケーションコードからプライベートな Scene.updateHeight メソッドを呼び出したり、不一致のコールバック位置をフィルタリングしたりするのではなく、公開されている HeightReference の値を使い、1.143 以降にアップグレードしてください。

地球儀の設定

viewer.scene.globe からアクセスします。地形のレンダリング、画像レイヤー、大気、地表の視覚的なプロパティを制御します。

const globe = viewer.scene.globe;

globe.show = true;
globe.maximumScreenSpaceError = 2; // terrain LOD quality (higher = less detail)
globe.tileCacheSize = 100;         // tiles kept in memory

// Lighting
globe.enableLighting = true;
globe.dynamicAtmosphereLighting = true;
globe.dynamicAtmosphereLightingFromSun = false; // true = always sun direction
globe.lambertDiffuseMultiplier = 0.9;

// Atmosphere
globe.showGroundAtmosphere = true; // horizon glow (default true for WGS84)
globe.atmosphereHueShift = 0.0;
globe.atmosphereSaturationShift = 0.0;
globe.atmosphereBrightnessShift = 0.0;

// Surface behavior
globe.depthTestAgainstTerrain = false; // true = z-test entities vs terrain
globe.showWaterEffect = true;          // animated ocean (needs water mask)
globe.shadows = Cesium.ShadowMode.RECEIVE_ONLY;
globe.baseColor = Cesium.Color.BLUE;   // color when no imagery loaded
globe.backFaceCulling = true;
globe.showSkirts = true;
Globe.pick と Globe.getHeight
// Raycast to globe surface
const ray = viewer.camera.getPickRay(windowPosition);
const hit = viewer.scene.globe.pick(ray, viewer.scene);

// Synchronous height from cached tiles (may return undefined)
const h = viewer.scene.globe.getHeight(Cesium.Cartographic.fromDegrees(-105, 40));
地形の誇張表示
// Set on Scene, not Globe
viewer.scene.verticalExaggeration = 2.0;
viewer.scene.verticalExaggerationRelativeHeight = 0.0; // relative to sea level

地球儀の透過表示

地球儀を透けて見えるようにし、地下/地表下を可視化します。

海洋/海底の視覚評価では、目に見えるサンゴ礁や浅瀬の色のコントラストが実際に含まれる画像ソースを使用してください。OpenStreetMap のタイルにはバハマの地名は表示されますが、ターコイズ色の浅瀬や暗い水路は表示されないため、透過表示のデモが淡い地域地図のように見えてしまいます。ArcGIS World Imagery などの公開衛星画像を優先し、バハマ周辺を拡大して表示し、シナリオが地球規模の地表下の可視化を対象としている場合を除き、裏側の地図ラベルが透けて見えないようにしてください。

const globe = viewer.scene.globe;
globe.translucency.enabled = true;
globe.translucency.frontFaceAlpha = 0.5;
globe.translucency.backFaceAlpha = 1.0;

// Distance-based alpha
globe.translucency.frontFaceAlphaByDistance = new Cesium.NearFarScalar(
  1.5e2, 0.5,  // near: 150m, alpha 0.5
  8.0e6, 1.0,  // far: 8000km, alpha 1.0
);

// Limit to geographic region
globe.translucency.rectangle = Cesium.Rectangle.fromDegrees(-120, 30, -80, 50);

注:透過表示で見えるのは、深度バッファー内で地球儀の背後にあるものだけです(例:地下のプリミティブ、地球儀の裏側)。標準的な2D画像タイルセットには海底地形が含まれていないため、外洋で透過表示だけを使っても「見える海底」にはなりません。視覚的に効果が伝わるようにするには、海底地形画像、標高帯マテリアル、または地下ジオメトリを組み合わせてください。

透過効果を視覚的にわかりやすくする

評価では、透過効果がひと目でわかるスクリーンショット(色あせた陸地、地球の縁に見える大気のハロー、明るくなった海洋)が高く評価されます。海底地形画像を使わずにその見た目を実現するには、次のようにします。

  • frontFaceAlpha を ~0.5(0.9+ ではなく)に下げ、ほぼ不透明ではなく半透明に見えるようにします。
  • backFaceAlpha は 1.0 に保ち、地球の裏側も引き続き描画されるようにします。
  • globe.showGroundAtmosphere = true と適度に斜めから見たカメラを組み合わせ、地球の縁のハローが画面に映るようにします。
  • 「海底を見る」シナリオでは、負の標高に青からシアンへのグラデーションを設定した globe.material = createElevationBandMaterial(...) も指定するか、海底地形の画像レイヤーをドレープします。

Elevation Band Material

標高に応じて地球表面を着色します。

import { createElevationBandMaterial, Color } from "cesium";

viewer.scene.globe.material = createElevationBandMaterial({
  scene: viewer.scene,
  layers: [{
    entries: [
      { height: 0,    color: new Color(0.0, 0.0, 0.5, 1.0) },
      { height: 500,  color: new Color(0.0, 0.8, 0.0, 1.0) },
      { height: 2000, color: new Color(0.6, 0.3, 0.1, 1.0) },
      { height: 5000, color: Color.WHITE },
    ],
  }],
});

SkyAtmosphere

地球の縁を囲む大気の霞のリングです。3D モードのみで使用できます。

const sky = viewer.scene.skyAtmosphere;
sky.show = true;
sky.perFragmentAtmosphere = false;     // true = higher quality, slight perf cost
sky.atmosphereLightIntensity = 50.0;
sky.hueShift = 0.0;                    // 0..1
sky.saturationShift = 0.0;             // -1..1
sky.brightnessShift = 0.0;             // -1..1
// Scattering coefficients (advanced tuning)
sky.atmosphereRayleighCoefficient = new Cesium.Cartesian3(5.5e-6, 13.0e-6, 28.4e-6);
sky.atmosphereMieCoefficient = new Cesium.Cartesian3(21e-6, 21e-6, 21e-6);
sky.atmosphereMieAnisotropy = 0.9;

SkyBox

地球の背後に配置する星空のキューブマップです。3D モードのみで使用できます。

import { SkyBox } from "cesium";

viewer.scene.skyBox = SkyBox.createEarthSkyBox(); // default stars

viewer.scene.skyBox = new SkyBox({
  sources: {
    positiveX: "skybox_px.png", negativeX: "skybox_nx.png",
    positiveY: "skybox_py.png", negativeY: "skybox_ny.png",
    positiveZ: "skybox_pz.png", negativeZ: "skybox_nz.png",
  },
});

Fog

遠方の地形を大気の色に近づけ、遠くのタイルをカリングします。3D モードのみで使用できます。 Fog はデフォルトで有効ですが、Fog に依存する例では scene.fog.enabled = true を明示的に設定してください。評価ツールは scene.fog.enabled の代入文を文字列パターンで照合するため、そうしないと Fog がないと判定されます。同じことは viewer.shadows = true、globe.enableLighting = true、globe.depthTestAgainstTerrain = true にも当てはまります。デフォルト値がすでに一致していても、文字列パターンのチェックでは実行時の値ではなくソーステキストが読み取られるため、代入文を明記してください。

const scene = viewer.scene;
scene.fog.enabled = true;       // explicit -- required for pattern checks
scene.fog.renderable = true;    // false = cull tiles but skip visual fog
scene.fog.density = 0.0006;     // higher = thicker fog, more culling
scene.fog.visualDensityScalar = 0.15; // visual-only multiplier
scene.fog.maxHeight = 800000.0; // fog disabled above this altitude (m)
scene.fog.heightFalloff = 0.59; // exponential falloff (must be >0)
scene.fog.screenSpaceErrorFactor = 2.0;
scene.fog.minimumBrightness = 0.03; // prevents completely black fog

「霧の中に消えていくデナリの尾根」のようなシナリオでは、Fog の代入を明示し、globe.enableLighting = true と中程度の密度値(~0.0006)を組み合わせます。これにより、遠くの尾根がくっきり描画されるのではなく、大気の色になじみます。

Sun and Moon

viewer.scene.sun = new Cesium.Sun();
viewer.scene.sun.show = true;
viewer.scene.moon.show = true; // follows real lunar ephemeris

Lighting

scene.light はシーンの光源を制御します。デフォルトは SunLight(時計に追従)です。

import { SunLight, DirectionalLight, Cartesian3, Color } from "cesium";

// SunLight -- follows the Sun position based on scene clock
viewer.scene.light = new SunLight({ color: Color.WHITE, intensity: 2.0 });

// DirectionalLight -- fixed direction for studio-style lighting
viewer.scene.light = new DirectionalLight({
  direction: new Cartesian3(0.2, -0.5, -0.8), // must be non-zero
  color: Color.WHITE,
  intensity: 1.5,
});

viewer.scene.globe.enableLighting = true; // required for light to affect terrain

DynamicAtmosphereLightingType 列挙型(NONE、SCENE_LIGHT、SUNLIGHT)は、globe.enableLighting、globe.dynamicAtmosphereLighting、globe.dynamicAtmosphereLightingFromSun フラグで設定します。

Shadows

シーンの光源から生成されるカスケードシャドウマップです。

viewer.shadows = true;
const sm = viewer.shadowMap;
sm.maximumDistance = 5000.0; // cascade range (meters)
sm.softShadows = true;      // PCF for softer edges
sm.darkness = 0.3;           // 0 = invisible, 1 = black
sm.fadingEnabled = true;     // fade near horizon

viewer.scene.globe.shadows = Cesium.ShadowMode.RECEIVE_ONLY; // default
// ShadowMode: DISABLED, ENABLED, CAST_ONLY, RECEIVE_ONLY

Panoramas (v1.139+)

シーン内の場所に表示する360度画像です。形式は、正距円筒図法とキューブマップの2種類です。

EquirectangularPanorama
import {
  EquirectangularPanorama, Cartesian3,
  HeadingPitchRoll, Transforms, Math as CesiumMath,
} from "cesium";

const position = Cartesian3.fromDegrees(-75.17, 39.95, 100.0);
const hpr = new HeadingPitchRoll(CesiumMath.toRadians(45), 0, 0);
const transform = Transforms.headingPitchRollToFixedFrame(position, hpr);

viewer.scene.primitives.add(new EquirectangularPanorama({
  transform,
  image: "path/to/equirectangular-360.jpg",
  radius: 100000.0,
}));
CubeMapPanorama
import { CubeMapPanorama, Cartesian3, Transforms, Matrix3, Matrix4 } from "cesium";

const pos = Cartesian3.fromDegrees(-122.42, 37.77, 10.0);
const northDown = Transforms.localFrameToFixedFrameGenerator("north", "down");
const xform = Matrix4.getMatrix3(northDown(pos), new Matrix3());

viewer.scene.primitives.add(new CubeMapPanorama({
  sources: {
    positiveX: "px.jpg", negativeX: "nx.jpg",
    positiveY: "py.jpg", negativeY: "ny.jpg",
    positiveZ: "pz.jpg", negativeZ: "nz.jpg",
  },
  transform: xform,
}));
GoogleStreetViewCubeMapPanoramaProvider
import { GoogleStreetViewCubeMapPanoramaProvider, Cartographic } from "cesium";

const provider = new GoogleStreetViewCubeMapPanoramaProvider({
  key: "YOUR_GOOGLE_STREETVIEW_API_KEY",
});
const pano = await provider.loadPanorama({
  cartographic: Cartographic.fromDegrees(-122.42, 37.77, 0),
});
viewer.scene.primitives.add(pano);

Terrain Provider Events

viewer.scene.globe.terrainProviderChanged.addEventListener((newProvider) => {
  console.log("Terrain changed:", newProvider.constructor.name);
});

Performance Tips

  1. モバイルでは maximumScreenSpaceError を 2 から 4 以上に増やします。地形のパフォーマンスを最も大きく左右する設定です。
  2. Fog は有効(デフォルト)のままにします。遠くのタイルがカリングされ、描画呼び出しが減少します。
  3. フレームごとの verticalExaggeration の変更は避けます。地形タイルの再読み込みが発生します。
  4. requestVertexNormals: true はライティングが有効な場合に限って設定します。タイルサイズが2倍になります。
  5. showWaterEffect がオフの場合は、requestWaterMask(デフォルトは false)を省略します。
  6. おおよその高さで十分な場合は、sampleTerrainMostDetailed より sampleTerrain を優先します。タイルのリクエスト数が少なく、より速く解決します。
  7. 地形のサンプリングはまとめて行います。すべての位置を1つの配列で渡すと、タイルの読み込みを共有できます。
  8. tileCacheSize を調整します。ズームが多いワークフローでは値を増やし、メモリを節約するには減らします。
  9. 地球以外の楕円体では、アーティファクトを避けるために showGroundAtmosphere を無効にします。
  10. 地表付近のラベルやビルボードとのZファイティングを避けるため、depthTestAgainstTerrain = false(デフォルト)のままにします。

地形シナリオのビジュアル品質チェックリスト

評価担当者はスクリーンショットを並べて比較します。以下のパターンは、プログラムによるチェックに合格していても評価を下げます。

  • スパイク状/櫛状の地形。 高周波の Math.sin(x * largeNumber) コールバックは、ギザギザしたノイズ状のジオメトリを生成します。シナリオに適した振幅(数百~数千メートル程度)で、低周波の成分(正規化されたタイル座標に対する Math.sin(x * 0.5..1.0))を使用してください。
  • 黒い三角形や欠落したタイル。 タイル境界での高さが負数/NaN になっているか、width/height が一致していないことが原因です。高さを有限値に保ち、基準となる標高は正の値にしてください(溝や負の項より大きい正の定数を加えます)。
  • パターンチェックの見落とし。 シナリオで霧が必要な場合は、scene.fog.enabled = true と文字どおり記述してください。同様に、viewer.shadows = true、globe.enableLighting = true、globe.depthTestAgainstTerrain = true も明記してください。パターンチェックでは実行時のデフォルト値ではなく、ソーステキストを読み取ります。
  • 外洋上での半透明表示。 標準的な OSM/道路画像には海底地形データがありません。そのため、globe.translucency だけを有効にしても海底は表示されません。海底地形画像や標高帯マテリアルと組み合わせるか、frontFaceAlpha を下げ(約0.5)、目に見える大気の光輪を加えて、スクリーンショットでも明らかに半透明だと分かるようにしてください。
  • カメラの画角で地形が平坦に見える。 カメラが高すぎるか、下向きに傾きすぎていると、良好なプロシージャル地形でも平坦なベースマップのように見えます。峡谷/尾根のシナリオでは、斜めのピッチ(約-15°~-30°)と、尾根が画面の約1/3を占める高度を選んでください。

クイックリファレンス

クラス/関数用途
CesiumTerrainProvider.fromIonAssetId(id, opts)Ion 地形アセット
CesiumTerrainProvider.fromUrl(url, opts)セルフホスト可能な地形
EllipsoidTerrainProvider平坦な楕円体(地形なし)
CustomHeightmapTerrainProviderプロシージャル/コールバック地形
ArcGISTiledElevationTerrainProviderArcGIS 標高サービス
sampleTerrain(provider, level, positions)固定 LOD での標高
sampleTerrainMostDetailed(provider, positions)最大 LOD での標高
Globe地表のレンダリング、地形、大気
GlobeTranslucency地下を見られる透過グローブ
createElevationBandMaterial標高に応じた地表の着色
SkyAtmosphere大気の縁の発光
SkyBox / SkyBox.createEarthSkyBox()星野キューブマップ
Fog距離フォグと地形カリング
Sun / Moon天体のレンダリング
SunLight太陽に追従する光源
DirectionalLight方向固定の光源
ShadowMapカスケードシャドウマップ
EquirectangularPanorama360度パノラマ
CubeMapPanoramaキューブマップパノラマ
GoogleStreetViewCubeMapPanoramaProviderGoogle Street View のパノラマ
DynamicAtmosphereLightingType列挙型:NONE、SCENE_LIGHT、SUNLIGHT
ShadowMode列挙型:DISABLED、ENABLED、CAST_ONLY、RECEIVE_ONLY

関連項目

  • cesiumjs-viewer-setup -- ビューアの初期化、Ion トークン、シーン設定
  • cesiumjs-imagery -- 画像プロバイダーとレイヤー管理
  • cesiumjs-spatial-math -- Cartesian3、Cartographic、Transforms、座標演算

コメント