繁体   English   中英

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

[英]THREE.js orthographic camera zoom to mouse point

我正在为我们的 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 ) 并保持二维视图? 提前致谢。

假设您有一个由世界坐标中的位置和观察(或枢轴)点描述的相机,缩放(或远离)特定点的核心非常简单。

您的表示似乎更简单:只是一个位置/距离对。 我没有看到旋转组件,所以我假设你的相机是一个自上而下的正交相机。

在这种情况下,您的观察点(您不需要)只是(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):

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

出于这个原因,许多图形库在它们的正交相机类中包含一个缩放因子,这正是这样做的。 我没有使用 ThreeJS 的经验,但我认为该属性称为“缩放”。

所以,总结一下:

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM