Mouse / Canvas X, Y to Three.js World X, Y, Z
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
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
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.
to get the mouse coordinates of a 3d object use projectVector:
var width = 640, height = 480;
var widthHalf = width / 2, heightHalf = height / 2;
var projector = new THREE.Projector();
var vector = projector.projectVector( object.matrixWorld.getPosition().clone(), camera );
vector.x = ( vector.x * widthHalf ) + widthHalf;
vector.y = - ( vector.y * heightHalf ) + heightHalf;
to get the three.js 3D coordinates that relate to specific mouse coordinates, use the opposite, unprojectVector:
var elem = renderer.domElement,
boundingRect = elem.getBoundingClientRect(),
x = (event.clientX - boundingRect.left) * (elem.width / boundingRect.width),
y = (event.clientY - boundingRect.top) * (elem.height / boundingRect.height);
var vector = new THREE.Vector3(
( x / WIDTH ) * 2 - 1,
- ( y / HEIGHT ) * 2 + 1,
0.5
);
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( scene.children );
There is a great example here. However, to use project vector, there must be an object where the user clicked. intersects will be an array of all objects at the location of the mouse, regardless of their depth.