The function below works perfectly for orienting objects towards the user's cursor, if the object is centered at world origin, but it does not work otherwise.
function onMouseMove( e ) {
var mouse3D = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1,
0.5);
eyes.forEach(function(element, index, array) {
element.lookAt(mouse3D);
})
}
How would I go about offsetting the lookat target (mousePosition3D) to work with non world origin objects? Is there a better way of getting a bunch of randomly placed objects orienting towards the user's cursor?
Related
I'm very new to three js and is currently trying to implement a feature where the user can zoom in where the cursor is. The plan is to use a raycaster to get the point of intersection and then and use it to update the vector of the orbit controls every time the cursor moves.
the orbit control is initialized like so
this.controls = new OrbitControls( this.camera_, this.threejs_.domElement );
this.controls.listenToKeyEvents( window );
this.controls.screenSpacePanning = false;
this.controls.minDistance = 30;
this.controls.maxDistance = 500;
this.controls.maxPolarAngle = Math.PI / 2;
this is the eventlistener
document.addEventListener('pointermove', (e) => this.onPointerMove(e), false);
and the onPointerMove function looks like this
onPointerMove(event){
const pointer = {
x: (event.clientX / window.innerWidth) * 2 - 1,
y: -(event.clientY / window.innerHeight) * 2 + 1,
}
this.rayCaster.setFromCamera( pointer, this.camera_);
const intersects = this.rayCaster.intersectObjects( this.scene_.children, false);
if ( intersects.length > 0 ) {
this.controls.target(intersects[0].point);
this.controls.update();
}
}
so far, intersects[0].point seems to be getting the intersect coordinate correctly but the orbit control is simply not getting updated. I have also tried changing the camera's position using
this.camera_.position.set(intersects[0].point.x+20,intersects[0].point.y+20,intersects[0].point.z+20);
this.controls.update();
however that just moves my camera everywhere i point.
Edit:
this doesnt work either
const newTarget = new Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z);
this.controls.target.copy(newTarget);
found the answer here.
Apparently you need to use either copy or set to change the target of the orbit controls. Without calling update().
like so
this.controls.target.set(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z);
I am trying to do what the title says.
When a user clicks on a face of the cube, that face will change colour.
This is my code snippet:
// create a cube
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xffff00 }); //0xF7F7F7 = gray
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.userData.originalColor = 0xffff00;
function onDocumentMouseClick(event) //if we detect a click event
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObject( cube );
if ( intersects.length > 0 )
{
var index = Math.floor( intersects[0].faceIndex / 2 );
switch (index)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
}
}
}
This code is incomplete.
My problems are these:
I don't know if this strategy is correct and works
I don't know what code to use inside the different cases, in order to paint that side of the cube.
First, consider breaking the cube's faces into draw groups. This answer delves into how to do that.
The idea is that all of the cube's faces will have the same color, but not the same material. This will allow you to change the material's colors individually, and as such you will individually change the color of the cube faces.
When you get a response back from the Raycaster, look for the faceIndex property. This will tell you the index of the face that was intersected. Find which draw group that index belongs to, and you can then reference that group's material index, and thus change the color of the material.
I use function like this in three.js 69
function Point3DToScreen2D(point3D,camera){
var p = point3D.clone();
var vector = p.project(camera);
vector.x = (vector.x + 1) / 2 * window.innerWidth;
vector.y = -(vector.y - 1) / 2 * window.innerHeight;
return vector;
}
It works fine when i keep the scene still.
But when i rotate the scene it made a mistake and return wrong position in the screen.It occurs when i rotate how about 180 degrees.It shoudn't have a position in screen but it showed.
I set a position var tmpV=Point3DToScreen2D(new THREE.Vector3(-67,1033,-2500),camera); in update and show it with css3d.And when i rotate like 180 degrees but less than 360 , the point shows in the screen again.Obviously it's a wrong position that can be telled from the scene and i haven't rotate 360 degrees.
I know little about the Matrix,So i don't know how the project works.
Here is the source of project in three.js:
project: function () {
var matrix;
return function ( camera ) {
if ( matrix === undefined ) matrix = new THREE.Matrix4();
matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
return this.applyProjection( matrix );
};
}()
Is the matrix.getInverse( camera.matrixWorld ) redundant? I tried to delete it and it didn't work.
Can anyone help me?Thanks.
You are projecting a 3D point from world space to screen space using a pattern like this one:
var vector = new THREE.Vector3();
var canvas = renderer.domElement;
vector.set( 1, 2, 3 );
// map to normalized device coordinate (NDC) space
vector.project( camera );
// map to 2D screen space
vector.x = Math.round( ( vector.x + 1 ) * canvas.width / 2 ),
vector.y = Math.round( ( - vector.y + 1 ) * canvas.height / 2 );
vector.z = 0;
However, using this approach, points behind the camera are projected to screen space, too.
You said you want to filter out points that are behind the camera. To do that, you can use this pattern first:
var matrix = new THREE.Matrix4(); // create once and reuse
...
// get the matrix that maps from world space to camera space
matrix.getInverse( camera.matrixWorld );
// transform your point from world space to camera space
p.applyMatrix( matrix );
Since the camera is located at the origin in camera space, and since the camera is always looking down the negative-z axis in camera space, points behind the camera will have a z-coordinate greater than zero.
// check if point is behind the camera
if ( p.z > 0 ) ...
three.js r.71
Like the example above but you can check vector.z to determine if it's in front.
var vector = new THREE.Vector3();
var canvas = renderer.domElement;
vector.set( 1, 2, 3 );
// map to normalized device coordinate (NDC) space
vector.project( camera );
// map to 2D screen space
vector.x = Math.round( ( vector.x + 1 ) * canvas.width / 2 ),
vector.y = Math.round( ( - vector.y + 1 ) * canvas.height / 2 );
//behind the camera if z isn't in 0..1 [frustrum range]
if(vector.z > 1){
vector = null;
}
To delve a little deeper into this answer:
// behind the camera if z isn't in 0..1 [frustrum range]
if(vector.z > 1){
vector = null;
}
This is not true. The mapping is not continuous. Points beyond the far
plane also map to z-values greater than 1
What exactly does the z-value of a projected vector stand for? X and Y are in normalised clipspace [-1,1] , what about z?
Would this be true?
projectVector.project(camera);
var inFrontOfCamera = projectVector.z < 1;
Since the camera is located at the origin in camera space, and since the camera is always looking down the negative-z axis in camera space, points behind the camera will have a z-coordinate greater than 1.
//check if point is behind the camera
if ( p.z > 1 ) ...
NOTICE: If this condition is satisfied, then the projected coordinates need to be centrosymmetric
{x: 0.233, y: -0.566, z: 1.388}
// after transform
{x: -0.233, y: 0.566, z: 1.388}
Three.js r70 source code
setFromCamera: function ( coords, camera ) {
// camera is assumed _not_ to be a child of a transformed object
if ( camera instanceof THREE.PerspectiveCamera ) {
this.ray.origin.copy( camera.position );
this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( camera.position ).normalize();
}
this line,set the mouse's position and z's default value 0.5.
this.ray.direction.set( coords.x, coords.y, 0.5 )
most example code about raycasting like
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
I wonder how it works ,what does the ( event.clientX / window.innerWidth ) * 2 - 1 mean, and why set the z 0.5?
event its every time your mouse click client.x is the mouse position on x-axiz width.innerheight is how height your screen size is when you have a 2d mouse you have to separate it so when you are on left half of screen you go left and right you go right so you divide the screen height in 2 and compare where the 1 on the end mouse the mouse registration around so if you want to offset the crosshairs you can do to
function onDocumentMouseMove(e) {
e.preventDefault();
var fleece = window.document.body.scrollHeight;
var fleeceb = window.document.body.scrollWidth;
mouse.x = (e.clientX / fleeceb) * 2 - 1;
mouse.y = - (e.clientY / fleece) * 2 + 1;
}
I have a problem with picking implementation. I found a number of different examples doing what I want, but I really cannot make it work as it should. I mainly followed this example
Basically, I have some meshes in my scene and, double clicking any of them, I'd like to change the color of the chosen one. In the scene there are 3 small cubes that are always selected and some more complex meshes that often aren't. I'd like to know if anybody can help me figuring why, if the cubes can be selected, the others often can't.
The code I used to detect the clicked mesh is:
var projector = new THREE.Projector();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 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, true );
if ( intersects.length > 0 )
{
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}
ray.intersectObject(), which is called by ray.intersectObjects(), requires face centroids.
mesh.geometry.computeCentroids();
This is important if you are creating your own custom geometries.
three.js r.51
As of r54 this is no longer necessary. See comment from WestLangley