Трехмерная орфографическая камера THREE.js для мыши

Я работаю над орфографической камерой для нашего приложения THREE.js. По сути, эта камера представит сцену пользователю в 2D (пользователи могут переключаться между 2D и 3D-камерой). Эта камера позволит панорамировать и масштабировать точку мыши. У меня работает панорамирование, и у меня есть увеличение рабочего времени, но не масштабирование до точки мыши. Здесь мой код:

import React from 'react';
import T from 'three';

let panDamper = 0.15;

let OrthoCamera = React.createClass({
  getInitialState: function () {
    return {
      distance: 150,
      position: { x: 8 * 12, y: 2 * 12, z: 20 * 12 },
    };
  },
  getThreeCameraObject: function () {
    return this.camera;
  },
  applyPan: function (x, y) { // Apply pan by changing the position of the camera
    let newPosition = {
      x: this.state.position.x + x * -1 * panDamper,
      y: this.state.position.y + y * panDamper,
      z: this.state.position.z
    };

    this.setState({position: newPosition});
  },
  applyDirectedZoom: function(x, y, z) {
    let zoomChange = 10;
    if(z < 0) zoomChange *= -1;
    let newDistance = this.state.distance + zoomChange;

    let mouse3D = {
      x: ( x / window.innerWidth ) * 2 - 1,
      y: -( y / window.innerHeight ) * 2 + 1
    };

    let newPositionVector = new T.Vector3(mouse3D.x, mouse3D.y, 0.5);
    newPositionVector.unproject(this.camera);
    newPositionVector.sub(this.camera.position);

    let newPosition = {
      x: newPositionVector.x,
      y: newPositionVector.y,
      z: this.state.position.z
    };

    this.setState({
      distance: newDistance,
      position: newPosition
    });
  },
  render: function () {
    let position = new T.Vector3(this.state.position.x, this.state.position.y, this.state.position.z);

    let left = (this.state.distance / -2) * this.props.aspect + this.state.position.x;
    let right = (this.state.distance / 2) * this.props.aspect + this.state.position.x;
    let top = (this.state.distance / 2) + this.state.position.y;
    let bottom = (this.state.distance / -2) + this.state.position.y;

    // Using react-three-renderer
    // https://github.com/toxicFork/react-three-renderer
    return <orthographicCamera
      {...(_.pick(this.props, ['near', 'far', 'name']))}
      position={position}
      left={left}
      right={right}
      top={top}
      bottom={bottom}
      ref={(camera) => this.camera = camera}/>
  }
});

module.exports = OrthoCamera;

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

Я взял реплики из этого вопроса. Насколько я могу судить, я успешно конвертируюсь в координаты THREE.js в mouse3D (см. Ответ на этот вопрос).

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

Ответы

Ответ 1

Предполагая, что у вас есть камера, которая описывается позицией и точкой обзора (или поворот) в мировых координатах, масштабирование (или вне), конкретная точка довольно проста в своем ядре.

Ваше представление кажется еще проще: просто пара позиции/расстояния. Я не видел компонент вращения, поэтому я предполагаю, что ваша камера предназначена для орфографии сверху вниз.

В этом случае ваша точка взгляда (которая вам не понадобится) просто (position.x, position.y - distance, position.z).

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

Если вы думаете об этом, это именно то, что происходит, когда вы масштабируетесь в точке в 3D. Вы продолжаете смотреть на одно и то же направление, но вы все больше и больше приближаетесь к своей цели.

Если вы хотите увеличить масштаб в 1,1 раза, вы можете представить, как масштабировать вектор, соединяющий ваше положение камеры с точкой масштабирования, на 1/1.1.

Вы можете сделать это путем простого интерполяции:

var newPosition = new THREE.Vector3();
newPosition.x = (orgPosition.x - zoomAt.x) / zoomFactor + zoomAt.x;
newPosition.y = (orgPosition.y - zoomAt.y) / zoomFactor + zoomAt.y;
newPosition.z = (orgPosition.z - zoomAt.z) / zoomFactor + zoomAt.z;

Как я уже говорил выше, в вашем случае вам не нужно будет обновлять точку поиска, а затем вычислять новое расстояние. Ваше новое расстояние будет просто:

var newDistance = newPosition.y

Это должно сделать это.

Он становится немного более сложным (главным образом в общем случае), если вы хотите установить минимальные и максимальные пределы расстояния как между парами позиции/взгляда, так и положения/увеличения точки.