cesium-裁剪3dTiles模型(vertical垂直裁剪)

cesium-裁剪模型(vertical垂直裁剪)

介绍

之前的文章介绍过,水平裁剪3dTiles模型查看模型横切片的例子,这次介绍一下垂直方向裁切模型

效果

先上效果

  • 未裁剪效果

image-20220209112845576

  • 裁剪后效果

image-20220209112943273

image-20220209113012088

思路分析

这个里的3dTiles模型我使用的是倾斜摄影模型,实现的效果和官网案例一样

  • 添加倾斜摄影模型,设置模型矩阵,修正模型的位置
  • 获取模型的外包球半径(radius)
  • 添加裁切面模型
    • 设置笛卡儿坐标系的原点位置为模型外接圆的圆心
    • 绘制平面(使用Entity类创建一个水平面),并设置一个范围(原点为平面中心)
    • 设置面的法向量和离原点(这里是外包球的球心)的最短距离(一个法向量和离原点距离就可以确认一个面)
  • 将平面做为裁切面添加到倾斜摄影模型中
  • 设置监听鼠标的事件,当平面被选中拖动后,动态(使用回调函数)的改变平面的高度
  • 平面被拖动后模型也会动态的被裁切

完整代码

这里使用的是vue模版代码实现,HTML代码实现类似

<template>
  <div class="home">
    <el-row type="flex" :gutter="20">
      <el-col :span="24">
        <div class="grid-content bg-purple">
          <el-breadcrumb separator="/">
            <el-breadcrumb-item>cesium</el-breadcrumb-item>
            <el-breadcrumb-item>裁剪功能</el-breadcrumb-item>
            <el-breadcrumb-item>3DTiles裁剪(平面上下移动)</el-breadcrumb-item>
          </el-breadcrumb>
        </div>
      </el-col>
    </el-row>
    <el-row type="flex" :gutter="20">
      <el-col :span="24">
        <div class="grid-content bg-purple">
          <cesiumComponent id="cesium" ref="refCesium"/>
        </div>
      </el-col>
    </el-row>
    <el-row type="flex" :gutter="20">
      <el-col :span="24">
        <div class="grid-content bg-purple">
          <el-button type="primary" @click="toggleClipping()">开关裁切</el-button>
          <el-button type="primary" @click="cameraLookAtTransform()">视角复位</el-button>
          <el-button type="primary" @click="showBoundVolumes()">开关边界体积</el-button>
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import cesiumComponent from '../cesium.vue'

