汇哥全球后援会
学而不思则罔,思而不学则殆。

cesium查找视野内模型

发布于November 20, 2021

背景

最近遇到一个需求,要求在camera移动的过程中把视野范围内的所有3dtiles单体化后的模型找到。但这很显然是不合理的,因为没有控制视野的长度,理论上camera的视野长度是无限长的,所以最后需求更改为探测合理范围内的模型。

思路

完成这个功能理论上需要将摄像机的视野范围和单体化模型进行碰撞计算,将单体化模型包含或相交于视野的模型找出来即可。首先需要实体化摄像机视野,这里可以采用单独建立一个Cesium.PerspectiveFrustum类来实例化。

https://cesium.com/learn/cesiumjs/ref-doc/PerspectiveFrustum.html?classFilter=PerspectiveFrustum

在实例化的过程中,可以把视野看作是顶点在camera位置的四棱锥,可以定义fov、aspectRatio、aspectRatio、far来定义四棱锥的形状,然后使用改实体化对象使用computeCullingVolume方法建立一个CullingVolume,这里的position、direction、up可以使用此时camera的属性。

const cullingVolume = new Cesium.PerspectiveFrustum({ fov: Cesium.Math.toRadians(100.0), aspectRatio: viewer.scene.drawingBufferWidth / viewer.scene.drawingBufferHeight, far: 6, near: 0.1 }).computeCullingVolume( viewer.camera.positionWC, viewer.camera.directionWC, viewer.camera.upWC );

另外,在设置四棱锥由于看不见本身的样子,就会导致效率效率异常慢,很容易影响心态,所以这里可以使用Cesium.DebugCameraPrimitive来进行测试。

https://cesium.com/learn/cesiumjs/ref-doc/DebugCameraPrimitive.html?classFilter=debug

使用之前最好重新先建立一个camera,并设置好position、directionWC、upWC等属性,然后就可以开始调试fov、near、far等参数。

Untitled Untitled

回到上面建立好了CullingVolume之后,就可以使用它的computeVisibility方法和3dtiles单体化模型的BoundingSphere进行计算,最后的结果如果是Cesium.Intersect.OUTSIDE说明不在视野范围内。这里的BoundingSphere可以使用3dtiles的scenetree.json中获取。

部分代码

const cullingVolume = new Cesium.PerspectiveFrustum({ fov: Cesium.Math.toRadians(100.0), aspectRatio: viewer.scene.drawingBufferWidth / viewer.scene.drawingBufferHeight, far: 6, near: 0.1 }).computeCullingVolume( viewer.camera.positionWC, viewer.camera.directionWC, viewer.camera.upWC ); //创建测试camera const camera = new Cesium.Camera(viewer.scene); camera.position = new Cesium.Cartesian3( ..., ..., ... ); camera.direction = new Cesium.Cartesian3( ..., ..., ... ); camera.up = new Cesium.Cartesian3( ..., ..., ... ); camera.frustum.fov = Cesium.Math.toRadians(80.0); camera.frustum.near = 0.1; camera.frustum.far = 9; viewer.scene.primitives.add(new Cesium.DebugCameraPrimitive({ camera: camera, color: Cesium.Color.YELLOW })); // 判断是否碰撞 const visibility = cullingVolume.computeVisibility(boundingSphere); if(visibility === Cesium.Intersect.OUTSIDE) return;