cesium加载三维立体primitive图元并贴地展示

文章讲述了在项目中使用Cesium加载大量三维立方体时,由entity切换到primitive图元后场景卡死的问题。作者发现是数据格式问题,尤其是经纬度数组中的NaN导致的。通过排查和调整WKT数据解析方法,解决了场景卡死问题并提供了贴地展示的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目场景:

由于项目里需要加载很多三维立方体,之前用的方式是entity,但是数据量大的话会有点卡顿,于是换成primitive图元的方式加载。

简单记录一下遇到的问题。

问题描述

viewer.scene.primitives.add()添加primitive时,地球卡死了,没办法进行放大缩小拖拽等一系列地球操作。
值得注意的是:当用entity得方式是可以加载出来的,只不过会卡顿,但是也没有遇到过场景scene直接卡死的情况。也就是说可以用entity加载但是一旦换成primitive的方式scene就卡死了。

1.刚开始还以为是几何图形太复杂的问题,但是primitie比entity更接近底层,应该比entity渲染更快才对啊,但是相反entity能加载出来,primitive却卡死了,这也就排除了这换个原因。

2.还以为是电脑GPU的问题但是发现scene场景卡死的时候电脑GPU并没有占用多少,甚至为0%,这也就排除了电脑GPU这一项。


原因分析:

排查数据格式。

我们知道加载多边形primitive需要传入经纬度数组

const geometry = new Cesium.PolygonGeometry({
     polygonHierarchy: new Cesium.PolygonHierarchy(
          new Cesium.Cartesian3.fromDegreesArray(geoms) // geoms为经纬度数组
     ),
     extrudedHeight: entityHeight,
   });

于是就对数据进行一条一条加载。直到遇到scene卡死的那条数据。
知道找到了场景卡死的那条数据:
[107.24044625699061, 31.291311316851022, 107.24047198601231, 31.291311468987722, 107.24047250052004, 31.291311493744068, 107.2404730097246, 31.29131156168811, 107.24047350872203, 31.291311672165513,NaN,NaN,107.24047399270674,31.291311824112327, 107.24047445701771, 31.291312016065213]。
注意里面竟然有两条NaN,这里就排查到了是这条数据的问题,导致加载暂停,场景卡死的。所以我查看了原始的WKT格式的数据。
大概长这样:

"MULTIPOLYGON(((107.25941467940206 31.291917660957814,107.25941751810778 31.291915276197532,107.2594241475583 31.29190534342199,107.25941467940206 31.291917660957814)),((107.25942693970325 31.291901710998584,107.2594272814655 31.291901545782753,107.25942773447713 31.291871994888496,107.25949975585885 31.291838338096827,107.25947575902191 31.291838199756548,107.25942693970325 31.291901710998584)))"

注意:这是一个多重多边形,这条数据里包含了两个polygon,于其他数据不一样,其他数据一个multipolygon只包含了一个polygon数据。
由于是需要我这边手动将wkt转为经纬度数组的。所以看了一下写的方法,代码如下:

 const geoms = item
            .replace('MULTIPOLYGON(((', '')
            .replace(')))', '')
            .replace(/\s/g, ',')
            .split(',')
            .map(item => Number(item));

问题显而易见了,上面这种方法只处理了开头和结尾处的字段,并没有对中间的括号")),((" 进行处理。所以再数组中才会出现NaN这种情况。
从wkt数据里面提取经纬度方法就改为如下:

 const geoms = item
            .replace('MULTIPOLYGON', '')
            .replace(/[()]/g, '')
            .replace(/\s/g, ',')
            .split(',')
            .map(item => Number(item));

这样就能正常渲染加载primitive了

可以看到中间有条线,将两个polygon连为一体了。
我这里没有做处理,这种情况在项目里并不常见。
如果想要处理的话,可以跟后端商量一下谁来调整。

三维primitive贴地展示:

这个根据自己的接口数据类型来调整。由于我这里数据量较大,所以先定位到具体的bbox再进行渲染数据,这里是每100条进行一次渲染。所以采用:
1.先处理所有的wkt数据转为一个二维经纬度数组。每一条数据为一个生成primitive所需的经纬度数据。
2.遍历生成的二维经纬度数组,每100条加载一次数据。
3.由于需要贴地展示,而如果用GroundPrimitive的话,是可以进行贴地的,但是就没有了拉伸高度,所以还需要用Primitie来加载。 如果需要拉伸高度的话需要根据经纬度点查询位置点的地形高度,然后加上你需要拉伸的立方体高度。

