cesium-裁剪3dTiles模型(多边形裁剪)

本文介绍了如何使用Cesium.js在3D模型中实现不规则的裁剪,通过确定法向量和构建ClippingPlane,演示了如何针对给定的多边形进行裁剪操作。代码示例展示了在Vue应用中具体实现步骤,目前仅适用于凸多边形裁剪,后续可能扩展到凹多边形处理。
摘要由CSDN通过智能技术生成

cesium-裁剪3dTiles模型(多边形裁剪)

介绍

除了之前介绍的完整的裁切模型方式,我们有时还需不规则的去裁剪3dTiles模型

我这里业务需要裁剪模型中一部分位置

效果

先上效果

  • 俯视效果

image-20220209150901636

  • 侧视效果

image-20220209150929379

思路分析

  1. 确定一个垂直XOY面的法向量
 let up = new Cesium.Cartesian3(0, 0, 10);
  1. 两点确定一个方向向量,由p2指向p1的向量
let right = Cesium.Cartesian3.subtract(p2C3, p1C3, new Cesium.Cartesian3());
  1. 计算normal, right叉乘up,得到平面法向量(垂直于两个向量),这个法向量指向right的左侧
let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3());
normal = Cesium.Cartesian3.normalize(normal, normal);
  1. 由于已经获得了法向量和过平面的一点,因此可以直接构造Plane,并进一步构造ClippingPlane
let planeTmp = Cesium.Plane.fromPointNormal(p1C3, normal);
return Cesium.ClippingPlane.fromPlane(planeTmp);
  1. 将生成的裁切面集合添加给模型

完整代码

这里使用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-item>方法一(推荐)</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="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_polygon",
  data() {
    return {
      _viewer: undefined,
      _scene: undefined,
      _camera: undefined,
      targetY: 0.0,
      tileset: undefined,
      tileUrl: "../tileset.json",
      clippingPlanes: undefined,
      selectedPlane: undefined,
      showBound: false,
      polygon: [
        [
          117.44834303855897,
          34.37801246980093
        ],
        [
          117.44802117347719,
          34.37690560837309
        ],
        [
          117.4492335319519,
          34.37684362370057
        ],
        [
          117.4492174386978,
          34.37811430031746
        ],
        [
          117.44834303855897,
          34.37801246980093
        ]
      ],
    };
  },
  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;
    },
    /**
     * 修改平面的高度
     */
    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;
      // 3dTiles模型初始化位置的矩阵
      let Matrix4 = 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]
      );

      // 3dTiles模型加载后的矩阵,可以f12打印查看:tileset.root.transform
      let transform = Cesium.Matrix4.fromArray(
          [-0.8874246461620654, -0.46095281470464317, 0, 0,
            0.2602796082288922, -0.5010893346724129, 0.8253266045740758, 0,
            -0.3804366214290463, 0.7324151700322881, 0.5646556435405804, 0,
            -2429070.591483741, 4676437.67731705, 3581165.448379543, 1]);

      //转换矩阵
      let inverseTransform = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4());
      // clippingPlane集合
      let clippingPlanes1 = [];
      for (let i = 0; i < that.polygon.length - 1; i++) {
        let plane = that.createPlane(that.polygon[i], that.polygon[i + 1], inverseTransform);
        clippingPlanes1.push(plane);
      }
      // 创建裁剪平面
      let clippingPlanes = new Cesium.ClippingPlaneCollection({
        //一组ClippingPlane对象,用于选择性地禁用每个平面外部的渲染。
        planes: clippingPlanes1,
        //应用于裁剪对象的边缘的高光的宽度(以像素为单位)
        edgeWidth: 1.0,
      });
      that.clippingPlanes = clippingPlanes;
      tileset = new Cesium.Cesium3DTileset({
        url: that.tileUrl,
        clippingPlanes: clippingPlanes,
        unionClippingRegions: true,
        modelMatrix: Matrix4,
      });
      that.tileset = tileset;
      viewer.scene.primitives.add(tileset);
      viewer.zoomTo(tileset);
      console.log(tileset);
      // 开启深度检测
      // viewer.scene.globe.depthTestAgainstTerrain = true;


      // 当模型准备好时执行
      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)
            );
            Cesium.Matrix4.multiplyByPoint(transform, boundingSphere.center, new Cesium.Cartesian3(0, 0, 0))
            return tileset;
          })

    },
    /**
     * 设置相机的视角
     */
    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;
      }
    },
    /**
     * 对点进行坐标转换
     * @param point 点坐标 数组形式
     * @param inverseTransform 转换举证
     * @returns {*} ClippingPlane 裁切面
     */
    getOriginCoordinateSystemPoint(point, inverseTransform) {
      let val = Cesium.Cartesian3.fromDegrees(point[0], point[1])
      return Cesium.Matrix4.multiplyByPoint(
          inverseTransform, val, new Cesium.Cartesian3(0, 0, 0))
    },
    /**
     * 创建裁剪面
     * @param p1 起始点
     * @param p2 结束点
     * @param inverseTransform 矩阵
     * @returns {*} ClippingPlane裁剪面(面法向量,点到面的垂直距离)
     */
    createPlane(p1, p2, inverseTransform) {
      let that = this;
      // 将仅包含经纬度信息的p1,p2,转换为相应坐标系的cartesian3对象
      let p1C3 = that.getOriginCoordinateSystemPoint(p1, inverseTransform)
      let p2C3 = that.getOriginCoordinateSystemPoint(p2, inverseTransform)

      // 定义一个垂直向上的向量up
      let up = new Cesium.Cartesian3(0, 0, 10)
      //  right 实际上就是由p1指向p2的向量 (这里是p2--》p1)
      let right = Cesium.Cartesian3.subtract(p2C3, p1C3, new Cesium.Cartesian3())
      // 计算normal, right叉乘up,得到平面法向量(垂直于两个向量),这个法向量指向right的右侧
      let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3())
      normal = Cesium.Cartesian3.normalize(normal, normal)

      //由于已经获得了法向量和过平面的一点,因此可以直接构造Plane,并进一步构造ClippingPlane
      let planeTmp = Cesium.Plane.fromPointNormal(p1C3, normal)
      return Cesium.ClippingPlane.fromPlane(planeTmp)
    },
  },
  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: 500px;
}
</style>

