THREE.js正交相机缩放到鼠标点

14
我正在为我们的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效果)。
我从this question中得到了线索。据我所知,我成功地将鼠标转换为THREE.js坐标mouse3D(请参阅this question的答案)。
那么,考虑到这个设置,如何使用正交相机平滑地缩放到鼠标点(mouse3D),并保持二维视图?提前感谢。

7
你有一把小提琴来尝试编写代码吗? - gaitat
那么问题是在缩放过程中相机旋转的问题吗? - Ramil Kudashev
你找到解决方案了吗,Scott H? - Aasha joney
这是一个用于将正交相机缩放到指针位置的TypeScript函数。 https://stackoverflow.com/questions/52824008/three-js-orthographiccamera-zoom-to-cursor#76450096 - andymel
1个回答

6
假设你有一台相机,其位置和观察点(或枢轴)在世界坐标系中描述,以特定点为中心进行缩放(或拉远)在其核心上相当简单。
你的表示似乎更简单:只是一个位置/距离对。我没有看到旋转组件,所以我假设你的相机意味着是一个自上而下的正交相机。
在这种情况下,你不需要的观察点就是(position.x, position.y - distance, position.z)。
在一般情况下,所有你需要做的就是同时移动相机位置和观察点,并保持相机法线(即方向)朝向缩放点。请注意,这将适用于所有投影类型或相机旋转。编辑(2020/05/01):使用正交投影时,这不是你需要完成的全部内容(请查看底部更新)。
如果你仔细想想,在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

搞定了。

只有在您想要在位置/看向点对和位置/缩放点对之间设置最小和最大距离限制时,它才会变得稍微复杂一些(主要是在一般情况下)。

更新(2020/05/01):

我刚意识到上面的内容虽然正确(除了遗漏一个次要但非常重要的步骤),但并不是对提问者问题的完整回答。在正交模式下更改相机位置当然不会改变正在呈现的图形的比例。为此,必须更新相机的投影矩阵(即正交投影的左、右、上和下参数必须更改)。

因此,许多图形库在其正交相机类中包括缩放因子,可以实现这一点。我没有使用ThreeJS的经验,但我认为该属性称为“zoom”。

综上所述:

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;
myCamera.zoom = myCamera.zoom * zoomFactor
myCamera.updateProjectionMatrix()

如果您想使用上述的正交相机类代码,则可能需要更改计算左、右、上和下部分,并在计算中添加缩放因子。以下是一个示例:

var aspect = this.viewportWidth / this.viewportHeight
var dX     = (this.right - this.left)
var dY     = (this.top   - this.bottom) / aspect
var left   = -dX / (2 * this.scale)
var right  =  dX / (2 * this.scale)
var bottom = -dY / (2 * this.scale)
var top    =  dY / (2 * this.scale)
mat4.ortho(this.mProjection, left, right, bottom, top, this.near, this.far)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接