在 THREE.js 中检测点击的对象

发布于 2024-12-12 18:30:14 字数 393 浏览 1 评论 0原文

我有一个 THREE.js 场景,其中出现了很多元素,我需要检测用户正在单击的对象。

到目前为止我所做的如下。相机不会移动太多 - 它只会有限度地改变垂直位置,始终朝向同一点。我的近似方法如下:

  • 如果相对于画布单击,我会获取坐标,
  • 通过简单的重新缩放将它们转换为 webGL 场景中的水平和垂直坐标,并添加一个足够远的 Z 坐标。
  • 我从上面的点开始采用一条由 THREE.Ray() 构造的水平射线,
  • 我使用 ray.intersectObjects() 来查找沿射线的第一个元素。

此方法大致有效,但有时与实际点相差几个像素。

是否有更可靠的技术来找出用户单击的对象?

I have a THREE.js scene where a lot of elements appear, and I need to detect what object the user is clicking on.

What I have done so far is the following. The camera does not move to much - it only changes the vertical position by a limited amount, always looking towards the same point. My approximate method is the following:

  • I take the coordinates if the click relative to the canvas
  • I translate them into horizontal and vertical coordinates in the webGL scene by means of a simple rescaling, and add a Z coordinate which is sufficiently far away.
  • I take a horizontal ray starting from the point above, constructed by THREE.Ray()
  • I use ray.intersectObjects() to find the first element along the ray.

This method approximately works, but it is sometimes a few pixels away from the actual point.

Is there a more reliable technique to find out the object where a user has clicked?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

雪落纷纷 2024-12-19 18:30:14

取决于您使用的相机类型。

1) PerspectiveCamera:是 Mr.doob 提供的链接。
2) OrthographicCamera:有很大不同:

var init = function() {
  camera = new THREE.OrthographicCamera( SCREEN_WIDTH / - 2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / - 2, NEAR, FAR);
  document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}

function onDocumentMouseDown( e ) {
  e.preventDefault();
  var mouseVector = new THREE.Vector3();
  mouseVector.x = 2 * (e.clientX / SCREEN_WIDTH) - 1;
  mouseVector.y = 1 - 2 * ( e.clientY / SCREEN_HEIGHT );
  var raycaster = projector.pickingRay( mouseVector.clone(), camera );
  var intersects = raycaster.intersectObject( TARGET );
  for( var i = 0; i < intersects.length; i++ ) {
    var intersection = intersects[ i ],
    obj = intersection.object;
    console.log("Intersected object", obj);
  }
}

Depends on what kind of camera are you using.

1) PerspectiveCamera: is ok link that Mr.doob provides.
2) OrthographicCamera: is quite different:

var init = function() {
  camera = new THREE.OrthographicCamera( SCREEN_WIDTH / - 2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / - 2, NEAR, FAR);
  document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}

function onDocumentMouseDown( e ) {
  e.preventDefault();
  var mouseVector = new THREE.Vector3();
  mouseVector.x = 2 * (e.clientX / SCREEN_WIDTH) - 1;
  mouseVector.y = 1 - 2 * ( e.clientY / SCREEN_HEIGHT );
  var raycaster = projector.pickingRay( mouseVector.clone(), camera );
  var intersects = raycaster.intersectObject( TARGET );
  for( var i = 0; i < intersects.length; i++ ) {
    var intersection = intersects[ i ],
    obj = intersection.object;
    console.log("Intersected object", obj);
  }
}
无所谓啦 2024-12-19 18:30:14

看看这个:

var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000);
var object; //your object

document.addEventListener('mousedown', onMouseDown, false);

