Souris / Toile X, Y à trois.js World X, Y, Z

j'ai cherché un exemple qui correspond à mon cas d'utilisation mais je n'en trouve pas. J'essaie de convertir les coordonnées de la souris en coordonnées 3D en tenant compte de la caméra.

Solutions j'ai trouvé toutes les intersections de rayons do pour réaliser la cueillette d'objet.

ce que j'essaie de faire, c'est positionner le centre d'un trois.objet js aux coordonnées que la souris est actuellement "terminée".

mon appareil est à x:0, y:0, z: 500 (bien qu'il se déplace pendant la simulation) et tous mes objets sont à z = 0 avec des valeurs de x et y variables donc j'ai besoin de connaître le monde X, Y basé sur l'hypothèse d'un z = 0 pour l'objet qui suivra la position de la souris.

cette question ressemble à un problème similaire, mais n'a pas de solution: obtenir des coordonnées de la souris en relation avec L'espace 3D en trois.js

étant donné la position de la souris sur l'écran avec un gamme de "en haut à gauche = 0, 0 | bas-droite = fenêtre.innerWidth, fenêtre.innerHeight", quelqu'un peut-il fournir une solution pour déplacer un Trois.js objet à la souris coordonne le long de z = 0?

51
demandé sur Community 2012-10-24 22:12:54

8 réponses

vous n'avez pas besoin d'avoir des objets dans votre scène pour faire cela.

vous connaissez déjà la position de la caméra.

en utilisant vector.unproject( camera ) vous pouvez obtenir un rayon pointant dans la direction que vous voulez.

vous avez juste besoin d'étendre ce rayon, de la position de la caméra, jusqu'à ce que la coordonnée z de l'extrémité du rayon est zéro.

Vous pouvez le faire comme ceci:

var vec = new THREE.Vector3(); // create once and reuse
var pos = new THREE.Vector3(); // create once and reuse

vec.set(
    ( event.clientX / window.innerWidth ) * 2 - 1,
    - ( event.clientY / window.innerHeight ) * 2 + 1,
    0.5 );

vec.unproject( camera );

vec.sub( camera.position ).normalize();

var distance = - camera.position.z / vec.z;

pos.copy( camera.position ).add( vec.multiplyScalar( distance ) );

la variable pos est la position du point dans L'espace 3D," sous la souris", et dans le plan z=0 .

trois.JSR.95

104
répondu WestLangley 2018-08-22 12:28:59

in R. 58 ce code fonctionne pour moi:

var planeZ = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
var mv = new THREE.Vector3(
    (event.clientX / window.innerWidth) * 2 - 1,
    -(event.clientY / window.innerHeight) * 2 + 1,
    0.5 );
var raycaster = projector.pickingRay(mv, camera);
var pos = raycaster.ray.intersectPlane(planeZ);
console.log("x: " + pos.x + ", y: " + pos.y);
5
répondu lazymf 2013-07-02 11:07:38

pour obtenir les coordonnées d'un objet 3d avec la souris, utilisez projectVector:

var width = 640, height = 480;
var widthHalf = width / 2, heightHalf = height / 2;

var projector = new THREE.Projector();
var vector = projector.projectVector( object.matrixWorld.getPosition().clone(), camera );

vector.x = ( vector.x * widthHalf ) + widthHalf;
vector.y = - ( vector.y * heightHalf ) + heightHalf;

pour les trois.js 3D coordonnées qui se rapportent à des coordonnées spécifiques de la souris, utiliser l'opposé, unprojectVector:

var elem = renderer.domElement, 
    boundingRect = elem.getBoundingClientRect(),
    x = (event.clientX - boundingRect.left) * (elem.width / boundingRect.width),
    y = (event.clientY - boundingRect.top) * (elem.height / boundingRect.height);

var vector = new THREE.Vector3( 
    ( x / WIDTH ) * 2 - 1, 
    - ( y / HEIGHT ) * 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 );

il y a un grand exemple ici . Cependant, pour utiliser project vector, il doit y avoir un objet sur lequel l'Utilisateur a cliqué. coupe sera un tableau de tous les objets à l'emplacement de la souris, indépendamment de leur profondeur.

2
répondu BishopZ 2012-10-24 18:35:16

cela a fonctionné pour moi lorsque j'ai utilisé un orthographic camera

let vector = new THREE.Vector3();
vector.set(
    (event.clientX / window.innerWidth) * 2 - 1,
    - (event.clientY / window.innerHeight) * 2 + 1,
    0
);
vector.unproject(camera);

WebGL trois.JSR.89

2
répondu KTCO 2018-01-02 22:12:23

ThreeJS s'éloigne lentement du projecteur.(Un)ProjectVector et la solution avec projecteur.pickingRay () ne fonctionne plus, je viens de terminer la mise à jour de mon propre code.. ainsi, la version de travail la plus récente devrait être comme suit:

var rayVector = new THREE.Vector3(0, 0, 0.5);
var camera = new THREE.PerspectiveCamera(fov,this.offsetWidth/this.offsetHeight,0.1,farFrustum);
var raycaster = new THREE.Raycaster();
var scene = new THREE.Scene();

//...

function intersectObjects(x, y, planeOnly) {
  rayVector.set(((x/this.offsetWidth)*2-1), (1-(y/this.offsetHeight)*2), 1).unproject(camera);
  raycaster.set(camera.position, rayVector.sub(camera.position ).normalize());
  var intersects = raycaster.intersectObjects(scene.children);
  return intersects;
}
1
répondu Pete Kozak 2014-10-29 15:23:44

ci-dessous est une classe ES6 que j'ai écrit basé sur la réponse de WestLangley, qui fonctionne parfaitement pour moi en trois.js r77.

notez qu'il suppose que votre viewport de rendu reprend l'intégralité de votre viewport de navigateur.

class CProjectMousePosToXYPlaneHelper
{
    constructor()
    {
        this.m_vPos = new THREE.Vector3();
        this.m_vDir = new THREE.Vector3();
    }

    Compute( nMouseX, nMouseY, Camera, vOutPos )
    {
        let vPos = this.m_vPos;
        let vDir = this.m_vDir;

        vPos.set(
            -1.0 + 2.0 * nMouseX / window.innerWidth,
            -1.0 + 2.0 * nMouseY / window.innerHeight,
            0.5
        ).unproject( Camera );

        // Calculate a unit vector from the camera to the projected position
        vDir.copy( vPos ).sub( Camera.position ).normalize();

        // Project onto z=0
        let flDistance = -Camera.position.z / vDir.z;
        vOutPos.copy( Camera.position ).add( vDir.multiplyScalar( flDistance ) );
    }
}

vous pouvez utiliser la classe comme suit:

// Instantiate the helper and output pos once.
let Helper = new CProjectMousePosToXYPlaneHelper();
let vProjectedMousePos = new THREE.Vector3();

...

// In your event handler/tick function, do the projection.
Helper.Compute( e.clientX, e.clientY, Camera, vProjectedMousePos );

vProjectedMousePos contient maintenant la position projetée de la souris sur le plan z=0.

1
répondu drone1 2016-06-05 19:35:18

Voici mon avis sur la création d'une classe es6. Travailler avec Trois.js r83. La méthode d'utilisation de rayCaster vient de mrdoob ici: trois.js Projecteur et Ray objets

    export default class RaycasterHelper
    {
      constructor (camera, scene) {
        this.camera = camera
        this.scene = scene
        this.rayCaster = new THREE.Raycaster()
        this.tapPos3D = new THREE.Vector3()
        this.getIntersectsFromTap = this.getIntersectsFromTap.bind(this)
      }
      // objects arg below needs to be an array of Three objects in the scene 
      getIntersectsFromTap (tapX, tapY, objects) {
        this.tapPos3D.set((tapX / window.innerWidth) * 2 - 1, -(tapY / 
        window.innerHeight) * 2 + 1, 0.5) // z = 0.5 important!
        this.tapPos3D.unproject(this.camera)
        this.rayCaster.set(this.camera.position, 
        this.tapPos3D.sub(this.camera.position).normalize())
        return this.rayCaster.intersectObjects(objects, false)
      }
    }

vous l'utiliseriez comme ceci si vous vouliez vérifier contre tous vos objets dans la scène pour des coups. J'ai fait le drapeau récursif faux ci-dessus parce que pour mes usages Je n'en avais pas besoin.

var helper = new RaycasterHelper(camera, scene)
var intersects = helper.getIntersectsFromTap(tapX, tapY, 
this.scene.children)
...
0
répondu Matt Thompson 2017-05-23 11:54:38

bien que les réponses fournies puissent être utiles dans certains scénarios, je peux à peine imaginer ces scénarios (peut-être des jeux ou des animations) parce qu'ils ne sont pas précis du tout (deviner autour de la NDC z De target?). Vous ne pouvez pas utiliser ces méthodes pour ne pas projeter les coordonnées de l'écran sur celles du monde si vous connaissez la cible z-plane. Mais pour la plupart des scénarios, vous devriez connaître cet avion.

par exemple, si vous dessinez sphère par Centre (Point connu dans l'Espace Modèle) et Rayon - vous devez obtenir le rayon Comme delta des coordonnées de la souris non injectées - mais vous ne pouvez pas! Avec tout le respect dû à la méthode de @ WestLangley avec targetZ ne fonctionne pas, il donne des résultats incorrects (je peux fournir jsfiddle si nécessaire). Un autre exemple - vous avez besoin de mettre cible orbit controls par double clic de souris, mais sans "vrai" raycasting avec des objets de scène (quand vous n'avez rien à choisir).

la solution pour moi est juste de créer le plan virtuel dans le point cible le long de l'axe z et utiliser raycasting avec ce plan par la suite. Le point cible peut être l'orbite actuelle contrôle cible ou le sommet de l'objet que vous avez besoin de dessiner étape par étape dans l'espace modèle existant, etc. Cela fonctionne parfaitement et c'est simple (exemple à la machine à écrire):

screenToWorld(v2D: THREE.Vector2, camera: THREE.PerspectiveCamera = null, target: THREE.Vector3 = null): THREE.Vector3 {
    const self = this;

    const vNdc = self.toNdc(v2D);
    return self.ndcToWorld(vNdc, camera, target);
}

//get normalized device cartesian coordinates (NDC) with center (0, 0) and ranging from (-1, -1) to (1, 1)
toNdc(v: THREE.Vector2): THREE.Vector2 {
    const self = this;

    const canvasEl = self.renderers.WebGL.domElement;

    const bounds = canvasEl.getBoundingClientRect();        

    let x = v.x - bounds.left;      

    let y = v.y - bounds.top;       

    x = (x / bounds.width) * 2 - 1;     

    y = - (y / bounds.height) * 2 + 1;      

    return new THREE.Vector2(x, y);     
}

ndcToWorld(vNdc: THREE.Vector2, camera: THREE.PerspectiveCamera = null, target: THREE.Vector3 = null): THREE.Vector3 {
    const self = this;      

    if (!camera) {
        camera = self.camera;
    }

    if (!target) {
        target = self.getTarget();
    }

    const position = camera.position.clone();

    const origin = self.scene.position.clone();

    const v3D = target.clone();

    self.raycaster.setFromCamera(vNdc, camera);

    const normal = new THREE.Vector3(0, 0, 1);

    const distance = normal.dot(origin.sub(v3D));       

    const plane = new THREE.Plane(normal, distance);

    self.raycaster.ray.intersectPlane(plane, v3D);

    return v3D; 
}
0
répondu SalientBrain 2018-04-16 08:49:41