鼠标/画布X,Y到Three.js世界X,Y,Z

Rob*_*ans 57 point projection mouse-coordinates three.js

我一直在寻找一个与我的用例相匹配的例子,却找不到一个.我正在尝试将屏幕鼠标坐标转换为考虑到相机的3D世界坐标.

解决方案我发现所有人都做射线交叉以实现对象拾取.

我想要做的是将Three.js对象的中心定位在鼠标当前"结束"的坐标上.

我的相机位于x:0,y:0,z:500(虽然它会在模拟过程中移动)并且我的所有物体都在z = 0时具有不同的x和y值,所以我需要知道基于X,Y的世界假设az = 0表示跟随鼠标位置的对象.

这个问题看起来像一个类似的问题,但没有解决方案:在THREE.js中获取鼠标相对于3D空间的坐标

给定屏幕上的鼠标位置,其范围为"左上角= 0,0 |右下角= window.innerWidth,window.innerHeight",任何人都可以提供将Three.js对象移动到鼠标坐标的解决方案沿z = 0?

Wes*_*ley 112

您不需要在场景中有任何对象来执行此操作.

你已经知道相机的位置了.

使用vector.unproject( camera )你可以获得指向你想要的方向的光线.

您只需要从摄像机位置延伸该光线,直到光线尖端的z坐标为零.

你可以这样做:

var vec = new THREE.Vector3(); // create once and reuse
var pos = new THREE.Vector3(); // create once and reuse

vec.set(
    ( event.clientX / window.innerWidth ) * 2 - 1,
    - ( event.clientY / window.innerHeight ) * 2 + 1,
    0.5 );

vec.unproject( camera );

vec.sub( camera.position ).normalize();

var distance = - camera.position.z / vec.z;

pos.copy( camera.position ).add( vec.multiplyScalar( distance ) );
Run Code Online (Sandbox Code Playgroud)

变量pos是3D空间中的点的位置,"鼠标下方"和平面中的位置z=0.


编辑:如果您需要"在鼠标下"和平面中的点z = targetZ,请将距离计算替换为:

var distance = ( targetZ - camera.position.z ) / vec.z;
Run Code Online (Sandbox Code Playgroud)

three.js r.98

  • 我想我明白了.如果用`var distance =(targetZ - camera.position.z)/ dir.z;`替换`var distance = -camera.position.z/dir.z;`,你可以指定`z`值(如`targetZ`). (7认同)
  • 我对同一个问题的完美答案.可以提一下,投影机可以实例化,无论如何都不需要设置 - 投影仪=新的THREE.Projector(); (4认同)
  • @WestLangley - 感谢您的详细阐述,我想分享这个[链接](https://www.youtube.com/watch?v=Ck1SH7oYRFM),它也帮助我解决了问题。这个想法是, Threejs 空间中的所有内容都可以用 -1 到 1 (NDC) 之间的坐标 xyz 来描述。对于 x 和 y,通过重新规范化 event.client 变量来完成,对于 z,通过选择 -1 到 1 之间的任意值(即 0.5)来完成。选择的值对代码的其余部分没有影响,因为我们使用它来定义沿射线的方向。 (3认同)
  • @WestLangley - 感谢您的精彩回答。我只有一个疑问,你能解释一下为什么第 6 行向量的 z 坐标设置为 0.5 吗?当我们想要指定不同于 0 的 z 值时(如 SCCOTTT 的情况),该行是否也会有所不同? (2认同)
  • @Matteo 该代码“取消保护”从标准化设备坐标(NDC)空间到世界空间的点。0.5 值是任意的。如果您不理解这个概念,请谷歌 NDC 空间。 (2认同)
  • @WestLangley - 最后一件事.声明`unproject`函数返回threejs空间中的点是否正确,该点投射到向量中给定的坐标x,y的2d点?有一些点映射到该2d点,因此我们要求具有指定z值的点. (2认同)
  • @nnyby 这个答案中的 `vec.x` 和 `vec.y` 必须在画布边缘返回 -1 和 +1;0 位于中心。请参阅[此答案](/sf/answers/948099421/),了解如何确保画布位于 div 内。 (2认同)

KTC*_*TCO 14

当使用时这对我有用orthographic camera

let vector = new THREE.Vector3();
vector.set(
  (event.clientX / window.innerWidth) * 2 - 1,
  - (event.clientY / window.innerHeight) * 2 + 1,
  0,
);
vector.unproject(camera);
Run Code Online (Sandbox Code Playgroud)

WebGL 三.js r.89

  • 如果使用 **React Three Fiber**,您可以进一步缩短此时间。上面的公式在取消投影之前将光标位置转换为 NDC 空间(https://thirdjs.org/docs/#api/en/math/Vector3.unproject),但 RTF 已经在 `useThree().mouse` 中为您计算了这一点(即 `const { mouse } = useThree(); vector.set(mouse.x, mouse.y, 0); vector.unproject(camera);`) (3认同)

laz*_*ymf 5

在r.58中,这段代码对我有用:

var planeZ = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
var mv = new THREE.Vector3(
    (event.clientX / window.innerWidth) * 2 - 1,
    -(event.clientY / window.innerHeight) * 2 + 1,
    0.5 );
var raycaster = projector.pickingRay(mv, camera);
var pos = raycaster.ray.intersectPlane(planeZ);
console.log("x: " + pos.x + ", y: " + pos.y);
Run Code Online (Sandbox Code Playgroud)

  • 为什么0.5?看起来0.5可以是任何东西,因为它是正常的方向.我已经尝试过其他数字,但它似乎没有任何区别. (3认同)

dro*_*ne1 5

下面是我根据 WestLangley 的回复编写的 ES6 类,它在 THREE.js r77 中非常适合我。

请注意,它假设您的渲染视口占据整个浏览器视口。

class CProjectMousePosToXYPlaneHelper
{
    constructor()
    {
        this.m_vPos = new THREE.Vector3();
        this.m_vDir = new THREE.Vector3();
    }

    Compute( nMouseX, nMouseY, Camera, vOutPos )
    {
        let vPos = this.m_vPos;
        let vDir = this.m_vDir;

        vPos.set(
            -1.0 + 2.0 * nMouseX / window.innerWidth,
            -1.0 + 2.0 * nMouseY / window.innerHeight,
            0.5
        ).unproject( Camera );

        // Calculate a unit vector from the camera to the projected position
        vDir.copy( vPos ).sub( Camera.position ).normalize();

        // Project onto z=0
        let flDistance = -Camera.position.z / vDir.z;
        vOutPos.copy( Camera.position ).add( vDir.multiplyScalar( flDistance ) );
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用该类:

// Instantiate the helper and output pos once.
let Helper = new CProjectMousePosToXYPlaneHelper();
let vProjectedMousePos = new THREE.Vector3();

...

// In your event handler/tick function, do the projection.
Helper.Compute( e.clientX, e.clientY, Camera, vProjectedMousePos );
Run Code Online (Sandbox Code Playgroud)

vProjectedMousePos 现在包含 z=0 平面上的投影鼠标位置。