1 环境安装

直接下载的cesium最新版本,现在的版本是1.133

在运行的时候要注意,如果使用python http.server,要放在Apps外面,也就是最外层执行。

不过官方推荐是npm。

npm install
npm start

之后访问http://localhost:8080/Apps/Sandcastle/index.html

不过我还是习惯python哈。。。

2 典型的cesium程序

这里以之前的glb模型的例子为例,来自:https://blog.csdn.net/fanged/article/details/151680096

<div id="cesiumContainer"></div>
<script>
  // 初始化 Cesium Viewer
  Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzYjVkMmRhYy03OGMxLTQwM2EtYWY0Ny00MDM4YjhjZmVkNzIiLCJpZCI6MzA0Mzc4LCJpYXQiOjE3NDc3MzE5NDN9.kzP84v1ibzx6iJP_ESqc-PiJ6-fTbHQvCR2KMc9lvws';
  const viewer = new Cesium.Viewer("cesiumContainer", {
    terrainProvider: new Cesium.EllipsoidTerrainProvider()
  });

  // 加载 3D Tiles (指向 rootTileset.json)
  const tileset = new Cesium.Cesium3DTileset({
    url: "data/rootTileset.json"   // 相对路径,index.html 同级目录下的 data 文件夹
  });

  viewer.scene.primitives.add(tileset);

  tileset.readyPromise.then(function() {
  const longitude = 139.7101;
  const latitude = 35.6852;
  const height = 50.0;

  const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);

  // 设置旋转角度(弧度制)
  const heading = Cesium.Math.toRadians(90); // 水平方向
  const pitch   = Cesium.Math.toRadians(0);   // 上下倾斜
  const roll    = Cesium.Math.toRadians(300);   // 翻滚

  const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
  const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(position, hpr);

  // 如果需要缩放,可以加这一句(比如缩小 0.1 倍)
  const scale = 0.1;
  tileset.modelMatrix = Cesium.Matrix4.multiplyByUniformScale(modelMatrix, scale, new Cesium.Matrix4());

  // 相机飞过去
  viewer.zoomTo(tileset);
});

</script>

可以看到,一开始初始化viewer,然后用Cesium3DTileset加载3dtiles模型,之后加到scene.primitives,最后用viewer控制视角。

把cesium的几个核心类简单介绍如下

2.1 Viewer类

作用:一键生成一个完整的地球应用。包含内容:相机、图层管理、UI 控件(比例尺、时间轴、动画控件)、场景(Scene)等。使用场景:绝大多数入门代码都是 new Cesium.Viewer("container") 开始的。

const viewer = new Cesium.Viewer("cesiumContainer", {
  terrain: Cesium.Terrain.fromWorldTerrain()
});

2.2 Cesium.Scene类

作用:表示 3D 场景,控制渲染、光照、雾效、天空盒等。获取方式:viewer.scene
常用属性:
scene.globe → 地球本体(椭球 + 地形)
scene.primitives → 原语(模型、几何体等)
scene.postProcessStages → 后处理特效(比如黑白滤镜、辉光)
低层渲染控制核心。

2.3 Cesium.Camera

作用:控制视角,决定你在地球上的“眼睛”位置。获取方式:viewer.camera
常用方法:
camera.flyTo() → 动画飞行到某个位置
camera.lookAt(target, offset) → 以某点为中心环视
camera.setView() → 立即跳转

2.4 Cesium.Globe

作用:地球本体,包含 椭球 + 影像 + 地形。获取方式:viewer.scene.globe
常用设置:
globe.enableLighting = true → 按太阳光照显示昼夜
globe.depthTestAgainstTerrain = true → 启用地形遮挡效果

2.5 Cesium.Entity & Cesium.EntityCollection

作用:高层抽象的地理对象。
支持内容:点(billboard)、线(polyline)、面(polygon)、模型(3D tiles、GLTF)等。

viewer.entities.add({
  name: "My Point",
  position: Cesium.Cartesian3.fromDegrees(116.39, 39.9, 100),
  point: { pixelSize: 10, color: Cesium.Color.RED }
});

2.6 Cesium.Primitive & Cesium.PrimitiveCollection

作用:更底层的几何渲染对象,比 Entity 更接近 WebGL。用法:一般加载大规模模型(如 3D Tiles)或自定义几何体时用。

