在Mapbox中使用Threejs射线拾取技术

1,287 阅读2分钟

mapbox结合three带来的问题

先说遇到的坑,在纯three中实现模型的交互事件时,一般是通过射线拾取技术。
所谓射线拾取,就是利用摄像机射出一条线,跟线相交的模型就是拾取到的。而在mapbox中去加载threejs时,mapbox内部要实现一系列的坐标系转换,最终就修改了相机矩阵。所以导致我使用射线拾取根本获取不到模型。

初次踩坑:threejs中的射线拾取方法

我使用了这个方法用来射线拾取获取模型,这个方法就是标准的射线拾取,在纯three项目中没有任何的问题,但是在mapbox中就获取不到了。我在网上查了大量文章,也几乎找不到关于mapbox使用threejs添加交互的案例和解决方法,找到的也是跟我一样不对的方法。。

queryRenderedFeatures: function(point){

        var mouse = new THREE.Vector2();
        
        // // scale mouse pixel position to a percentage of the screen's width and height
        mouse.x = ( point.x / this.map.transform.width ) * 2 - 1;
        mouse.y = 1 - ( point.y / this.map.transform.height ) * 2;

        this.raycaster.setFromCamera(mouse, this.camera);

        // calculate objects intersecting the picking ray
        var intersects = this.raycaster.intersectObjects(this.world.children, true);

        return intersects
    },

解决思路

一开始获取不到模型,我也是不知道具体的问题出在哪里,因此思路如下:

  1. 检查代码,查看是否是不是因为小问题导致的,比如传错变量、漏写代码、this等
  2. 查看优秀的开源库,比如我查看了threebox的源码以及案例,找到了一些思路
  3. 查看mapbox issues,最终定位到了问题所在

问题解决

修改后的方法如下

queryRenderedFeatures(event) {
        this.mouse.x = (event.clientX / this.map.transform.width) * 2 - 1
        this.mouse.y = -(event.clientY / this.map.transform.height) * 2 + 1
        //解决射线拾取的问题
        const camInverseProjection = new THREE.Matrix4().getInverse(this.camera.projectionMatrix)
        const cameraPosition = new THREE.Vector3().applyMatrix4(camInverseProjection)
        const mousePosition = new THREE.Vector3(this.mouse.x, this.mouse.y, 1).applyMatrix4(camInverseProjection)
        const viewDirection = mousePosition
            .clone()
            .sub(cameraPosition)
            .normalize()
        this.raycaster.set(cameraPosition, viewDirection)
        let intersects = this.raycaster.intersectObjects(this.scene.children, true) //
        return intersects
    }

总结

在解决问题的过程中,思路是很重要滴,只靠搜索引擎真的很难解决问题,毕竟网上的很多文章都只是停留于表面,实现基本的业务,不会告诉你为什么这样解决。