cesium裁剪

605 阅读4分钟

1.cesium裁剪重要使用如下api

 new Cesium.ClippingPlaneCollection(options)

image.png 这是一个裁剪平面集合,里面主要参数是多个裁剪面

Cesium.ClippingPlane

裁剪面是如何确定的呢?

cesium中要确定一个裁剪面需要一个法向量normal,和一个到原点的距离distance确定。 首先是法向量,法向量是垂直于裁剪面,只要确定了法向量,裁剪面的方向就定下来了。但是垂直于法向量的面有无数个,如下图

image.png 红色箭头线就是一个法量,下边的四边形就垂直于该法向量。有无数个和四边形平行的平面,都垂直于这个法向量,只是他们距离法向量起点的距离不同。 如果红色箭头的底部是原点,那么1.法向量为红色箭头 2.距离原点距离为0的平面就是一个确定的平面。 反过来,如果要确定一个平面需要知道1.法向量和2.距离坐标原点的距离。

法向量求解:

还是参考上边的图,法向量可以由两个向量叉乘得到(线性代数),平面上不共线的三个点,就可以确定两个向量。 distance翻译意思如下:从原点到平面的最短距离。距离的符号决定原点位于平面的哪一侧。如果距离为正,则原点位于法线方向的半空间内;如果为负,则原点位于与法线相反的半空间中;如果为零,则平面通过原点。

在cesium中的裁剪面api使用如下

// This clipping plane's distance is positive, which means its normal
// is facing the origin. This will clip everything that is behind
// the plane, which is anything with y coordinate < -5.
const clippingPlanes = new Cesium.ClippingPlaneCollection({
    planes : [
        new Cesium.ClippingPlane(new Cesium.Cartesian3(1.0, 1.0, 0.0), 0.0)
    ],
});
// Create an entity and attach the ClippingPlaneCollection to the model.
const entity = viewer.entities.add({
    position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 10000),
    model : {
        uri : 'model.gltf',
        minimumPixelSize : 128,
        maximumScale : 20000,
        clippingPlanes : clippingPlanes
    }
});
viewer.zoomTo(entity);

这里直接写死了法向量为

image.png new Cesium.Cartesian3(1.0, 0.0, 0.0)

距离原点距离为0. 以我自己的理解,new Cesium.ClippingPlane(new Cesium.Cartesian3(1.0, 0.0, 0.0), 0.0)可以做如下解释,1.裁剪平面的法向量就是y轴正方向(距离原点为0,法向量是y轴的平面,以下我称为y0平面)。2.将y0平面沿着y轴平移0个单位,平移方向和法向量指向相反。(原理还不清楚)

修改距离为700

image.png

修改距离为-700

image.png

distance从-700到0,再到700,如果法向量为正,裁剪面可视范围在变大,地形看起来是往左边在扩张

反着法向量方向走

2.挖地形

可以利用以上api进行地形开挖,官方例子如下

   planes: [
      new Cesium.ClippingPlane(
        new Cesium.Cartesian3(1.0, 0.0, 0.0),
        -700.0
      ),
      new Cesium.ClippingPlane(
        new Cesium.Cartesian3(-1.0, 0.0, 0.0),
        -700.0
      ),
      new Cesium.ClippingPlane(
        new Cesium.Cartesian3(0.0, 1.0, 0.0),
        -700.0
      ),
      new Cesium.ClippingPlane(
        new Cesium.Cartesian3(0.0, -1.0, 0.0),
        -700.0
      ),
    ],

image.png 地形分割例2

  var terrainModels = Cesium.createWorldTerrain();
    var viewer = new Cesium.Viewer("cesiumContainer", {
      terrainProvider: terrainModels,
      animation: false, //是否显示动画工具
      timeline: false,  //是否显示时间轴工具
      fullscreenButton: false,  //是否显示全屏按钮工具
    });
    //#endregion

    //开启深度检测
    viewer.scene.globe.depthTestAgainstTerrain = true;
    //调整相机视角
    viewer.scene.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(114.39564, 30.52214, 2000),
    });


 
    //实例化ScreenSpaceEventHandler对象
    var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

DrawClippingPlane(
  [
  new Cesium.Cartesian3(
         -2271462.4715688457,
         5008132.491340798,
         3220219.1648368714
  ),
    
      new Cesium.Cartesian3(
       -2271809.8402533564,
       5008216.286909559,
       3219846.2909253053
      ),
      new Cesium.Cartesian3(
         -2271940.7657159385,
         5008006.534266515,
        3220078.5847187885
    )
]
)
    //封装挖地形函数
    function DrawClippingPlane(points) {
      var pointsLength = points.length;
      var clippingPlanes = []; // 存储ClippingPlane集合

      //计算裁剪面
      for (var i = 0; i < pointsLength; ++i) {
        var nextIndex = (i + 1) % pointsLength;

        //计算两个笛卡尔的按分量求和
        var midpoint = Cesium.Cartesian3.add(points[i], points[nextIndex], new Cesium.Cartesian3());
        //缩放笛卡尔坐标
        midpoint = Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint);
        //计算提供的笛卡尔坐标系的标准化形式
        var up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3());

        //计算两个笛卡尔的分量差异
        var right = Cesium.Cartesian3.subtract(points[nextIndex], midpoint, new Cesium.Cartesian3());
        //计算提供的笛卡尔坐标系的标准化形式
        right = Cesium.Cartesian3.normalize(right, right);

        //叉乘求法向量
        var normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3());
        //计算提供的笛卡尔坐标系的标准化形式
        normal = Cesium.Cartesian3.normalize(normal, normal);

        //原始中心平面
        var originCenteredPlane = new Cesium.Plane(normal, 0.0);
        //计算点到平面的有符号最短距离
        var distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint);

        clippingPlanes.push(new Cesium.ClippingPlane(normal, distance));
      }

      //创建ClippingPlaneCollection对象
      var ClippingPlaneCollectionObj = new Cesium.ClippingPlaneCollection({
        planes: clippingPlanes,
        edgeWidth: 1.0,
        edgeColor: Cesium.Color.RED
      });

      //赋值给globe的clippingPlanes
      viewer.scene.globe.clippingPlanes = ClippingPlaneCollectionObj;
     
    }

接着上边,给挖开的地方四周加墙

var maximumHeights = [];
  for (var i = 0; i < points.length+1; i++) {
    maximumHeights.push(22);
  }
  const points2=[...points,points[0]];
viewer.entities.add({
    wall: {
      positions:points2,
      material: new Cesium.ImageMaterialProperty({
        image: "./ce.jpg",
        repeat: new Cesium.Cartesian2(points2.length, 1.0)
      }),
      maximumHeights: maximumHeights,
      minimumHeights: new Cesium.CallbackProperty(function () {
        var minimumHeights = [];
        for (var i = 0; i < points2.length+1; i++) {
          minimumHeights.push(-50);
        }
        return minimumHeights;
      }, false),
    }
  });

image.png

在填充底面

   viewer.entities.add({
    polygon: {
      hierarchy: new Cesium.PolygonHierarchy(points2),
      material: new Cesium.ImageMaterialProperty({
        image: "./di.jpg",
        repeat: new Cesium.Cartesian2(points2.length / 2, points2.length / 2)
      }),
      closeTop: false,
      height: new Cesium.CallbackProperty(function () {
        return -50;
      }, false)
    }
  })

image.png