Cesium自定义多边形进行模型剖切

方法

针对自定义绘制多边形进行模型剖切实际上有2种方法
方法一:

  • 利用cesium自带的ClippingPlaneCollection进行模型剖切,通过绘制的点,把剖切面构造出来,进行实现模型剖切

方法二:

  • 通过Cesium自身写入shader方法实现,通过判断点是否在多边形内实现剖切

因为这里没有通过方法二实现,因此这里只对方法一进行简述

原理

对于平面的normal,它指向的方向将不会被裁剪,反向的方向才会被裁剪。

1、绘制不规则多边形
2、取不规则多边形中的点的数据(若绘制的顺序是逆时针绘制,则还需要对点数据进行取反操作)
3、点与点之间绘制同向的向量,与向上的向量进行叉乘得到面的法向量(点+法向量即可构造一个面)
4、遍历点与操作3类似,即可构造一系列的剖切面

注意

img
在这一段描述中可以知道原点的选择直接影响到ClippingPlane的构造,如果root.transform存在,需要使用root.transform对应的坐标原点作为构造ClippingPlane时的坐标原点,如果不存在的话,就使用boundingSphere.center。

因此这里计算绘制点的坐标的时候需要加上坐标转换的代码

function getInverseTransform (tileSet) {
  let transform
  let tmp = tileSet.root.transform
  if ((tmp && tmp.equals(Cesium.Matrix4.IDENTITY)) || !tmp) {
  	// 如果root.transform不存在,则3DTiles的原点变成了boundingSphere.center
    transform = Cesium.Transforms.eastNorthUpToFixedFrame(tileSet.boundingSphere.center)
  } else {
    transform = Cesium.Matrix4.fromArray(tileSet.root.transform)
  }
  return Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4())
}

每一个点在与下一个点形成right向量的时候,都需要进行坐标转换,使得点能与模型的局部坐标对应得上,否则形成的面会不在模型上。

关键代码实现

这里的关键代码实际上就是指原理中所说的第三步与第四步,把tileset与绘制的点丢到该方法中即可

          const clippingByPositions = (tilesetData, originPositions) => {
            const Cartesian3 = Cesium.Cartesian3;
            const pointsLength = originPositions.length;
            const clockwise = getPolygonAnticlockwise(originPositions);
            //所有的裁切面
            const clippingPlanes = [];
            let positions;
            if (clockwise) {
              //如果为逆,则需要对数组取反
              positions = originPositions.reverse();
            } else {
              positions = originPositions;
            }

            //转换矩阵
            const inverseTransform = Cesium.Matrix4.inverseTransformation(
              tilesetData.root.transform,
              new Cesium.Matrix4()
            );
            for (let i = 0; i < pointsLength; ++i) {
              const nextIndex = (i + 1) % pointsLength;
              const next = Cesium.Matrix4.multiplyByPoint(
                inverseTransform,
                positions[nextIndex],
                new Cesium.Cartesian3()
              );
              const now = Cesium.Matrix4.multiplyByPoint(
                inverseTransform,
                positions[i],
                new Cesium.Cartesian3()
              );
              // 定义一个垂直向上的向量up
              let up = new Cesium.Cartesian3(0, 0, 10);
              //得到指向下一个点的向量
              let right = Cartesian3.subtract(next, now, new Cartesian3());
              right = Cartesian3.normalize(right, right);

              let normal = Cartesian3.cross(right, up, new Cartesian3());
              Cartesian3.normalize(normal, normal);
                if(clippingType.object.clippingType==='内部剖切'){
                  Cartesian3.negate(normal, normal);
                }
              //将法向量进行反向
              // Cartesian3.negate(normal, normal);

              //由于已经获得了法向量和过平面的一点,因此可以直接构造Plane,并进一步构造ClippingPlane
              let planeTmp = Cesium.Plane.fromPointNormal(now, normal);
              const clipPlane = Cesium.ClippingPlane.fromPlane(planeTmp);

              clippingPlanes.push(clipPlane);
            }

            const clipPlanes = new Cesium.ClippingPlaneCollection({
              planes: clippingPlanes,
              edgeWidth: 0.0,
              edgeColor: Cesium.Color.WHITE,
              enabled: true,
              unionClippingRegions: clippingType.object.clippingType==='内部剖切'?false:true,
            });
            tilesetData.clippingPlanes = clipPlanes;
            console.log("clippingPlanes", tilesetData.clippingPlanes);
          };

实现效果

内部裁切
img

外部裁切
img

posted @ 2022-12-30 14:18  百年内必成大牛  阅读(1569)  评论(0编辑  收藏  举报