const primitive = new Cesium.Primitive({
  geometryInstances: new Cesium.GeometryInstance({ geometry: ... }),
  appearance: new Cesium.PerInstanceColorAppearance()
});
viewer.scene.primitives.add(primitive);

2.7 Cesium.DataSource

作用:外部数据的入口,比如 CZML、GeoJSON、KML。

Cesium.GeoJsonDataSource.load("data.geojson").then(function(ds) {
  viewer.dataSources.add(ds);
  viewer.zoomTo(ds);
});

2.8 Cesium.Cesium3DTileset

作用:3D Tiles 格式的数据集(大规模建筑、点云、城市模型)。

const tileset = await Cesium.Cesium3DTileset.fromUrl("tileset.json");
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);

3 地形代码

这里主要分析的代码是地形,也就是示例代码中的http://127.0.0.1:8000/Apps/Sandcastle/gallery/Terrain.html

核心代码整理如下:

const viewer = new Cesium.Viewer("cesiumContainer", {
  terrain: Cesium.Terrain.fromWorldTerrain({
    requestWaterMask: true,
    requestVertexNormals: true,
  }),
});

// set lighting to true
viewer.scene.globe.enableLighting = true;

// adjust time so scene is lit by sun
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2023-01-01T00:00:00");

// setup alternative terrain providers
const ellipsoidProvider = new Cesium.EllipsoidTerrainProvider();

// Sine wave
const customHeightmapWidth = 32;
const customHeightmapHeight = 32;
const customHeightmapProvider = new Cesium.CustomHeightmapTerrainProvider({
  width: customHeightmapWidth,
  height: customHeightmapHeight,
  callback: function (x, y, level) {
    const width = customHeightmapWidth;
    const height = customHeightmapHeight;
    const buffer = new Float32Array(width * height);

    for (let yy = 0; yy < height; yy++) {
      for (let xx = 0; xx < width; xx++) {
        const u = (x + xx / (width - 1)) / Math.pow(2, level);
        const v = (y + yy / (height - 1)) / Math.pow(2, level);

        const heightValue = 4000 * (Math.sin(8000 * v) * 0.5 + 0.5);

        const index = yy * width + xx;
        buffer[index] = heightValue;
      }
    }

    return buffer;
  },
});

Sandcastle.addToolbarMenu(
  [
    {
      text: "CesiumTerrainProvider - Cesium World Terrain",
      onselect: function () {
        viewer.scene.setTerrain(
          Cesium.Terrain.fromWorldTerrain({
            requestWaterMask: true,
            requestVertexNormals: true,
          }),
        );
        viewer.scene.globe.enableLighting = true;
      },
    },
    {
      text: "CesiumTerrainProvider - Cesium World Terrain - no effects",
      onselect: function () {
        viewer.scene.setTerrain(Cesium.Terrain.fromWorldTerrain());
      },
    },
    {
      text: "CesiumTerrainProvider - Cesium World Terrain w/ Lighting",
      onselect: function () {
        viewer.scene.setTerrain(
          Cesium.Terrain.fromWorldTerrain({
            requestVertexNormals: true,
          }),
        );
        viewer.scene.globe.enableLighting = true;
      },
    },
    {
      text: "CesiumTerrainProvider - Cesium World Terrain w/ Water",
      onselect: function () {
        viewer.scene.setTerrain(
          Cesium.Terrain.fromWorldTerrain({
            requestWaterMask: true,
          }),
        );
      },
    },
    {
      text: "EllipsoidTerrainProvider",
      onselect: function () {
        viewer.terrainProvider = ellipsoidProvider;
      },
    },
    {
      text: "CustomHeightmapTerrainProvider",
      onselect: function () {
        viewer.terrainProvider = customHeightmapProvider;
      },
    },
    {
      text: "VRTheWorldTerrainProvider",
      onselect: function () {
        viewer.scene.setTerrain(
          new Cesium.Terrain(
            Cesium.VRTheWorldTerrainProvider.fromUrl(
              "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/",
              {
                credit: "Terrain data courtesy VT MÄK",
              },
            ),
          ),
        );
      },
    },
    {
      text: "ArcGISTerrainProvider",
      onselect: function () {
        viewer.scene.setTerrain(
          new Cesium.Terrain(
            Cesium.ArcGISTiledElevationTerrainProvider.fromUrl(
              "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
            ),
          ),
        );
      },
    },
  ],
  "terrainMenu",
);

