Javascript 如何从鼠标单击坐标获取 WebGL 3d 空间中的对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7364693/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 01:58:33  来源:igfitidea点击:

How to get object in WebGL 3d space from a mouse click coordinate

javascript3dmousewebglcoordinate

提问by seibelj

I'm building a boardgame in WebGL. The board can be rotated/zoomed. I need a way to translate a click on the canvas element (x,y) into the relevant point in 3D space (x, y, z). The ultimate result is that I want to know the (x, y, z) coordinate that contains the point that touches the object closest to the user. For instance, the user clicks a piece, and you imagine a ray traveling through 3D space that goes through both the piece and the game board, but I want the (x, y, z) coord of the piece at the point where it was touched.

我正在 WebGL 中构建一个棋盘游戏。该板可以旋转/缩放。我需要一种方法将画布元素 (x,y) 上的点击转换为 3D 空间 (x, y, z) 中的相关点。最终的结果是我想知道(x,y,z)坐标,该坐标包含与用户最近的物体接触的点。例如,用户点击一个棋子,你想象一条射线穿过 3D 空间,穿过棋子和游戏板,但我想要棋子的 (x, y, z) 坐标在它所在的点感动。

I feel like this must be a very common problem, but I can't seem to find a solution in my googles. There must be some way to project the current view of the 3D space into 2D so you can map each point in 2D space to the relevant point in 3D space. I want to the user to be able to mouse over a space on the board, and have the spot change color.

我觉得这一定是一个非常普遍的问题,但我似乎无法在我的谷歌中找到解决方案。必须有某种方法将 3D 空间的当前视图投影到 2D 中,以便您可以将 2D 空间中的每个点映射到 3D 空间中的相关点。我希望用户能够将鼠标悬停在板上的空间上,并让专色更改颜色。

回答by sinisterchipmunk

You're looking for an unproject function, which converts screen coordinates into a ray cast from the camera position into the 3D world. You must then perform ray/triangle intersection tests to find the closest triangle to the camera which also intersects the ray.

您正在寻找一个 unproject 函数,它将屏幕坐标转换为从相机位置投射到 3D 世界的光线。然后,您必须执行光线/三角形相交测试,以找到距离相机最近且也与光线相交的三角形。

I have an example of unprojecting available at jax/camera.js#L568-- but you'll still need to implement ray/triangle intersection. I have an implementation of that at jax/triangle.js#L113.

我在jax/camera.js#L568有一个取消投影的例子——但你仍然需要实现光线/三角形相交。我在jax/triangle.js#L113有一个实现。

There is a simpler and (usually) faster alternative, however, called 'picking'. Use this if you want to select an entire object (for instance, a chess piece), and if you don't care about where the mouse actually clicked. The WebGL way to do this is to render the entire scene in various shades of blue (the blue is a key, while red and green are used for unique IDs of the objects in the scene) to a texture, then read back a pixel from that texture. Decoding the RGB into the object's ID will give you the object that was clicked. Again, I've implemented this and it's available at jax/world.js#L82. (See also lines 146, 162, 175.)

然而,有一种更简单且(通常)更快的替代方法,称为“挑选”。如果您想选择整个对象(例如,棋子),并且您不关心鼠标实际单击的位置,请使用此选项。WebGL 的方法是将整个场景以各种深浅不同的蓝色(蓝色是关键,而红色和绿色用于场景中对象的唯一 ID)渲染到纹理,然后从那种质地。将 RGB 解码为对象的 ID 将为您提供单击的对象。同样,我已经实现了它,它可以在jax/world.js#L82 上找到。(另见第 146、162、175 行。)

Both approaches have pros and cons (discussed hereand in some of the comments after) and you'll need to figure out which approach best serves your needs. Picking is slower with huge scenes, but unprojecting in pure JS is extremely slow (since JS itself isn't all that fast) so my best recommendation would be to experiment with both.

这两种方法都有利有弊(在此处和之后的一些评论中进行了讨论),您需要确定哪种方法最能满足您的需求。大场景的选择速度较慢,但​​在纯 JS 中取消投影非常慢(因为 JS 本身并不是那么快)所以我最好的建议是对两者进行试验。

FYI, you could also look at the GLU project and unproject code, which I based my code loosely upon: http://www.opengl.org/wiki/GluProject_and_gluUnProject_code

仅供参考,您还可以查看 GLU 项目和取消项目代码,我的代码松散地基于这些代码:http: //www.opengl.org/wiki/GluProject_and_gluUnProject_code

回答by xeolabs

I'm working on this problem at the moment - the approach I'm taking is

我目前正在解决这个问题 - 我采取的方法是

  1. Render objects to pick buffer each with unique colour
  2. Read buffer pixel, map back to picked object
  3. Render picked object to buffer with each pixel colour a function of Z-depth
  4. Read buffer pixel, map back to Z-depth
  5. We have picked object and approximate Z for the pick coords
  1. 渲染对象以选择每个具有独特颜色的缓冲区
  2. 读取缓冲区像素,映射回拾取的对象
  3. 渲染拾取的对象以缓冲每个像素颜色的 Z 深度函数
  4. 读取缓冲区像素,映射回 Z 深度
  5. 我们已经拾取了对象并为拾取坐标近似了 Z

回答by Tomasz Drozdowski

This is the working demo

这是工作演示

function onMouseUp(event) {

    event.preventDefault();        
    x_pos = (event.clientX / window.innerWidth) * 2 - 1;
    y_pos = -(event.clientY / window.innerHeight) * 2 + 1;
    z_pos = 0.5;

    var vector = new THREE.Vector3( x_pos , y_pos , z_pos );

    var projector = new THREE.Projector();
    projector.unprojectVector(vector, camera);
    var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
    var intersects = raycaster.intersectObjects(intersectObjects);

    if (intersects.length > 0) {

        xp = intersects[0].point.x.toFixed(2);
        yp = intersects[0].point.y.toFixed(2);
        zp = intersects[0].point.z.toFixed(2);  
        destination = new THREE.Vector3( xp , yp , zp );

        radians =  Math.atan2( ( driller.position.x - xp) , (driller.position.z - zp));
        radians += 90 * (Math.PI / 180);
        console.log(radians);

        var tween = new TWEEN.Tween(driller.rotation).to({ y : radians },200).easing(TWEEN.Easing.Linear.None).start();

    }

weissner-doors.de/drone/

weissner-doors.de/drone/

回答by Naruto

culted from one of the threads. not sure about (x,y,z) but you can get the canvas(x,y)using

从其中一个线程中培养出来的。不确定 (x,y,z) 但你可以得到canvas(x,y)使用

getBoundingClientRect()

getBoundingClientRect()

function getCanvasCoord(){
  var mx = event.clientX;
  var my = event.clientY;
  var canvas = document.getElementById('canvasId');
  var rect = canvas.getBoundingClientRect();// check if your browser supports this
  mx = mx - rect.left;
  my = my - rect.top;
  return {x: mx , y: my};
}