export default {
  name: "clipping_3dTile",
  data() {
    return {
      _viewer: undefined,
      _scene: undefined,
      _camera: undefined,
      targetY: 0.0,
      tileset: undefined,
      isShowTileSet: true,
      tileUrl: "..../tileset.json",
      clippingPlanes: undefined,
      selectedPlane: undefined,
      showBound: false
    };
  },
  components: {
    cesiumComponent
  },
  mounted() {
    this.init();
    this.addTiles();
  },
  methods: {
    init() {
      let that = this;
      that.$refs.refCesium.initMap();
      that._viewer = that.$refs.refCesium._viewer;
      that._camera = that._viewer.camera;
      that._scene = that._viewer.scene;
      that.mouseHandler();
    },
    mouseHandler() {
      let that = this;
      let viewer = that._viewer;
      let scene = that._scene;
      let selectedPlane;
      // 监听鼠标按下时,选择平面并设置样式
      let downHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      downHandler.setInputAction(function (movement) {
        let pickedObject = scene.pick(movement.position);
        if (
            Cesium.defined(pickedObject) &&
            Cesium.defined(pickedObject.id) &&
            Cesium.defined(pickedObject.id.plane)
        ) {
          // 获取选中平面对象
          selectedPlane = pickedObject.id.plane;
          // 选中时修改平面的材质透明度
          selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.05);
          selectedPlane.outlineColor = Cesium.Color.WHITE;
          // 当鼠标选中平面后,禁止场景的拖拽
          scene.screenSpaceCameraController.enableInputs = false;
        }
      }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

      // 监听鼠标向上释放时,平面设置样式
      let upHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      upHandler.setInputAction(function () {
        if (Cesium.defined(selectedPlane)) {
          // 鼠标松开时复原平面的材质透明度
          selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.1);
          selectedPlane.outlineColor = Cesium.Color.WHITE;
          selectedPlane = undefined;
        }
        // 当鼠标松开选中平面后,开启场景的拖拽
        scene.screenSpaceCameraController.enableInputs = true;
      }, Cesium.ScreenSpaceEventType.LEFT_UP);

      // 监听鼠标选中移动时,设置平面
      let moveHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      moveHandler.setInputAction(function (movement) {
        //当存在选中平面时执行
        if (Cesium.defined(selectedPlane)) {
          // 移动起点的高度减去移动终点的高度
          let deltaY = movement.startPosition.y - movement.endPosition.y;
          // 目标高度
          that.targetY += deltaY;
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    },
    /**
     * 修改平面的高度
     */
    createPlaneUpdateFunction(plane) {
      let that = this;
      return function () {
        plane.distance = that.targetY;
        return plane;
      };
    },
    /**
     * 添加3dTiles模型
     */
    addTiles() {
      let that = this;
      let viewer = that._viewer;
      let tileset = that.tileset;
      let clippingPlanes = that.clippingPlanes;
      // 创建裁剪平面
      clippingPlanes = new Cesium.ClippingPlaneCollection({
        //一组ClippingPlane对象,用于选择性地禁用每个平面外部的渲染。
        planes: [
          // 裁剪面两个参数的:第一个为平面法向量,第二个为原点到平面的垂直距离
          new Cesium.ClippingPlane(
              //笛卡尔3:表示为三维空间的平面的法向量,x表示为该法向量在x轴上的分量,y表示为该法向量在y轴上的分量,z表示为该法向量在z轴上的分量
              new Cesium.Cartesian3(0.0, 0.0, -1.0),
              0.0
          ),
        ],
        //应用于裁剪对象的边缘的高光的宽度(以像素为单位)
        edgeWidth: 1.0,
      });
      that.clippingPlanes = clippingPlanes;
      tileset = new Cesium.Cesium3DTileset({
        url: that.tileUrl,
        clippingPlanes: clippingPlanes,
        modelMatrix: Cesium.Matrix4.fromArray([1, 5.551115123125783e-16, 5.898416033378595e-9, 0, -6.106226635438361e-16, 1, -1.1355608731111744e-8, 0, -5.898416061134171e-9, 1.1355608731111744e-8, 0.9999999999999999, 0, 9.912469893228263, -19.08345020748675, -14.613607150502503, 1]),
      });
      that.tileset = tileset;
      viewer.scene.primitives.add(tileset);
      viewer.zoomTo(tileset);
      // 开启深度检测
      viewer.scene.globe.depthTestAgainstTerrain = true;

      console.log(tileset);

      // 当模型准备好时执行
      return tileset.readyPromise
          .then(function () {
            let boundingSphere = tileset.boundingSphere;//外包球
            let radius = boundingSphere.radius; //外包球半径

            // 定位到模型,并设置相机的俯仰角和距离
            viewer.zoomTo(
                tileset,
                new Cesium.HeadingPitchRange(0.5, -0.2, radius * 4.0)
            );

            // 遍历添加裁切面模型
            for (let i = 0; i < clippingPlanes.length; ++i) {
              let plane = clippingPlanes.get(i);
              let planeEntity = viewer.entities.add({
                // 笛卡儿坐标系的原点位置为模型外接圆的圆心
                position: boundingSphere.center,
                plane: {
                  // 范围
                  dimensions: new Cesium.Cartesian2(
                      radius * 2,
                      radius * 2
                  ),
                  //设置材质透明度
                  material: Cesium.Color.WHITE.withAlpha(0.1),
                  //使用回调函数,动态改变模型位置
                  plane: new Cesium.CallbackProperty(
                      that.createPlaneUpdateFunction(plane),
                      false
                  ),
                  // 轮廓
                  outline: true,
                  //轮廓颜色
                  outlineColor: Cesium.Color.WHITE,
                },
              });
            }
            return tileset;
          })

    },
    /**
     * 移除裁切面
     */
    toggleClipping() {
      let that = this;
      if(that.isShowTileSet){
        that.tileset._clippingPlanes=null;
        that.isShowTileSet=false;
      }else {
        that.tileset._clippingPlanes=that.clippingPlanes;
        that.isShowTileSet=true;
      }

    },
    /**
     * 设置相机的视角
     */
    cameraLookAtTransform() {
      let that = this;
      let boundingSphere = that.tileset.boundingSphere;
      that._camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(Cesium.Math.toRadians(120.0), Cesium.Math.toRadians(-10), boundingSphere.radius * 2.5))
      that._camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
    },
    /**
     *  开关展示边界体积
     */
    showBoundVolumes() {
      let that = this;
      if (!that.showBound) {
        that.tileset.debugShowContentBoundingVolume = true;
        that.showBound = true;
      } else {
        that.tileset.debugShowContentBoundingVolume = false;
        that.showBound = false;
      }
    }
  },
  created() {

  },
}
</script>

<style scoped>
.home {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.el-breadcrumb {
  margin: 10px;
  font-size: 15px;
}

#cesium {
  max-height: 600px;
}
</style>

核心代码

添加模型和裁剪平面

   /**
     * 添加3dTiles模型
     */
    addTiles() {
      let that = this;
      let viewer = that._viewer;
      let tileset = that.tileset;
      let clippingPlanes = that.clippingPlanes;
      // 创建裁剪平面
      clippingPlanes = new Cesium.ClippingPlaneCollection({
        //一组ClippingPlane对象,用于选择性地禁用每个平面外部的渲染。
        planes: [
          // 裁剪面两个参数的:第一个为平面法向量,第二个为原点到平面的垂直距离
          new Cesium.ClippingPlane(
              //笛卡尔3:表示为三维空间的平面的法向量,x表示为该法向量在x轴上的分量,y表示为该法向量在y轴上的分量,z表示为该法向量在z轴上的分量
              new Cesium.Cartesian3(0.0, 0.0, -1.0),
              0.0
          ),
        ],
        //应用于裁剪对象的边缘的高光的宽度(以像素为单位)
        edgeWidth: 1.0,
      });
      that.clippingPlanes = clippingPlanes;
      tileset = new Cesium.Cesium3DTileset({
        url: that.tileUrl,
        clippingPlanes: clippingPlanes,
        modelMatrix: Cesium.Matrix4.fromArray([1, 5.551115123125783e-16, 5.898416033378595e-9, 0, -6.106226635438361e-16, 1, -1.1355608731111744e-8, 0, -5.898416061134171e-9, 1.1355608731111744e-8, 0.9999999999999999, 0, 9.912469893228263, -19.08345020748675, -14.613607150502503, 1]),
      });
      that.tileset = tileset;
      viewer.scene.primitives.add(tileset);
      viewer.zoomTo(tileset);
      // 开启深度检测
      viewer.scene.globe.depthTestAgainstTerrain = true;

      console.log(tileset);

      // 当模型准备好时执行
      return tileset.readyPromise
          .then(function () {
            let boundingSphere = tileset.boundingSphere;//外包球
            let radius = boundingSphere.radius; //外包球半径

            // 定位到模型,并设置相机的俯仰角和距离
            viewer.zoomTo(
                tileset,
                new Cesium.HeadingPitchRange(0.5, -0.2, radius * 4.0)
            );

            // 遍历添加裁切面模型
            for (let i = 0; i < clippingPlanes.length; ++i) {
              let plane = clippingPlanes.get(i);
              let planeEntity = viewer.entities.add({
                // 笛卡儿坐标系的原点位置为模型外接圆的圆心
                position: boundingSphere.center,
                plane: {
                  // 范围
                  dimensions: new Cesium.Cartesian2(
                      radius * 2,
                      radius * 2
                  ),
                  //设置材质透明度
                  material: Cesium.Color.WHITE.withAlpha(0.1),
                  //使用回调函数,动态改变模型位置
                  plane: new Cesium.CallbackProperty(
                      that.createPlaneUpdateFunction(plane),
                      false
                  ),
                  // 轮廓
                  outline: true,
                  //轮廓颜色
                  outlineColor: Cesium.Color.WHITE,
                },
              });
            }
            return tileset;
          })
    },

平面高度设置函数

 /**
     * 修改平面的高度
     */
    createPlaneUpdateFunction(plane) {
      let that = this;
      return function () {
        plane.distance = that.targetY;
        return plane;
      };
    },

监听鼠标事件

 mouseHandler() {
      let that = this;
      let viewer = that._viewer;
      let scene = that._scene;
      let selectedPlane;
      // 监听鼠标按下时,选择平面并设置样式
      let downHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      downHandler.setInputAction(function (movement) {
        let pickedObject = scene.pick(movement.position);
        if (
            Cesium.defined(pickedObject) &&
            Cesium.defined(pickedObject.id) &&
            Cesium.defined(pickedObject.id.plane)
        ) {
          // 获取选中平面对象
          selectedPlane = pickedObject.id.plane;
          // 选中时修改平面的材质透明度
          selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.05);
          selectedPlane.outlineColor = Cesium.Color.WHITE;
          // 当鼠标选中平面后,禁止场景的拖拽
          scene.screenSpaceCameraController.enableInputs = false;
        }
      }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

      // 监听鼠标向上释放时,平面设置样式
      let upHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      upHandler.setInputAction(function () {
        if (Cesium.defined(selectedPlane)) {
          // 鼠标松开时复原平面的材质透明度
          selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.1);
          selectedPlane.outlineColor = Cesium.Color.WHITE;
          selectedPlane = undefined;
        }
        // 当鼠标松开选中平面后,开启场景的拖拽
        scene.screenSpaceCameraController.enableInputs = true;
      }, Cesium.ScreenSpaceEventType.LEFT_UP);

      // 监听鼠标选中移动时,设置平面
      let moveHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      moveHandler.setInputAction(function (movement) {
        //当存在选中平面时执行
        if (Cesium.defined(selectedPlane)) {
          // 移动起点的高度减去移动终点的高度
          let deltaY = movement.startPosition.y - movement.endPosition.y;
          // 目标高度
          that.targetY += deltaY;
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙霸天

你的打赏是我不断创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值