Подтвердить что ты не робот

Мышь/Холст X, Y - Three.js World X, Y, Z

Я искал пример для примера, который соответствует моему варианту использования, но не может найти его. Я пытаюсь преобразовать координаты экранной мыши в 3D-координаты мира с учетом камеры.

Решения Я нашел все пересечения лучей для достижения выбора объекта.

То, что я пытаюсь сделать, - позиционировать центр объекта Three.js в координатах, которые мышь в настоящее время "перегружена".

Моя камера находится в x: 0, y: 0, z: 500 (хотя она будет перемещаться во время моделирования), и все мои объекты находятся в z = 0 с переменными значениями x и y, поэтому мне нужно знать мир X, Y на основе предположения, что az = 0 для объекта, который будет следовать за положением мыши.

Этот вопрос похож на аналогичную проблему, но не имеет решения: Получение координат мыши относительно 3D-пространства в THREE.js

Учитывая положение мыши на экране с диапазоном "top-left = 0, 0 | bottom-right = window.innerWidth, window.innerHeight", может ли кто-нибудь предоставить решение для перемещения объекта Three.js на мышь координаты вдоль z = 0?

4b9b3361

Ответ 1

Вам не нужно иметь какие-либо объекты в вашей сцене, чтобы сделать это.

Вы уже знаете положение камеры.

Используя vector.unproject( camera ) вы можете получить луч, указывающий в vector.unproject( camera ) вам направлении.

Вам просто нужно вытянуть этот луч из положения камеры, пока координата z кончика луча не станет равной нулю.

Вы можете сделать это так:

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 ) );

Переменная pos - это положение точки в трехмерном пространстве "под мышкой" и в плоскости z=0.


РЕДАКТИРОВАТЬ: Если вам нужна точка "под мышкой" и в плоскости z = targetZ, замените вычисление расстояния на:

var distance = ( targetZ - camera.position.z ) / vec.z;

three.js r.98

Ответ 2

В r.58 этот код работает для меня:

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);

Ответ 3

чтобы получить координаты мыши для использования объекта 3D 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;

чтобы получить 3D-координаты three.js, относящиеся к определенным координатам мыши, используйте противоположный 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 );

Здесь есть отличный пример здесь. Однако для использования вектора проекта должен быть объект, на который пользователь щелкнул. intersects будет массивом всех объектов в местоположении мыши, независимо от их глубины.

Ответ 4

Это работало для меня при использовании 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 three.js r.89

Ответ 5

ТриJS медленно отбрасывает проектор. (Un) ProjectVector и решение с projector.pickingRay() больше не работает, просто закончил обновление моего собственного кода.. поэтому самая последняя рабочая версия должна быть следующей:

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;
}

Ответ 6

Ниже приведен класс ES6, который я написал на основе ответа WestLangley, который отлично работает для меня в THREE.js r77.

Обратите внимание, что предполагается, что ваш видовой экран рендеринга занимает весь ваш браузер.

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

Вы можете использовать класс следующим образом:

// 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 теперь содержит проецируемую позицию мыши на плоскости z = 0.

Ответ 7

Вот мой пример создания класса es6. Работа с Three.js r83. Метод использования rayCaster представлен здесь mrdoob: Объекты проектора и Ray.Js

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

Вы использовали бы это так, если бы вы хотели проверить все ваши объекты в сцене для хитов. Я сделал рекурсивный флаг ложным выше, потому что для моих целей мне это не нужно.

var helper = new RaycasterHelper(camera, scene)
var intersects = helper.getIntersectsFromTap(tapX, tapY, 
this.scene.children)
...

Ответ 8

Хотя предоставленные ответы могут быть полезны в некоторых сценариях, я вряд ли могу представить эти сценарии (возможно, игры или анимации), потому что они не совсем точны (гадание вокруг целевого NDC z?). Вы не можете использовать эти методы для непроектирования экранных координат для мировых, если вы знаете целевую плоскость z. Но для большинства сценариев вы должны знать этот самолет.

Например, если вы рисуете сферу по центру (известная точка в пространстве модели) и радиус - вам нужно получить радиус в виде дельта непроектированных координат мыши, но вы не можете! При всем уважении метод @WestLangley с targetZ не работает, он дает неверные результаты (при необходимости я могу предоставить jsfiddle). Другой пример - вам нужно установить цель управления орбитой двойным щелчком мыши, но без "реальной" raycasting с объектами сцены (когда вам нечего выбирать).

Решение для меня состоит в том, чтобы просто создать виртуальную плоскость в целевой точке вдоль оси z и впоследствии использовать raycasting с этой плоскостью. Целевой точкой может быть текущий элемент управления орбиты цели или вершины объекта, который нужно поэтапно рисовать в существующем пространстве модели и т.д. Это работает отлично и просто (пример в машинописном виде):

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; 
}