2011-10-31 21 views
18

Tengo una escena THREE.js donde aparecen muchos elementos y necesito detectar en qué objeto está haciendo clic el usuario.Detectar objeto cliqueado en THREE.js

Lo que he hecho hasta ahora es el siguiente. La cámara no se mueve demasiado; solo cambia la posición vertical en una cantidad limitada, siempre mirando hacia el mismo punto. Mi método aproximado es el siguiente:

  • tomo las coordenadas si el clic relativa a la lona
  • que traducirlos en coordenadas horizontales y verticales en la escena WebGL por medio de un simple cambio de escala, y añadir una Z coordenada que está lo suficientemente lejos.
  • Tomo un rayo horizontal a partir del punto anterior, construido por THREE.Ray()
  • Uso ray.intersectObjects() para encontrar el primer elemento a lo largo del rayo.

Este método funciona aproximadamente, pero a veces está a unos pocos píxeles del punto real.

¿Existe una técnica más confiable para encontrar el objeto donde un usuario ha hecho clic?

+0

El margen y el relleno pueden estar causando que sus coordenadas estén un poco desplazadas. ¿Lo contabilizaste? – Prusse

+0

Por el momento en la demostración no hay margen ni rellenos, pero la técnica que describí no es exacta de todos modos. – Andrea

+4

Eche un vistazo a [este ejemplo] (http://mrdoob.github.com/three.js/examples/canvas_interactive_cubes.html). –

Respuesta

7

Depende de qué tipo de cámara estás utilizando.

1) PerspectiveCamera: está bien vínculo que Mr.doob proporciona.
2) OrthographicCamera: es bastante diferente:

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

¿se ha eliminado "projector.pickingRay"? –

+2

aquí una solución alternativa https://github.com/mrdoob/three.js/issues/5587 –

3

Salida ésta:

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

Ésta es una solución muy agradable y evita un raycast intensivo de la CPU, pero sólo ve si los clics del ratón cerca del pivote del objeto . La ventaja es que un objeto sin malla funcionaría de esta manera. La desventaja es el "punto de acceso" circular. – Hacky

2

Cheques para intersección del ratón y cualquiera de los cubos en el espacio 3D y altera su color. Tal vez this te ayude.

+3

¡Bienvenido a Stack Overflow! Tome un minuto para leer [Como respuesta] (http://stackoverflow.com/questions/how-to-answer) - esto parece útil, pero que se beneficiarían de una explicación de lo que hace el código, considere [editar] (http://stackoverflow.com/posts/41385887/edit)-ing that in? –

0

Tuve algunos problemas al intentar implementar esto para un lienzo que no ocupa todo el ancho y alto de la pantalla. Aquí está la solución que encontré funciona bastante bien.

Inicializar todo en un lienzo existentes:

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

Añadir un detector de eventos a la lona:

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) 

o para un evento 'touchstart', cambiar las líneas y el cálculo de la mouse.x 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;