Изменение объекта вслед за движением мыши
Решение этого вопроса заставило меня вспомнить тригонометрию и векторную алгебру.
Постановка задачи: при создании изменять объект вслед за движением мыши.
Что мы имеем: координаты курсора в экранных координатах, угол обзора камеры, соотношение сторон.
Нужно определить: насколько отличается масштаб вида в мировых координатах в данный момент времени от масштаба экрана, найти расстояние от положения объекта в экранных координатах и умножить его на масштаб вида в текущий момент времени.
Чтоб смотреть на камеру со стороны (сверху), я создал третий рендер и поместил его во второй канвас. При нажатии на канвас он увеличивается на весь экран. В последствии сделаю там ортогональную камеру.
Первое, что мы сделаем – определим положение объекта в экранных координатах. Для этого спроецируем вектор положения объекта на экран. В three.js предусмотрена функция project(). Клонируем вектор положения объекта, иначе функция изменит положение объекта.
let pos = scope.currentEntity.position.clone().project( sceneCamera.camera ); // это в координатах вида.
Переводим в координаты экрана:
scope.centerOfObjectScreen.x = ( pos.x * window.innerWidth/2 ) + window.innerWidth/2; scope.centerOfObjectScreen.y = - ( pos.y * window.innerHeight/2 ) + window.innerHeight/2;
На видео первый желтый шарик показывает положение создаваемого объекта.
Теперь определим положение камеры в 3D пространстве и запишем его в новый вектор. Для этого также предусмотрена функция:
let cameraPosition = new THREE.Vector3(); cameraPosition = camera.position.clone();
Аналогично определим направление камеры, выраженный единичным вектором:
let cameraDirection = new THREE.Vector3(); camera.getWorldDirection ( cameraDirection );
Определим расстояние от камеры до объекта:
let distance = point.distanceTo( cameraPosition );
Теперь из центра из камеры получим точку, отстоящую на расстояние равное расстоянию от камеры до объекта:
cameraPosition.add(cameraDirection.multiplyScalar(distance) );
На видео это второй желтый шарик и зеленая линия, я создал их для того, чтобы понимать, правильно ли я делаю.
Проецируем точку положения объекта на линию, проходящую из центра камеры по оси камеры
let line3 = new THREE.Line3(camera.position, cameraPosition); let pointOnLine = new THREE.Vector3(); line3.closestPointToPoint( point, true, pointOnLine);
Это вторая зеленая линия от первого желтого шарика перпендикулярно к оси камеры.
Определяем расстояние от камеры до точки пересечения.
distance = pointOnLine.distanceTo( camera.position );
Это и будет расстояние от камеры до плоскости, перпендикулярной оси камеры и проходящей через объект.
По формулам тригонометрии найдем размер экрана в этой плоскости в мировых координатах
let angle = camera.fov/2; let sizeOfViewX = distance * Math.tan( angle * Math.PI / 180 ) * 2; let sizeOfViewY = sizeOfViewX * camera.aspect * 2;
определяем коэффициент (1000000000 – для точности, это же Javascript)
let ratio = ( 1000000000 * window.innerHeight)/(1000000000 * worldSizeOfScreen.height );
Определяем расстояние от центра объекта до указателя мыши в экранных координатах
let distance = currentCoordsScreen.distanceTo( scope.centerOfObjectScreen );
и делим его на полученный коэффициент
width = distance / ratio; // получили размер куба в мировых координатах.
PS. Сначала я хотел перерисовать схемки для этого поста, но потом передумал, так живее, видно мои умственные мучения ))