three.js orthographic camera object picking - three.js

i am trying to pick objects in a scene where i use an orthographic camera.
my code fragment already works, but it is not precise.
i already found some answers on stackoverflow, but those are deprecated or won't work anymore at all.
here is my code onMouseDown
function onDocumentMouseUp( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
var pos = camera.position;
var ray = new THREE.Raycaster(pos, vector.unproject(camera).sub(camera.position).normalize());
var intersects = ray.intersectObjects(objects);
if (intersects.length > 0) {
console.log("touched:" + intersects[0]);
}
else {
console.log("not touched");
}
}
please see http://jsfiddle.net/ujzpe07t/1/
if you click some pixels away left/right/above/below the cube, it still tells me that the object was touched.
i am using three.js r69.
any hints would be very much appreciated.
thanks, cheers!

Here is the pattern to use when raycasting (picking) with either an orthographic camera or a perspective camera:
var raycaster = new THREE.Raycaster(); // create once
var mouse = new THREE.Vector2(); // create once
...
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects, recursiveFlag );
three.js r.84

Related

How to appear/disappear Text on the boxgeometry using mouse hover in Threejs

I've created text on all the sides of the boxgeometry using below lines of code:-
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.filltext ("Alok", 0, 9);
but I want text should only be visible when we take the cursor on boxgeometry and disable the text
when we remove the cursor.
If anyone has any advice, any help would be greatly appreciated!
It finally worked , I've used raycaster in which i'm calling the function for text and using "mousemove"
document.addEventListener ("mousemove",( event ) => {
var vector = new THREE.Vector3();
pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.set( camera.position, vector.sub( camera.position
).normalize() );
this.textfunction();
}

Threejs object Translate and restore

I am new to Three.js. I have loaded a collada (.dae) file having multiple objects.
Now I want to translate each object on its z axis while on mouse over and restore its position when mouse left that object.
I am using "Raycaster" for this purpose.
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
raycaster = new THREE.Raycaster(camera.position, vector.sub( camera.position ).normalize());
mouse = new THREE.Vector2();
and the function is
function onDocumentMouseMove ( event ) {
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
// console.log(scene);
var intersect = raycaster.intersectObjects( scene.children[2].children);
// console.log(intersect);
if ( intersect.length > 0 )
{
}
else
{
}
}
I have the object where my mouse is intersecting but I'm not sure how to translate the object and restore it on mouse out so it looks smooth.
Edited
You need to move your intersect check out of your onDocumentMouseMove and check on your loop. onDocumentMouseMove will be called only when your moves moves, but the check will be called every frame.
Your loop should look contain the following:
raycaster.setFromCamera( mouse, camera );
var intersect = raycaster.intersectObjects(scene.children);
if ( intersect.length > 0 ) {
intersect[0].object.translateZ(1);
} else {
// For all of your objects:
cube.position.z = 0;
}
Here's a CodePen.

Three.js: not accurate to detect faceIndex when clicking the 2*2*2 geometry cube

I have created a 2*2*2 geometry cube via Three.js. Now I want to detect the click event when clicking the faces (24 faces in total).
Please check my current implementation at https://jsfiddle.net/agongdai/pdwg3myr/17/. When clicking on the faces, I want to console.log the current face index. But the index is not always accurate. For example, clicking on the top-left gray cell should show 0, but actually clicking the bottom part of it shows 2.
Please help me to check the mouse click event handler:
var raycaster = new THREE.Raycaster();
function onDocumentMouseDown( event ) {
var vector = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.setFromCamera( vector, 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 );
console.log(index);
}
}
Could anybody please help?
Em, after googling a lot, I found this page and applied the approach. It's working properly https://jsfiddle.net/agongdai/pdwg3myr/19/:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onDocumentMouseDown(event) {
mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObject(cube);
if (intersects.length > 0) {
var index = Math.floor(intersects[0].faceIndex / 2);
console.log(index);
}
}
Update
To adjust to the left/top shift and scrolling, update it to https://jsfiddle.net/agongdai/pdwg3myr/24/:
function onDocumentMouseDown(event) {
const holder = renderer.domElement;
const rect = holder.getBoundingClientRect();
mouse.x = ((event.pageX - rect.left - window.scrollX) / holder.clientWidth) * 2 - 1;
mouse.y = -((event.pageY - rect.top - window.scrollY) / holder.clientHeight) * 2 + 1;
...
}

three js highlight box while using fly controls

Is it possible to have a object highlighted while using fly controls? Like this but the boxes highlighting when you click them. What I ultimately want it to do is to click on a object and then it go onto a website. But for the moment I have tried to just highlight a object when it its clicked using:
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( mesh );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}
/*
// Parse all the faces
for ( var i in intersects ) {
intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );
}
*/
But this does not seem to work when I'm using fly controls is it possible? If not could the object just be highlighted to show some text?
I just tested this with the fly controls, its working fine. Make sure to raycast against an array that contains your meshes or just scene.children in this case.
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}
}
container.addEventListener( 'mousedown', onDocumentMouseDown, false );
Three.js r.71
Consider checking out https://github.com/jeromeetienne/threex.domevents to easily add DOM Events like click or mouseover to your meshes.

Orthographic Camera and pickingRay?

I tried to implement a function to select and move objects in a stage compound of an orthographic camera.
I'd like to get the same example as : http://threejs.org/examples/webgl_interactive_draggablecubes.html
but not with perspective camera.
I already replaced :
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
with :
var camera = new THREE.OrthographicCamera(window.innerWidth / -zoom, window.innerWidth / zoom, window.innerHeight / zoom, window.innerHeight / -zoom, -1000, 1000);
and replaced this :
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
by this :
var ray = projector.pickingRay( vector, camera );
But, it does not seem to be enough to run.
So, what's it lack ?
Is it possible to use Projector and Ray with OrthographicCamera ??
Thanks.
Please try this code in your sample:
event.preventDefault();
if (event.button === 0) {
var projector = new THREE.Projector();
var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
// use picking ray since it's an orthographic camera
var raycaster = projector.pickingRay(vector, camera);
//ditermine whether any of the supplied objects are hit by this ray
var intersertsObj = raycaster.intersectObjects(_entities);
// _entities : is array entities you put to scence
// you can store this list before you entity to scence:
// _entities.push(entity);
// scene.add(entity);
if (intersertsObj.length > 0) {
var pickedObject = intersertsObj[0];
}
}
event = null;
it is works fine in my project.
There is a function Raycaster.setFromCamera() that was introduced after this question was originally asked. It handles orthographic cameras specifically, so I think that is now the preferred way to solve this issue.
In April 2022, the following works for me to detect clicks when using an orthographic camera.
Orthographic camera:
const camera = new THREE.OrthographicCamera(
window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
-1000,
1000
);
camera.position.y = 20;
camera.zoom = 50;
camera.updateProjectionMatrix();
Click handler:
renderer.domElement.addEventListener('pointerdown', (event) => {
const mouse = new THREE.Vector2();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
console.log("Intersects:", intersects);
});

Resources