核心代码

    /**
     * 对点进行坐标转换
     * @param point 点坐标 数组形式
     * @param inverseTransform 转换举证
     * @returns {*} ClippingPlane 裁切面
     */
    getOriginCoordinateSystemPoint(point, inverseTransform) {
      let val = Cesium.Cartesian3.fromDegrees(point[0], point[1])
      return Cesium.Matrix4.multiplyByPoint(
          inverseTransform, val, new Cesium.Cartesian3(0, 0, 0))
    },
    /**
     * 创建裁剪面
     * @param p1 起始点
     * @param p2 结束点
     * @param inverseTransform 矩阵
     * @returns {*} ClippingPlane裁剪面(面法向量,点到面的垂直距离)
     */
    createPlane(p1, p2, inverseTransform) {
      let that = this;
      // 将仅包含经纬度信息的p1,p2,转换为相应坐标系的cartesian3对象
      let p1C3 = that.getOriginCoordinateSystemPoint(p1, inverseTransform)
      let p2C3 = that.getOriginCoordinateSystemPoint(p2, inverseTransform)

      // 定义一个垂直向上的向量up
      let up = new Cesium.Cartesian3(0, 0, 10)
      //  right 实际上就是由p1指向p2的向量 (这里是p2--》p1)
      let right = Cesium.Cartesian3.subtract(p2C3, p1C3, new Cesium.Cartesian3())
      // 计算normal, right叉乘up,得到平面法向量(垂直于两个向量),这个法向量指向right的右侧
      let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3())
      normal = Cesium.Cartesian3.normalize(normal, normal)

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

待解决问题

  1. 这里只能裁剪凸多边形,凹多边形还不行
  2. 3dtiles模型裁剪只能裁剪一个,不能多裁剪

参考

参考

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
Cesium For Unreal是一款强大的工具,它允许开发者将真实世界的地理信息数据和虚拟现实世界相结合。它基于Unreal Engine游戏引擎,为用户提供了无与伦比的灵活性和自定义性。 在使用Cesium For Unreal时,我们可以使用其裁剪功能来对地理数据进行处理。裁剪是一种将地理数据集按照指定的范围进行切割的方法。这就意味着我们可以根据需要,只选择显示或加载特定区域的数据,从而提高性能和效率。 Cesium For Unreal提供了多种裁剪选项,以满足不同的需求。其中包括矩形裁剪多边形裁剪和分层裁剪等。用户可以根据项目的要求选择适合的裁剪方式。 使用矩形裁剪时,我们可以指定一个矩形范围来裁剪地理数据。只有位于该范围内的数据才会被加载和显示,从而减少不必要的数据传输和计算量。 多边形裁剪允许我们根据自定义的多边形轮廓来裁剪数据。这对于需要特定区域的详细数据时非常有用,例如城市规划、景点导览等。 分层裁剪是一种将地理数据分成多个层次的方法。这种裁剪方式可以根据距离或视角来控制数据的显示。例如,当用户越近越接近地球表面时,我们可能只加载和显示离用户最近的地区数据,而远离用户的数据则可以逐渐裁剪掉,以提高性能。 总而言之,Cesium For Unreal的裁剪功能使得我们能够根据需求对地理数据进行灵活的处理和优化,提高了开发效率和用户体验。无论是选择矩形、多边形还是分层裁剪方式,都可以根据项目的需求进行选择和定制,以满足各种应用场景的要求。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

孙霸天

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

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

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

打赏作者

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

抵扣说明:

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

余额充值