原生cesium 实现楼栋抽离效果

🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:cesium实践(原生)
🌈 若有帮助,还请关注点赞收藏,不行的话我再努努力💪💪💪

需求背景

需要实现整栋楼的展开,合上,单层抽离的预览效果

解决思路

由于3dtiles格式分成选中的元素太多,做模型抽离较麻烦
采用把模型按每层分成小模型用model的方式交互加载

解决效果

index.vue

/**
* @author: liuk
* @date: 2024-11-18
* @describe:楼层叠加
*/
<template>
  <div class="floorsStacking-wrap">
    <p>楼层叠加</p>
    <p>当前楼层:{{ curFloor || 20 }}</p>
    <el-button type="primary" style="width:100px;margin-left: 0" @click='spreadFloor'>展开楼层</el-button>
    <el-button type="primary" style="width:100px;margin-left: 0" @click='closeFloor'>合上楼层</el-button>
    <el-button type="warning" style="width:100px;margin-left: 0" @click="reset">重置</el-button>
    <el-button type="primary" style="width:100px;margin-left: 0" @click="mapResetCamera">视角</el-button>
    <Tdt_img_d/>
  </div>
</template>

<script lang="ts" setup>
import {usemapStore} from "@/store/modules/cesiumMap";
import {onMounted, onUnmounted, reactive, toRefs} from "vue"
import {cartesianToWgs84} from "@/utils/dictionary";
// Component
import Tdt_img_d from "@/views/cesium/component/controlPanel/layerManagement/basicMap/tdt_img_d.vue"
import {ElMessage} from "element-plus";

const mapStore = usemapStore()
const model = reactive({
  curFloor: ""
})
const {curFloor} = toRefs(model)

onMounted(() => {
  viewer.dataSources.add(floorsStackingDatasource);
  addEntity()
  mapResetCamera()
  handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  handler.setInputAction(onMouseClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);
})

onUnmounted(() => {
  handler.destroy()
  floorsStackingDatasource?.entities?.removeAll()
  viewer.dataSources.remove(floorsStackingDatasource);
})

// 地图逻辑
const viewer = mapStore.getCesiumViewer();
const floorsStackingDatasource = new Cesium.CustomDataSource("floorsStacking");
let handler

const addEntity = () => {
  new Array(20).fill("").forEach((_, index) => {
    floorsStackingDatasource.entities.add({
      customType: "floorsStacking",
      floor: index + 1,
      position: Cesium.Cartesian3.fromDegrees(113.136103, 25.720441, 673 + index * 3 - (index && 2.3)),
      model: {
        uri: getGlbUrl(index + 1),
      },
    });
  })
}

const spreadFloor = () => {
  floorsStackingDatasource.entities.values.forEach((entity, index) => {
    const pos = cartesianToWgs84(entity.position.getValue())
    entity.position = Cesium.Cartesian3.fromDegrees(pos[0], pos[1], pos[2] + 4 * entity.floor)
  })
}

const closeFloor = () => {
  floorsStackingDatasource.entities.values.forEach((entity, index) => {
    const pos = cartesianToWgs84(entity.position.getValue())
    entity.position = Cesium.Cartesian3.fromDegrees(pos[0], pos[1], pos[2] - 4 * entity.floor)
  })
}

const onMouseClick = (movement) => {
  const pickedObject = viewer.scene.pick(movement.position);
  if (!Cesium.defined(pickedObject) || !Cesium.defined(pickedObject.id)) return
  const entity = pickedObject.id;
  if (entity instanceof Cesium.Entity && entity.customType === "floorsStacking") {
    model.curFloor = entity.floor
    ElMessage.success(`点击了${entity.floor}`)
    floorsStackingDatasource.entities.values.forEach(entity => {
      if (entity.floor > model.curFloor) entity.show = false
    })
  }
}

const reset = () => {
  model.curFloor = ""
  floorsStackingDatasource.entities.values.forEach(entity => entity.show = true)
}


const getGlbUrl = (i) => {
  switch (true) {
    case i === 1:
      return `${import.meta.env.VITE_APP_MODELDATA}/floorsStackGlb/1.glb`
    case i < 7:
      return `${import.meta.env.VITE_APP_MODELDATA}/floorsStackGlb/${i}.glb`
    case i < 16:
      return `${import.meta.env.VITE_APP_MODELDATA}/floorsStackGlb/6.glb`
    case i === 16:
      return `${import.meta.env.VITE_APP_MODELDATA}/floorsStackGlb/16.glb`
    case i < 19:
      return `${import.meta.env.VITE_APP_MODELDATA}/floorsStackGlb/17.glb`
    default:
      return `${import.meta.env.VITE_APP_MODELDATA}/floorsStackGlb/${i}.glb`
  }
}

const mapResetCamera = () => {
  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(113.139392, 25.720645, 792.29),
    orientation: {
      heading: Cesium.Math.toRadians(275),
      pitch: Cesium.Math.toRadians(-17.7),
      roll: 0.0
    }
  });
}
</script>

<style lang="scss" scoped>
.floorsStacking-wrap {
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  align-items: center;
  position: absolute;
  top: 100px;
  left: 280px;
  width: 120px;
  height: 240px;
  border-radius: 5px;
  background: rgba(0, 0, 0, 0.6);
  pointer-events: auto;

}
</style>

