Cesium中的拣选(pick)具备一套比较巧妙机制,。可以简单的认为,Cesium的常规的鼠标拣选是基于最终成图做的。就如同下面的这幅画,红色的箭头指向牛的臀,而不是后面的房子,是因为牛挡住了房子。这是一种比较自然的理解方式。
如果箭头不是从眼睛的方向,那么上面的方法就用不了了。这种情况下有两个解决办法,一个是跟前面一样,从射线的方向再“拍一次照片”。还有一种办法就是每个三角形与射线进行相交判断。
1、射线与三角形相交
这个算法是大多数三维平台的基本算法,早期三维平台,比如worldWind全部采用这种技术路线去判断拣选。该算法比较暴力,需要对场景所有的模型进行遍历。如果三角网比较多,性能就会慢下来。
射线与三角形的相机点,可以分解为三角形两条边的代数和。即r1*v1+(1-r1)*v2=p。只要r1满足0<=r1<=1即可。每次计算大于20次乘法。具体函数可参考IntersectionTests这个类。
2、如何获得模型的三角网
要想获得模型三角网,就需要先获得三角网顶点和索引,及模型的变换矩阵。
在Cesium中,Model这个类有个属性叫sceneGraph,它存储了该Model从根节点到本节点的变换矩阵(computedModelMatrix)。
矩阵的问题解决了,那现在就是顶点和索引。由于显存数据和内存是不同的,因此Cesium在从内存中读取到三角网信息后,就丢弃内存数据,减少内存压力,当然这里指原始是三角网信息。因此这时候只能从显存中读取Mesh数据。
幸运的是,Buffer这个类提供了webgl的底层数据访问能力。利用如下函数即可从显存Buffer中读取数据
const gl = this._gl;
const target = WebGLConstants.COPY_READ_BUFFER;
gl.bindBuffer(target, this._buffer);
gl.getBufferSubData(
target,
sourceOffset,
arrayView,
destinationOffset,
length,
);
gl.bindBuffer(target, null);
其中COPY_READ_BUFFER是关键字,指明是从显存到内存。
3、解构三角网顶点和索引
第二步获得数据并不能直接使用,因为Cesium可能对这些数据进行了量化压缩。量化压缩只要是int与float相互转换。比如颜色的rgb可以转换为一个float(rgb的值太小,用int存储不值得)。之所以采用量化压缩,是因为显卡是区分数值类型的。
4、PickModel什么时候用
大部分情况下PickModel这个类用不到。在Cesium示例中,如果设置某对象贴模型就需要采用这个方法计算模型在某个点的高度,从而调整某对象的位置。这里的某对象包括立体图标、模型等。更多参考示例Clamp Model to Ground - Cesium Sandcastle