1.多边形裁切
1.1 基本流程
cesium117版本添加了多边形裁切功能,本文分析源码,看看是如何处理的。多边形裁切的大概流程分为4部分:
- 通过经纬度坐标传入多个闭合的边界;
- 将多个边界打包成两张纹理,一张是每个多边形的坐标,另一张是每个多边形的边界;
- 将两张多边形纹理通过一个计算着色器(屏幕空间着色器模拟计算着色器)生成一张符号距离场纹理;
- 将这两张图传入地球瓦片和3DTiles的着色器中进行多边形裁切.
1.2 多边形纹理打包
这是在js代码中处理的,使用了ClippingPolygon和ClippingPolygonCollection两个类,ClippingPolygon类负责每个多边形的坐标收集以及每个多边形的范围计算。
以下是ClippingPolygon类的主要代码,过程比较简单。
/**
* Computes a rectangle with the spherical extents that encloses the polygon defined by the list of positions, including cases over the international date line and the poles.
* 根据给定的位置列表计算球上的坐标区域(使用弧度表示),包括越过国际日期线和极点的情况
* @private
*
* @param {Rectangle} [result] An object in which to store the result.
* @returns {Rectangle} The result rectangle with spherical extents.
*/
ClippingPolygon.prototype.computeSphericalExtents = function (result) {
if (!defined(result)) {
result = new Rectangle();
}
// 经纬度范围
const rectangle = this.computeRectangle(scratchRectangle);
// 计算出球面点笛卡尔
let spherePoint = Cartographic.toCartesian(
Rectangle.southwest(rectangle),
this.ellipsoid,
spherePointScratch
);
// Project into plane with vertical for latitude
// 投影到具有垂直纬度的平面中
let magXY = Math.sqrt(
spherePoint.x * spherePoint.x + spherePoint.y * spherePoint.y
);
// Use fastApproximateAtan2 for alignment with shader
// 球面纬度
let sphereLatitude = CesiumMath.fastApproximateAtan2(magXY, spherePoint.z);
// 球面经度
let sphereLongitude = CesiumMath.fastApproximateAtan2(
spherePoint.x,
spherePoint.y
);
// 西南的经纬度
result.south = sphereLatitude;
result.west = sphereLongitude;
// 计算东北点位
spherePoint = Cartographic.toCartesian(
Rectangle.northeast(rectangle),
this.ellipsoid,
spherePointScratch
);
// Project into plane with vertical for latitude
magXY = Math.sqrt(
spherePoint.x * spherePoint.x + spherePoint.y * spherePoint.y
);
// Use fastApproximateAtan2 for alignment with shader
sphereLatitude = CesiumMath.fastApproximateAtan2(magXY, spherePoint.z);
sphereLongitude = CesiumMath.fastApproximateAtan2(
spherePoint.x,
spherePoint.y
);
// 计算东北经纬度
result.north = sphereLatitude;
result.east = sphereLongitude;
return result;
};
ClippingPolygonCollection类的过程主要在update函数中,函数过程如下
ClippingPolygonCollection.prototype.update = function (frameState) {
const context = frameState.context;
// 是否支持
if (!ClippingPolygonCollection.isSupported(frameState)) {
throw new RuntimeError(
"ClippingPolygonCollections are only supported for WebGL 2."
);
}
// It'd be expensive to validate any individual position has changed. Instead verify if the list of polygon positions has had elements added or removed, which should be good enough for most cases.
// 验证任何个人立场的改变都是昂贵的。相反,请验证多边形位置列表中是否添加或删除了元素,这在大多数情况下应该足够好。
// 总共的顶点数量
const totalPositions = this._polygons.reduce(
(totalPositions, polygon) => totalPositions + polygon.length,
0
);
// 总共的顶点数量不变
if (totalPositions === this.totalPositions) {
return;
}
this._totalPositions = totalPositions;
// If there are no clipping polygons, there's nothing to update.
if (this.length === 0) {
return;
}
// 符号距离计算命令,命令存在就取消
if (defined(this._signedDistanceComputeCommand)) {
// 如果正在计算就取消
this._signedDistanceComputeCommand.canceled = true;
this._signedDistanceComputeCommand = undefined;
}
// 多边形纹理
let polygonsTexture = this._polygonsTexture;
// 范围纹理
let extentsTexture = this._extentsTexture;
// 符号距离纹理
let signedDistanceTexture = this._signedDistanceTexture;
if (defined(polygonsTexture)) {
// 当前像素数量