Sandcastle.addDefaultToolbarMenu(
  [
    {
      text: "Mount Everest",
      onselect: function () {
        lookAtMtEverest();
      },
    },
    {
      text: "Half Dome",
      onselect: function () {
        const target = new Cesium.Cartesian3(
          -2489625.0836225147,
          -4393941.44443024,
          3882535.9454173897,
        );
        const offset = new Cesium.Cartesian3(
          -6857.40902037546,
          412.3284835694358,
          2147.5545426812023,
        );
        viewer.camera.lookAt(target, offset);
        viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
      },
    },
    {
      text: "San Francisco Bay",
      onselect: function () {
        const target = new Cesium.Cartesian3(
          -2708814.85583248,
          -4254159.450845907,
          3891403.9457429945,
        );
        const offset = new Cesium.Cartesian3(
          70642.66030209465,
          -31661.517948317807,
          35505.179997143336,
        );
        viewer.camera.lookAt(target, offset);
        viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
      },
    },
  ],
  "zoomButtons",
);

let terrainSamplePositions;

function lookAtMtEverest() {
  const target = new Cesium.Cartesian3(
    300770.50872389384,
    5634912.131394585,
    2978152.2865545116,
  );
  const offset = new Cesium.Cartesian3(
    6344.974098678562,
    -793.3419798081741,
    2499.9508860763162,
  );
  viewer.camera.lookAt(target, offset);
  viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
}

function sampleTerrainSuccess(terrainSamplePositions) {
  const ellipsoid = Cesium.Ellipsoid.WGS84;

  //By default, Cesium does not obsure geometry
  //behind terrain. Setting this flag enables that.
  viewer.scene.globe.depthTestAgainstTerrain = true;

  viewer.entities.suspendEvents();
  viewer.entities.removeAll();

  for (let i = 0; i < terrainSamplePositions.length; ++i) {
    const position = terrainSamplePositions[i];

    viewer.entities.add({
      name: position.height.toFixed(1),
      position: ellipsoid.cartographicToCartesian(position),
      billboard: {
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        scale: 0.7,
        image: "../images/facility.gif",
      },
      label: {
        text: position.height.toFixed(1),
        font: "10pt monospace",
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        pixelOffset: new Cesium.Cartesian2(0, -14),
        fillColor: Cesium.Color.BLACK,
        outlineColor: Cesium.Color.BLACK,
        showBackground: true,
        backgroundColor: new Cesium.Color(0.9, 0.9, 0.9, 0.7),
        backgroundPadding: new Cesium.Cartesian2(4, 3),
      },
    });
  }
  viewer.entities.resumeEvents();
}

function createGrid(rectangleHalfSize) {
  const gridWidth = 41;
  const gridHeight = 41;
  const everestLatitude = Cesium.Math.toRadians(27.988257);
  const everestLongitude = Cesium.Math.toRadians(86.925145);
  const e = new Cesium.Rectangle(
    everestLongitude - rectangleHalfSize,
    everestLatitude - rectangleHalfSize,
    everestLongitude + rectangleHalfSize,
    everestLatitude + rectangleHalfSize,
  );
  const terrainSamplePositions = [];
  for (let y = 0; y < gridHeight; ++y) {
    for (let x = 0; x < gridWidth; ++x) {
      const longitude = Cesium.Math.lerp(e.west, e.east, x / (gridWidth - 1));
      const latitude = Cesium.Math.lerp(e.south, e.north, y / (gridHeight - 1));
      const position = new Cesium.Cartographic(longitude, latitude);
      terrainSamplePositions.push(position);
    }
  }
  return terrainSamplePositions;
}

Sandcastle.addToggleButton(
  "Enable Lighting",
  viewer.scene.globe.enableLighting,
  function (checked) {
    viewer.scene.globe.enableLighting = checked;
  },
);

Sandcastle.addToggleButton(
  "Enable fog",
  viewer.scene.fog.enabled,
  function (checked) {
    viewer.scene.fog.enabled = checked;
  },
);

Sandcastle.addToolbarButton(
  "Sample Everest Terrain at Level 9",
  function () {
    const terrainSamplePositions = createGrid(0.005);
    Promise.resolve(
      Cesium.sampleTerrain(viewer.terrainProvider, 9, terrainSamplePositions),
    ).then(sampleTerrainSuccess);
    lookAtMtEverest();
  },
  "sampleButtons",
);

