cesium-裁剪模型(vertical垂直裁剪)
介绍
之前的文章介绍过,水平裁剪3dTiles模型查看模型横切片的例子,这次介绍一下垂直方向裁切模型
效果
先上效果
- 未裁剪效果
- 裁剪后效果
思路分析
这个里的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);
}