【问题标题】:How to get object in WebGL 3d space from a mouse click coordinate如何从鼠标单击坐标获取 WebGL 3d 空间中的对象
【发布时间】:2011-11-13 22:45:42
【问题描述】:

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

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

【问题讨论】:

  • 你不是在说这个吧? 3dkingdoms.com/selection.html#ray 只需将法线从相机镜头放到平面上,然后找出 POI。
  • 我认为this 是对我想做的事情的一般描述,但我无法从他的描述中找出算法。
  • 必须进行某种计算,将视口想象成一个平面,位于 (0,0,0) 处,矩形的中心位于原点。我会更多地考虑这一点,我相信有一个优雅的解决方案。

标签: javascript 3d mouse webgl coordinate


【解决方案1】:

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

我在jax/camera.js#L568 上提供了一个非投影示例——但您仍然需要实现射线/三角形相交。我在jax/triangle.js#L113 有一个实现。

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

这两种方法各有利弊(在here 和之后的一些 cmets 中讨论过),您需要弄清楚哪种方法最能满足您的需求。大型场景的拾取速度较慢,但​​在纯 JS 中取消投影非常慢(因为 JS 本身并不是那么快),所以我最好的建议是尝试两者。

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

【讨论】:

  • 澄清一下:WebGL中的picking通常比unprojecting更快,纯粹因为JS很慢,picking利用GPU进行硬件加速,从而绕过了JS的瓶颈。在传统的 C 实现中,不投影可能是一种可行的方法,尤其是当您只需要检查非常少量的三角形时。
  • 哇,非常感谢,我会调查所有这些。这完美地回答了我的问题。
  • 那么,如果我需要知道确切的三角形/顶点,仅选择是不够的?
  • 正确。你可以通过用不同的选择颜色绘制每个三角形来解决这个问题,但这会非常慢(我怀疑它在 JavaScript 中是否可行,但发生了奇怪的事情),因为它需要为每个三角形重新传递。对于精确的三角形/顶点信息,最好的选择是不投影。
  • 为澄清起见,“采摘”一词与所使用的任何特定技术无关。此外,当屏幕上有几千个对象可见时,通过 colorids 选择代码的结构可能会变得非常慢。这就是为什么我建议使用适当的空间细分(适用于场景和网格三角形)进行光线拾取。
【解决方案2】:

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

  1. 渲染对象以选择缓冲区,每个对象都具有独特的颜色
  2. 读取缓冲区像素,映射回拾取的对象
  3. 将拾取的对象渲染到缓冲区,每个像素颜色是 Z 深度的函数
  4. 读取缓冲区像素,映射回 Z 深度
  5. 我们已经为拾取坐标选择了对象和近似 Z

【讨论】:

    【解决方案3】:

    这是工作演示

    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/

    【讨论】:

    • 问题是关于 webGL,它不使用 ThreeJS。
    【解决方案4】:

    从其中一个线程中获得。 不确定 (x,y,z) 但您可以使用

    获得canvas(x,y)

    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};
    }
    

    【讨论】:

      猜你喜欢
      • 2012-06-08
      • 2015-05-11
      • 1970-01-01
      • 2022-01-07
      • 2019-01-21
      • 2016-04-14
      • 2010-10-24
      • 1970-01-01
      相关资源
      最近更新 更多