Приведите точку к ближайшему положению камеры поворотом камеры

#three.js

#three.js

Вопрос:

У меня есть сцена в Three.js (r67) с камерой, которая управляется OrbitControls .

Если я теперь выберу произвольную точку ( Vector3 ) в сцене, каков наилучший способ переместить эту точку (программно) в ближайшее положение камеры, просто повернув камеру?


Пример сценария

На рисунке ниже левая сторона является отправной точкой. Камера вращается вокруг зеленой сферы (например, OrbitControls), где центр камеры является центром сферы. Теперь мне нравится автоматически поворачивать камеру вокруг сферы (делая минимальное количество ходов), чтобы красная рамка была ближайшей к камере (например, с правой стороны). введите описание изображения здесь

Ответ №1:

Независимо от метода выбора точки в сцене, есть несколько вариантов понимания того, что вы подразумеваете под «приведением камеры просто вращением».

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

Это просто:

 camera.lookAt(your_point_in_scene);
  

Вы могли бы сделать это более сложным. Во-первых, найдите текущий вектор наведения. По умолчанию камера смотрит в направлении Vector(0,0,1) . Когда мы поворачиваем ее так же, как и камеру, у нас будет направление камеры:

 var vec = new THREE.Vector3(0,0,1);
vec.applyQuaternion(camera.rotation._quaternion);
  

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

 var object_dir = object_world_point.clone().sub(camera.position);
var axis = vec.clone().crossProduct(object_dir);
var angle = Math.acos( vec.clone().dot(object_dir) / vec.length() / object_dir.length());
  

Имея угол и ось, мы могли бы повернуть камеру:

 camera.rotateOnAxis(axis, angle);
  

Или, если вы хотите сделать его плавным:

 // before animation started
var total_rotation = 0, 
    rotateon,
    avel = 0.01; // 0.01 radian per second    


if(total_rotation < angle){
  rotateon = avel * time_delta;
  camera.rotateOnAxis(axis, angle);
  total_rotation  = rotateon;
}
  

Ответ №2:

Ну, это не сложно, если у вас есть центральная / целевая точка для камеры. Вы вычисляете разницу от целевого положения до положения точки и нормализуете этот вектор на длину камеры-расстояние до центральной точки (т. Е. Что-то вроде pointdistance.multiplyScalar(cameradistance.длина () / расстояние до точки.длина ()) ).

И это все. Если я правильно понял ваш вопрос. Все, что вам нужно сделать, это «расширить» положение точки на ваш «купол движения камеры», и тогда у вас будет идеальное новое положение камеры. Поворот камеры выполняется автоматически, поскольку вы всегда нацеливаетесь на центральную точку.

И если вы хотите немного сгладить движение камеры, вы можете просто интерполировать угол (а не позиции напрямую), например, с помощью экспоненциальной функции, как вам больше нравится.

Ответ №3:

Привет, дорогой, пожалуйста, следуйте этому независимо от метода выбора точки в сцене, есть несколько пониманий того, что вы подразумеваете под «приведением камеры просто вращением». Я полагаю, вы хотите повернуть камеру таким образом, чтобы выбранная точка находилась в центре экрана. Это просто:

 camera.lookAt(your_point_in_scene);
  

Вы могли бы сделать это более сложным. Во-первых, найдите текущий вектор наведения. По умолчанию камера смотрит в направлении вектора (0,0,1). Когда мы поворачиваем ее так же, как и камеру, у нас будет направление камеры:

 var vec = new THREE.Vector3(0,0,1);
vec.applyQuaternion(camera.rotation._quaternion);
  

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

 var object_dir = object_world_point.clone().sub(camera.position);
var axis = vec.clone().crossProduct(object_dir);
var angle = Math.acos( vec.clone().dot(object_dir) / vec.length() / object_dir.length());