<think>好的,我现在需要回答用户关于如何在Cesium实现地形开挖抽离效果的问题。首先,我得回忆一下Cesium的相关功能和API。Cesium是一个强大的地理可视化库,支持3D地形渲染,所以地形开挖应该和地形裁剪有关。 用户提到的“地形开挖抽离效果”可能是指挖开地形的一部分,显示被遮挡的地下层,或者将某块地形单独分离出来。常见的应用场景比如地下管线展示、地质勘探等。我需要考虑如何利用Cesium的现有功能来实现这一点。 首先想到的是Cesium的ClippingPlane功能。ClippingPlane可以通过平面来裁剪3D模型或地形,这可能适用于开挖效果。需要创建一个或多个裁剪平面,应用到地形上,这样地形的一部分会被切掉,露出下方的结构。比如,使用四个平面围成一个立方体区域,挖出一个坑。 另外,Cesium的CustomShader也可能有用。通过自定义着色器,可以更灵活地控制地形的渲染方式,比如根据高度或位置来隐藏部分地形。不过,CustomShader可能需要较高的GLSL知识,对于不熟悉的人来说可能比较复杂。 还有,地形开挖可能需要动态修改地形的高程数据。比如,将某个区域的高程设置为透明或调整高度,但这可能涉及到实时修改地形瓦片,实现起来比较困难,尤其是在大规模地形中。 接下来,我需要整理具体的实现步骤。首先,使用ClippingPlaneCollection创建裁剪平面,然后将其应用到地形提供器上。可能需要设置模型的clippingPlanes属性,但地形和模型的处理方式可能不同,需要查阅文档确认。 另外,可能需要结合其他几何图形,比如绘制一个立方体或圆柱体作为开挖区域,然后利用这个几何体生成裁剪平面。或者使用多边形划定开挖区域,然后计算对应的裁剪平面。 需要注意的是,裁剪平面的方向,因为平面是有方向的,裁剪的是反方向的部分。可能需要调整平面的法线方向,确保正确的区域被裁剪。 此外,性能也是一个考虑因素。过多的裁剪平面可能会影响渲染性能,尤其是在低端设备上。因此,需要优化平面的数量和复杂度。 最后,可能需要综合使用多种技术,比如ClippingPlane配合3D Tiles的裁剪,或者结合后期处理效果来增强视觉效果,比如在开挖边缘添加高亮或渐变效果。 总结一下,可能的步骤是:1. 创建裁剪平面集合;2. 应用到地形或3D模型;3. 调整平面参数以控制开挖区域;4. 可能的辅助视觉效果处理。需要验证这些步骤的可行性,并查看是否有示例代码或文档支持。</think>在Cesium实现地形开挖抽离效果,主要通过地形裁剪技术实现。以下是分步实现方案: 1. **核心原理** 使用ClippingPlane(裁剪平面)技术,通过创建多个平面切割地形表面,形成几何开挖效果。每个平面可定义法线方向,控制裁剪范围。 2. **基础实现步骤** ```javascript // 创建裁剪平面集合 const clippingPlanes = new Cesium.ClippingPlaneCollection({ planes : [ new Cesium.ClippingPlane(new Cesium.Cartesian3(1, 0, 0), 1000), // 法线方向+X,距离原点1000米 new Cesium.ClippingPlane(new Cesium.Cartesian3(-1, 0, 0), 1000), new Cesium.ClippingPlane(new Cesium.Cartesian3(0, 1, 0), 1000), new Cesium.ClippingPlane(new Cesium.Cartesian3(0, -1, 0), 1000) ], edgeWidth: 1.0, // 裁剪边缘线宽 edgeColor: Cesium.Color.RED // 边缘高亮颜色 }); // 应用至地形 viewer.terrainProvider = new Cesium.CesiumTerrainProvider({ url: Cesium.IonResource.fromAssetId(1), clippingPlanes: clippingPlanes }); ``` 3. **进阶优化方案** - **动态开挖控制**:绑定平面参数到UI控件,实现交互式调节 ```javascript const plane = clippingPlanes.get(0); slider.addEventListener('input', (e) => { plane.distance = parseFloat(e.target.value); }); ``` - **三维开挖区域**: ```javascript // 创建三维包围盒 const boundingBox = Cesium.BoundingSphere.fromPoints([ Cesium.Cartesian3.fromDegrees(116.39, 39.91, 0), Cesium.Cartesian3.fromDegrees(116.41, 39.93, 50) ]); // 自动生成6个平面形成立方体 const planes = Cesium.ClippingPlane.fromBoundingSphere(boundingBox); ``` 4. **性能优化技巧** - 使用`unionClippingRegions: false`进行多平面联合运算 - 限制同时激活的裁剪平面数量(建议≤6个) - 采用LOD技术分级裁剪精度 5. **视觉效果增强** ```javascript // 添加半透明遮罩层 viewer.scene.globe.showGroundAtmosphere = true; viewer.scene.globe.baseColor = Cesium.Color.BLUE.withAlpha(0.3); // 边缘发光特效 viewer.scene.postProcessStages.add( Cesium.PostProcessStageLibrary.createEdgeDetectionStage() ); ``` **注意事项**: 1. 坐标系需统一使用WGS84 2. 地形开挖深度建议控制在200米以内(避免穿透地球) 3. 移动设备需降低裁剪精度保证流畅度 完整示例可在Cesium Sandcastle中调试,建议从简单立方体开挖入手,逐步增加复杂地形处理逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值