web-dev-qa-db-fra.com

Détecter l'objet cliqué dans THREE.js

J'ai une scène THREE.js où de nombreux éléments apparaissent et je dois détecter l'objet sur lequel l'utilisateur clique.

Ce que j'ai fait jusqu'à présent est le suivant. La caméra ne bouge pas trop - elle ne change que la position verticale d'une quantité limitée, toujours en regardant vers le même point. Ma méthode approximative est la suivante:

  • Je prends les coordonnées si le clic par rapport à la toile
  • Je les traduis en coordonnées horizontales et verticales dans la scène webGL au moyen d'un simple redimensionnement, et j'ajoute une coordonnée Z suffisamment éloignée.
  • Je prends un rayon horizontal à partir du point ci-dessus, construit par THREE.Ray ()
  • J'utilise ray.intersectObjects () pour trouver le premier élément le long du rayon.

Cette méthode fonctionne approximativement, mais elle est parfois à quelques pixels du point réel.

Existe-t-il une technique plus fiable pour trouver l'objet sur lequel un utilisateur a cliqué?

25
Andrea

Cela dépend du type d'appareil photo que vous utilisez.

1) PerspectiveCamera: est un lien ok fourni par Mr.doob .
2) OrthographicCamera: est assez différent:

var init = function() {
  camera = new THREE.OrthographicCamera( SCREEN_WIDTH / - 2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / - 2, NEAR, FAR);
  document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}

function onDocumentMouseDown( e ) {
  e.preventDefault();
  var mouseVector = new THREE.Vector3();
  mouseVector.x = 2 * (e.clientX / SCREEN_WIDTH) - 1;
  mouseVector.y = 1 - 2 * ( e.clientY / SCREEN_HEIGHT );
  var raycaster = projector.pickingRay( mouseVector.clone(), camera );
  var intersects = raycaster.intersectObject( TARGET );
  for( var i = 0; i < intersects.length; i++ ) {
    var intersection = intersects[ i ],
    obj = intersection.object;
    console.log("Intersected object", obj);
  }
}
10
Luca Davanzo

Découvrez celui-ci:

var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000);
var object; //your object

document.addEventListener('mousedown', onMouseDown, false);

function onMouseDown(e) {
    var vectorMouse = new THREE.Vector3( //vector from camera to mouse
        -(window.innerWidth/2-e.clientX)*2/window.innerWidth,
        (window.innerHeight/2-e.clientY)*2/window.innerHeight,
        -1/Math.tan(22.5*Math.PI/180)); //22.5 is half of camera frustum angle 45 degree
    vectorMouse.applyQuaternion(camera.quaternion);
    vectorMouse.normalize();        

    var vectorObject = new THREE.Vector3(); //vector from camera to object
    vectorObject.set(object.x - camera.position.x,
                     object.y - camera.position.y,
                     object.z - camera.position.z);
    vectorObject.normalize();
    if (vectorMouse.angleTo(vectorObject)*180/Math.PI < 1) {
        //mouse's position is near object's position

    }
}
4
user3394964

Vérifie l'intersection de la souris et de l'un des cubes dans l'espace 3D et modifie sa couleur. Peut-être que ceci vous aidera.

3
BlindElephants

J'ai rencontré des problèmes en essayant d'implémenter cela pour une toile qui ne prend pas toute la largeur et la hauteur de l'écran. Voici la solution que j'ai trouvée fonctionne assez bien.

Initialisez tout sur une toile existante:

var init = function() {
  var canvas_model = document.getElementById('model')
  var viewSize = 50 // Depending on object size, canvas size etc.
  var camera = new THREE.OrthographicCamera(-canvas_model.clientWidth/viewSize, canvas_model.clientWidth/viewSize, canvas_model.clientHeight/viewSize, -canvas_model.clientHeight/viewSize, 0.01, 2000),
}

Ajoutez un écouteur d'événements au canevas:

canvas_model.addEventListener('click', function(event){
  var bounds = canvas_model.getBoundingClientRect()
  mouse.x = ( (event.clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
  mouse.y = - ( (event.clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;
  raycaster.setFromCamera( mouse, camera );
  var intersects = raycaster.intersectObjects(scene.children, true);
  if (intersects.length > 0) {
     // Do stuff
  }
}, false)

Ou pour un événement 'touchstart', changez les lignes calculant mouse.x et mouse.y en:

mouse.x = ( (event.touches[0].clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
mouse.y = - ( (event.touches[0].clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;
0
Evertvdw