Mouse / Canvas X, Y to Three.js World X, Y, Z

Below is an ES6 class I wrote based on WestLangley's reply, which works perfectly for me in THREE.js r77.

Note that it assumes your render viewport takes up your entire browser viewport.

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

You can use the class like this:

// 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 );

vProjectedMousePos now contains the projected mouse position on the z=0 plane.


In r.58 this code works for me:

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

This worked for me when using an 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);

WebGL three.js r.89


You do not need to have any objects in your scene to do this.

You already know the camera position.

Using vector.unproject( camera ) you can get a ray pointing in the direction you want.

You just need to extend that ray, from the camera position, until the z-coordinate of the tip of the ray is zero.

You can do that like so:

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

The variable pos is the position of the point in 3D space, "under the mouse", and in the plane z=0.


EDIT: If you need the point "under the mouse" and in the plane z = targetZ, replace the distance computation with:

var distance = ( targetZ - camera.position.z ) / vec.z;

three.js r.98