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),确保函数在瓦片边界处连续。将归一化后的值乘以较小的频率常数(0.4 到 1.0),不要乘以 width/height 或较大的整数。
  • 保持正的基准高度(例如 +1200),这样减去沟槽项后,瓦片边缘也不会产生负高度(负数/NaN 高度会产生失利报告中提到的黑色三角形伪影)。
  • 组合 2-3 个方向不同的低频正弦项和一个高斯沟槽,不要叠加许多高频项。
  • 振幅预算:山脊高度为数百米,沟槽深度与之相当;峡谷场景的总起伏通常小于 2000 米。
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,该 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 瓦片加载时,每个回调都会保留其请求的制图位置。 请优先使用公开的 HeightReference 值,并升级到 1.143+,而不要调用私有的 Scene.updateHeight 方法,或在应用代码中过滤位置不匹配的回调。

地球配置

通过 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(...),为负海拔设置蓝色到青色的渐变,或覆盖测深影像图层。

高程带材质

按高程为地球表面着色。

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 },
    ],
  }],
});

天空大气

环绕地球仪边缘的大气雾霾光环。仅限 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;

天空盒

位于地球仪后方的星空立方体贴图。仅限 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",
  },
});

雾效

将远处地形的颜色混合到大气颜色中,并剔除远处瓦片。仅限 3D 模式。 默认启用雾效,但在任何依赖雾效的示例中,都要显式设置 scene.fog.enabled = true——评估器会按字面匹配 scene.fog.enabled 赋值;否则会判定雾效缺失。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

对于“丹纳利山脊逐渐隐入雾中”风格的场景,将显式雾效赋值与 globe.enableLighting = true 以及中等密度值(~0.0006)搭配使用,让远处的山脊线与大气颜色融合,而非清晰呈现。

太阳和月亮

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

光照

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 标志进行配置。

阴影

由场景光源生成的级联阴影贴图。

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

全景图(v1.139+)

场景位置的 360 度影像。支持两种格式:等距矩形和立方体贴图。

等距矩形全景图
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,
}));
立方体贴图全景图
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,
}));
Google 街景立方体贴图全景图提供程序
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);

地形提供程序事件

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

性能提示

  1. 在移动设备上,将 maximumScreenSpaceError 从 2 提高到 4 以上——这是对地形性能影响最大的调节项。
  2. 保持雾效启用(默认值)——它会剔除远处瓦片,减少绘制调用。
  3. 避免每帧更改 verticalExaggeration——这会强制重新加载地形瓦片。
  4. 仅在启用光照时设置 requestVertexNormals: true——这会使瓦片大小翻倍。
  5. showWaterEffect 关闭时,跳过 requestWaterMask(默认值为 false)。
  6. 当近似高度足够时,优先使用 sampleTerrain 而非 sampleTerrainMostDetailed——解析速度更快,且瓦片请求更少。
  7. 批量采样地形——将所有位置放入同一个数组传入,以共享瓦片加载。
  8. 调整 tileCacheSize——频繁缩放的工作流应增大此值;需要节省内存时则应减小。
  9. 在非地球椭球体上禁用 showGroundAtmosphere,以避免伪影。
  10. 保持 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 街景全景图
DynamicAtmosphereLightingType枚举:NONE、SCENE_LIGHT、SUNLIGHT
ShadowMode枚举:DISABLED、ENABLED、CAST_ONLY、RECEIVE_ONLY

另请参阅

  • cesiumjs-viewer-setup -- Viewer 初始化、Ion 令牌、Scene 配置
  • cesiumjs-imagery -- 影像提供程序和图层管理
  • cesiumjs-spatial-math -- Cartesian3、Cartographic、Transforms、坐标数学

评论

支持 Markdown