function onMouseDown(e) {
    var vectorMouse = new THREE.Vector3( //vector from camera to mouse
        -(window.innerWidth/2-e.clientX)*2/window.innerWidth,
        (window.innerHeight/2-e.clientY)*2/window.innerHeight,
        -1/Math.tan(22.5*Math.PI/180)); //22.5 is half of camera frustum angle 45 degree
    vectorMouse.applyQuaternion(camera.quaternion);
    vectorMouse.normalize();        

    var vectorObject = new THREE.Vector3(); //vector from camera to object
    vectorObject.set(object.x - camera.position.x,
                     object.y - camera.position.y,
                     object.z - camera.position.z);
    vectorObject.normalize();
    if (vectorMouse.angleTo(vectorObject)*180/Math.PI < 1) {
        //mouse's position is near object's position

    }
}

Check out this one:

var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000);
var object; //your object

document.addEventListener('mousedown', onMouseDown, false);

function onMouseDown(e) {
    var vectorMouse = new THREE.Vector3( //vector from camera to mouse
        -(window.innerWidth/2-e.clientX)*2/window.innerWidth,
        (window.innerHeight/2-e.clientY)*2/window.innerHeight,
        -1/Math.tan(22.5*Math.PI/180)); //22.5 is half of camera frustum angle 45 degree
    vectorMouse.applyQuaternion(camera.quaternion);
    vectorMouse.normalize();        

    var vectorObject = new THREE.Vector3(); //vector from camera to object
    vectorObject.set(object.x - camera.position.x,
                     object.y - camera.position.y,
                     object.z - camera.position.z);
    vectorObject.normalize();
    if (vectorMouse.angleTo(vectorObject)*180/Math.PI < 1) {
        //mouse's position is near object's position

    }
}
南巷近海 2024-12-19 18:30:14

检查鼠标与 3d 空间中任何立方体的交集并更改其颜色。也许对您有帮助。

Checks for intersection of the mouse and any of the Cubes in 3d space and alters it's color. Maybe this help you.

吃兔兔 2024-12-19 18:30:14

我在尝试对不占据屏幕整个宽度和高度的画布实现此操作时遇到了问题。这是我发现效果很好的解决方案。

初始化现有画布上的所有内容:

var init = function() {
  var canvas_model = document.getElementById('model')
  var viewSize = 50 // Depending on object size, canvas size etc.
  var camera = new THREE.OrthographicCamera(-canvas_model.clientWidth/viewSize, canvas_model.clientWidth/viewSize, canvas_model.clientHeight/viewSize, -canvas_model.clientHeight/viewSize, 0.01, 2000),
}

向画布添加事件侦听器:

canvas_model.addEventListener('click', function(event){
  var bounds = canvas_model.getBoundingClientRect()
  mouse.x = ( (event.clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
  mouse.y = - ( (event.clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;
  raycaster.setFromCamera( mouse, camera );
  var intersects = raycaster.intersectObjects(scene.children, true);
  if (intersects.length > 0) {
     // Do stuff
  }
}, false)

或者对于“touchstart”事件,将计算 mouse.x 和 mouse.y 的行更改为:

mouse.x = ( (event.touches[0].clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
mouse.y = - ( (event.touches[0].clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;

I ran into problems trying to implement this for a canvas which does not take up the entire width and height of the screen. Here is the solution I found works quite well.

Initialize everything on an existing canvas:

var init = function() {
  var canvas_model = document.getElementById('model')
  var viewSize = 50 // Depending on object size, canvas size etc.
  var camera = new THREE.OrthographicCamera(-canvas_model.clientWidth/viewSize, canvas_model.clientWidth/viewSize, canvas_model.clientHeight/viewSize, -canvas_model.clientHeight/viewSize, 0.01, 2000),
}

Add an event listener to the canvas:

canvas_model.addEventListener('click', function(event){
  var bounds = canvas_model.getBoundingClientRect()
  mouse.x = ( (event.clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
  mouse.y = - ( (event.clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;
  raycaster.setFromCamera( mouse, camera );
  var intersects = raycaster.intersectObjects(scene.children, true);
  if (intersects.length > 0) {
     // Do stuff
  }
}, false)

Or for a 'touchstart' event, change the lines calculating the mouse.x and mouse.y into:

mouse.x = ( (event.touches[0].clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
mouse.y = - ( (event.touches[0].clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文