2011-10-31 6 views
18

J'ai une scène THREE.js où beaucoup d'éléments apparaissent, et j'ai besoin de détecter sur quel objet l'utilisateur clique.Détecter un objet cliqué dans THREE.js

Ce que j'ai fait jusqu'à présent est le suivant. La caméra ne bouge pas beaucoup - elle change seulement la position verticale d'une quantité limitée, en regardant toujours 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 traduire en coordonnées horizontales et verticales de la scène WebGL au moyen d'un renormalisation simple, et ajouter un Z coordonnée qui est suffisamment loin.
  • 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 éloignée de quelques pixels du point réel.

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

+0

Margin et le bourrage peuvent être en train de provoquer un décalage de vos coordonnées. L'avez-vous expliqué? – Prusse

+0

Pour le moment dans la démo, il n'y a pas de marge ni de paddings, mais la technique que j'ai décrite n'est pas exacte de toute façon. – Andrea

+4

Jetez un coup d'œil à [cet exemple] (http://mrdoob.github.com/three.js/examples/canvas_interactive_cubes.html). –

Répondre

7

Dépend du type d'appareil photo utilisé.

1) PerspectiveCamera: est ok lien qui Mr.doob fournit.
2) OrthographicCamera: est tout à fait 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); 
    } 
} 
+0

"projector.pickingRay" est supprimé? –

+2

ici une solution alternative https://github.com/mrdoob/three.js/issues/5587 –

3

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 

    } 
} 
+0

c'est une très bonne solution et empêche une raycast intensive du processeur, mais ne voit que si la souris clique près du pivot de l'objet . Avantage est qu'un objet sans mesh aussi fonctionnerait de cette façon. Le désavantage est le "point chaud" circulaire. – Hacky

2

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

+3

Bienvenue dans Stack Overflow! Prenez une minute pour lire [Comment répondre] (http://stackoverflow.com/questions/how-to-answer) - cela semble utile, mais il serait utile d'avoir une explication de ce que le code fait, envisager [modifier] (http://stackoverflow.com/posts/41385887/edit)-ing cela dans? –

0

J'ai rencontré des problèmes en essayant d'implémenter ceci 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 très bien.

Initialiser 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), 
} 

Ajouter un écouteur d'événement sur la toile:

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 », modifiez les lignes du calcul de la mouse.x et mouse.y dans:

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; 
Questions connexes