方法
针对自定义绘制多边形进行模型剖切实际上有2种方法
方法一:
- 利用cesium自带的ClippingPlaneCollection进行模型剖切,通过绘制的点,把剖切面构造出来,进行实现模型剖切
方法二:
- 通过Cesium自身写入shader方法实现,通过判断点是否在多边形内实现剖切
因为这里没有通过方法二实现,因此这里只对方法一进行简述
原理
对于平面的normal,它指向的方向将不会被裁剪,反向的方向才会被裁剪。
1、绘制不规则多边形
2、取不规则多边形中的点的数据(若绘制的顺序是逆时针绘制,则还需要对点数据进行取反操作)
3、点与点之间绘制同向的向量,与向上的向量进行叉乘得到面的法向量(点+法向量即可构造一个面)
4、遍历点与操作3类似,即可构造一系列的剖切面
注意
在这一段描述中可以知道原点的选择直接影响到ClippingPlane的构造,如果root.transform存在,需要使用root.transform对应的坐标原点作为构造ClippingPlane时的坐标原点,如果不存在的话,就使用boundingSphere.center。
因此这里计算绘制点的坐标的时候需要加上坐标转换的代码
function getInverseTransform (tileSet) {
let transform
let tmp = tileSet.root.transform
if ((tmp && tmp.equals(Cesium.Matrix4.IDENTITY)) || !tmp) {
// 如果root.transform不存在,则3DTiles的原点变成了boundingSphere.center
transform = Cesium.Transforms.eastNorthUpToFixedFrame(tileSet.boundingSphere.center)
} else {
transform = Cesium.Matrix4.fromArray(tileSet.root.transform)
}
return Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4())
}
每一个点在与下一个点形成right向量的时候,都需要进行坐标转换,使得点能与模型的局部坐标对应得上,否则形成的面会不在模型上。
关键代码实现
这里的关键代码实际上就是指原理中所说的第三步与第四步,把tileset与绘制的点丢到该方法中即可
const clippingByPositions = (tilesetData, originPositions) => {
const Cartesian3 = Cesium.Cartesian3;
const pointsLength = originPositions.length;
const clockwise = getPolygonAnticlockwise(originPositions);
//所有的裁切面
const clippingPlanes = [];
let positions;
if (clockwise) {
//如果为逆,则需要对数组取反
positions = originPositions.reverse();
} else {
positions = originPositions;
}
//转换矩阵
const inverseTransform = Cesium.Matrix4.inverseTransformation(
tilesetData.root.transform,
new Cesium.Matrix4()
);
for (let i = 0; i < pointsLength; ++i) {
const nextIndex = (i + 1) % pointsLength;
const next = Cesium.Matrix4.multiplyByPoint(
inverseTransform,
positions[nextIndex],
new Cesium.Cartesian3()
);
const now = Cesium.Matrix4.multiplyByPoint(
inverseTransform,
positions[i],
new Cesium.Cartesian3()
);
// 定义一个垂直向上的向量up
let up = new Cesium.Cartesian3(0, 0, 10);
//得到指向下一个点的向量
let right = Cartesian3.subtract(next, now, new Cartesian3());
right = Cartesian3.normalize(right, right);
let normal = Cartesian3.cross(right, up, new Cartesian3());
Cartesian3.normalize(normal, normal);
if(clippingType.object.clippingType==='内部剖切'){
Cartesian3.negate(normal, normal);
}
//将法向量进行反向
// Cartesian3.negate(normal, normal);
//由于已经获得了法向量和过平面的一点,因此可以直接构造Plane,并进一步构造ClippingPlane
let planeTmp = Cesium.Plane.fromPointNormal(now, normal);
const clipPlane = Cesium.ClippingPlane.fromPlane(planeTmp);
clippingPlanes.push(clipPlane);
}
const clipPlanes = new Cesium.ClippingPlaneCollection({
planes: clippingPlanes,
edgeWidth: 0.0,
edgeColor: Cesium.Color.WHITE,
enabled: true,
unionClippingRegions: clippingType.object.clippingType==='内部剖切'?false:true,
});
tilesetData.clippingPlanes = clipPlanes;
console.log("clippingPlanes", tilesetData.clippingPlanes);
};
实现效果
内部裁切
外部裁切