// 采用1先漫游到bbox,2再渲染primitive
  async loadGeoJSON(data) {
    const viewer = window.viewer;
    const chunkSize = 100; // 每100条数据加载一次
    console.time();
    //1
    const coords = []; // 存储所有的坐标点,用于生成primitive和计算bbox
    for (let i = 0; i < data.length; i++) {
      const geoms = data[i]
        .replace('MULTIPOLYGON', '')
        .replace(/[()]/g, '')
        .replace(/\s/g, ',')
        .split(',')
        .map(item => Number(item));
      coords.push(geoms);
    }
    viewer.camera.flyTo({
      destination: new Cesium.Rectangle.fromDegrees(...getBBoxByCoords(coords)),
    });
    //2
    for (let index = 0; index < coords.length; index += chunkSize) {
      const chunk = coords.slice(index, index + chunkSize);
      const instances = await Promise.all(
        chunk.map(async item => {
          // 异步根据经纬度获取地形高度
          const height = await getHeigthByLonLat(item[0], item[1]);
          const entityHeight = height + 50;
          const geometry = new Cesium.PolygonGeometry({
            polygonHierarchy: new Cesium.PolygonHierarchy(
              new Cesium.Cartesian3.fromDegreesArray(item)
            ),
            extrudedHeight: entityHeight,
          });
          return new Cesium.GeometryInstance({
            geometry,
          });
        })
      );
      const primitive = new Cesium.Primitive({
        geometryInstances: instances,
        appearance: new Cesium.MaterialAppearance({
          material: new Cesium.Material({
            fabric: {
              type: 'Color',
              uniforms: {
                color: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
              },
            },
          }),
        }),
      });
      viewer.scene.primitives.add(primitive);
    }
    console.timeEnd();
  },
};

根据经纬度查询地形高度:

export const getHeigthByLonLat = async (lon, lat) => {
  var positions = Cesium.Cartographic.fromDegrees(lon, lat);
  const updatedPositions = await Cesium.sampleTerrain(window.viewer.terrainProvider, 13, [
    positions,
  ]);
  if (updatedPositions && updatedPositions.length) {
    return updatedPositions[0].height;
  } else {
    return 0;
  }
};

总结:如果加载primitive,scene卡死,大概率就是数据的问题,只需要进行排查即可。

Cesium中,如果你想要给圆柱图元的侧添加纹理贴图,你需要通过自定义材质(Material)以及设置几何体的方式来进行处理。以下是基本步骤: 1. **创建圆柱实体**:首先需要使用`Cesium.Entity` 或者 `Cesium.Primitive` 创建一个圆柱形状。 2. **加载纹理图片**:通过指定路径加载一张用于映射到圆柱表的纹理图像。可以利用 `czm_texture2D` 来引用该资源,将其应用至材质属性中。 3. **配置UV坐标**:为了将二维平内的图片正确地投影到三维空间中的物体表上,需合理调整其对应的 UV 坐标系参数值范围及方向等信息,使得最终效果能够满足需求。 4. **定义着色器代码片段**:编写GLSL语言描述顶点变换、光照计算规则等内容;同时也要明确如何结合之前准备好的纹理数据完成最后颜色输出过程。 下是一个简单的示例,展示怎样把一幅地图当作侧壁图案应用于标准模型之上: ```javascript var viewer = new Cesium.Viewer('cesiumContainer'); viewer.scene.primitives.add(new Cesium.Primitive({ geometryInstances : new Cesium.GeometryInstance({ geometry : new Cesium.CylinderGeometry({ length : 200000, topRadius : 50000, bottomRadius : 50000 }), attributes:{ textureCoordinates:new Float64Array([ 0, 0,//左下角 1, 0,//右下角 1, 1,//右上角 0, 1//左上角 ]) } }), appearance : new Cesium.MaterialAppearance({ material : Cesium.Material.fromType('Image')({ image : './path/to/your/image.png' }) }) })); ``` 上述代码块演示了向场景内加入具备特定外观特性的基础组件实例的操作流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值