从鼠标位置发出的Three.js射线投射

8
我目前正在使用three.js和jQuery构建一个应用程序。我正在进行射线投射,并在射线击中位置放置一个帮助器。更多地说,我正在尝试实现this
我已经成功实现了这一点,但是当我移动对象的坐标并将相机移动到新位置时,我的射线投射(或至少是帮助器)会偏移。它似乎偏移大约30个像素,但随着您改变相机角度而改变,如here所示。
以下是帮助器的代码以及用于射线投射的onMouseMove事件。
// RAYCAST HELPER
            var geometry = new THREE.CylinderGeometry( 0, 5, 15, 3 ); // radius at top, radius at bottom, height, segments
            //geometry.applyMatrix( new THREE.Matrix4().makeTranslation( -50, 0, 0 ) );
            geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
            helper = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial({ color: 0xEB1515, ambient: 0xEB1515, wireframe: false }) );
            scene.add( helper );
            rays = true;

    function onMouseMove( event ) {
                    //console.log("Mouse moved");
                    $( document ).ready(function() {
                    if ( rays == true ) {

                        cX = event.clientX - $( "#info" ).width()
                        cY = event.clientY - $( "#topbar" ).height()

                        mouseVector.x = 2 * ( cX / canvaswidth ) - 1;
                        mouseVector.y = 1 - 2 * ( cY / canvasheight );
                        mouseVector.z = 1;

                        var raycaster = projector.pickingRay( mouseVector.clone(), camera );

                        for (var i = 0; i < buildingsroofs.length; i++) {
                            var intersects = raycaster.intersectObject( buildingsroofs[i]);

                            // Toggle rotation bool for meshes that we clicked
                            if ( intersects.length > 0 ) {
                                console.log("Intersection");
                                helper.position.set( 0, 0, 0 );
                                helper.lookAt( intersects[ 0 ].face.normal );
                                helper.position.copy(intersects[0].point);
                            } 
                        }
                    } //End of overarching if loop
                }) 
            } //End of onMouse function

有趣的是,我已经在这里让它工作了,但这些对象的坐标与我想要的不符。您还可以看到我正在调整顶部div和侧面div的高度,所以这似乎不是最初我想象的问题。
5个回答

10
一切都正确,你的鼠标向量 x 和 y 值(即 clientX 和 clientY)很可能与 WebGL 画布不匹配(例如,你可能从某个元素中获取这些值而不是从画布中获取)。最简单的方法是将鼠标放在画布的左上角(应该是 (0, 0)),并在单击时记录鼠标位置,以查看你的值是否不准确。同样适用于右下角,并根据画布的宽度和高度进行检查。我通过这种方式解决了类似的问题。在我的情况下,(cx, cy) 是 (-10, -10),这足以导致光线投射出现重大错误。

嗨,尼克,感谢您的回复。我最近又遇到了这个问题。我看了你的解决方案,但是鼠标坐标似乎是正确的,虽然我认为这仍然是一个很好的答案,因为它确实会影响交点位置。将我的远平面和纵横比稍微调高一些值似乎暂时解决了我的问题。 - James Milner
@Loxodromes 很高兴我能提供一些见解,但我必须说你的情况非常奇怪。我正在使用r62,即使是在最小运行的应用程序中,短距离(小于100个单位)的射线投射也非常准确。你的射线投射距离比这要大得多吗?你已经恢复到了最小测试案例吗?你是在移动设备还是桌面浏览器上工作? - Engineer
我目前在桌面浏览器上运行r67,因为我正在使用英国地理信息(英国国家网格坐标),所以确实使用了较大的单位,目前最高达到约600000,这可能是导致问题的原因。幸运的是,调整视锥和FOV似乎解决了问题,尽管我无法真诚地告诉您为什么。在此之前,我确实尝试过调整光线投射器的精度,但那似乎也没有任何效果。 - James Milner

4

在这方面我遇到了很多困难,但我仍然不确定为什么这会起作用,但是这个计算x和y的方法对我来说是可行的。

let canvas = document.querySelector('canvas');

let x = (event.offsetX / canvas.clientWidth) * 2 - 1;
let y = -(event.offsetY / canvas.clientHeight) * 2 + 1;

3

如果您正在使用 ClientX 和 ClientY,请使用 event.OffsetX 和 event.OffsetY。


3

奇怪的是,当将透视相机的近参数从0.1更改为1时,问题似乎自行解决了。

camera = new THREE.PerspectiveCamera(45, canvaswidth / canvasheight , 0.1, 10000 );

致:

camera = new THREE.PerspectiveCamera(45, canvaswidth / canvasheight , 1, 10000 ); 

我仍然不确定为什么这个方法解决了问题,但它似乎起作用了。
编辑:将视场从45更改为60消除了在更改近摄像机参数后仍然发生的光线投射中的轻微畸变。

我花了好几个小时来研究这个问题,谢谢! - Russ

0
因为我没有使用全屏分辨率作为我的WebGL画布的大小,所以我需要进行替代计算来减去窗口和画布之间的距离,以定义我的鼠标实际悬停的位置。
function onDocumentMouseMove( event ) {
    mouseVector.x = (event.offsetX / (window.innerWidth - 240)) * 2 - 1;
    mouseVector.y = -(event.offsetY / (window.innerHeight - 100)) * 2 + 1;
}
// when the mouse moves, call the given function
document.addEventListener( 'mousemove', onDocumentMouseMove, false );

一旦我的鼠标坐标针对画布大小正确计算,其内部的对象就会更准确地反应。(此解决方案不操作摄像机,仅操作返回的鼠标坐标。)

希望这能有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接