# cesium-裁剪模型(vertical垂直裁剪)
# 介绍
之前的文章介绍过,水平裁剪3dTiles模型查看模型横切片的例子,这次介绍一下垂直方向裁切模型
# 效果
先上效果
- 未裁剪效果

- 裁剪后效果


# 思路分析
这个里的3dTiles模型我使用的是倾斜摄影模型,实现的效果和官网案例 (opens new window)一样
- 添加倾斜摄影模型,设置模型矩阵,修正模型的位置
- 获取模型的外包球半径(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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# 核心代码
# 添加模型和裁剪平面
/**
* 添加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;
})
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 平面高度设置函数
/**
* 修改平面的高度
*/
createPlaneUpdateFunction(plane) {
let that = this;
return function () {
plane.distance = that.targetY;
return plane;
};
},
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 监听鼠标事件
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49