Sandcastle.addToolbarButton(
  "Sample Most Detailed Everest Terrain",
  function () {
    if (!Cesium.defined(viewer.terrainProvider.availability)) {
      window.alert(
        "sampleTerrainMostDetailed is not supported for the selected terrain provider",
      );
      return;
    }
    const terrainSamplePositions = createGrid(0.0005);
    Promise.resolve(
      Cesium.sampleTerrainMostDetailed(
        viewer.terrainProvider,
        terrainSamplePositions,
      ),
    ).then(sampleTerrainSuccess);
    lookAtMtEverest();
  },
  "sampleButtons",
);

3.1 初始化

const viewer = new Cesium.Viewer("cesiumContainer", {
  terrain: Cesium.Terrain.fromWorldTerrain({
    requestWaterMask: true,
    requestVertexNormals: true,
  }),
});

默认加载 Cesium World Terrain (Cesium ion 提供的全球高程数据)。
requestWaterMask: true → 在水体区域绘制动态水面效果。
requestVertexNormals: true → 用于更真实的地形光照(比如阴影、坡面朝向)。

viewer.scene.globe.enableLighting = true;
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2023-01-01T00:00:00");

打开地形光照(根据太阳位置计算阴影效果)。
固定时间 → 方便调试,不然光照会随着系统时间走。

3.2 地形

初始化的地形是terrain: Cesium.Terrain.fromWorldTerrain

后面提供了几种替换的。分别是

CesiumTerrainProvider - Cesium World Terrain

就是默认地形

CesiumTerrainProvider - Cesium World Terrain - no effects

在模型地形上去掉了requestWaterMask,requestVertexNormals这两个参数。

CesiumTerrainProvider - Cesium World Terrain w/ Lighting

增加了requestVertexNormals: true,,viewer.scene.globe.enableLighting = true;

CesiumTerrainProvider - Cesium World Terrain w/ Water

增加了requestWaterMask: true。

EllipsoidTerrainProvider

const ellipsoidProvider = new Cesium.EllipsoidTerrainProvider();

提供基础球体,所有高程都是基于WGS84,没有高低起伏。

VRTheWorldTerrainProvider

                    Cesium.VRTheWorldTerrainProvider.fromUrl(
                      "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/",
                      {
                        credit: "Terrain data courtesy VT MÄK",
                      },

VT MÄK 公司提供的 VR-TheWorld 地形数据,这是一种高精度的商业地形服务,提供全球范围的高分辨率地形数据。

不过这个好像不是免费的,所以在我这边没有显示。。。

ArcGISTerrainProvider

                    Cesium.ArcGISTiledElevationTerrainProvider.fromUrl(
                      "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
                    ),

Esri(易智瑞)提供的 WorldElevation3D 地形数据,这是一种高质量的全球三维高程地形服务,由知名地理信息公司 Esri 提供支持。

看着和Cesium默认的地形好像差不多。。。

CustomHeightmapTerrainProvider

搞了一个波浪地形。

        const customHeightmapProvider = new Cesium.CustomHeightmapTerrainProvider({
          width: customHeightmapWidth,
          height: customHeightmapHeight,
          callback: function (x, y, level) {
            const width = customHeightmapWidth;
            const height = customHeightmapHeight;
            const buffer = new Float32Array(width * height);

            for (let yy = 0; yy < height; yy++) {
              for (let xx = 0; xx < width; xx++) {
                const u = (x + xx / (width - 1)) / Math.pow(2, level);
                const v = (y + yy / (height - 1)) / Math.pow(2, level);

                const heightValue = 4000 * (Math.sin(8000 * v) * 0.5 + 0.5);

                const index = yy * width + xx;
                buffer[index] = heightValue;
              }
            }

            return buffer;
          },
        });

3.3 跳转

以珠峰为例

function lookAtMtEverest() {
          const target = new Cesium.Cartesian3(
            300770.50872389384,
            5634912.131394585,
            2978152.2865545116,
          );
          const offset = new Cesium.Cartesian3(
            6344.974098678562,
            -793.3419798081741,
            2499.9508860763162,
          );
          viewer.camera.lookAt(target, offset);
          viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);

这里的坐标是 WGS84。

target是中心点,offset是观察角度。。

3.4 其它

高程

Cesium.sampleTerrain(viewer.terrainProvider, 9, terrainSamplePositions),

短时间还用不到这个,所以就不细看了。。。

Sandcastle

演示的辅助类,估计后面也是不会用,跳过。。。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