threejs - 如何将对象相对于鼠标旋转到从其当前中心点开始的度数设置

threejs - how to rotate an object in relation to mouse to a degree setting from its current center point

我在场景中有一个 threejs 模型,其 0,0,0 点位于其末尾。我不得不旋转它,使其静止位置处于我想要的角度,并将其动画化到场景中,因此位置和旋转都不在 0,0,0。此外,相机已经定位并设置为观察物体,因此它的位置和旋转已经改变。

我想获取用户的鼠标坐标,让模型围绕其在世界 Y 轴上的中心点旋转一定数量的最大和最小度数。我知道如何将鼠标坐标映射到我想要的度数/弧度角度,但是我如何告诉模型旋转?

我试过 model.rotation.y = [radians],它旋转,我假设,基于它当前拥有的任何旋转矩阵(或者至少,不是我期望的方式)。如果我这样做 rotateOnWorldAxis 它从它被导入的 0,0,0 点开始旋转,这不是我想要的位置,而且不是绝对度数(即它旋转 30 度从当前每次鼠标移动的位置,而不是世界原点的 30 度。

如何让它在世界Y轴上以中心点为中心旋转一定角度?

模型位置在{_x: 0, _y: -0.3, _z: -2},它的旋转在{x: -2, y: 2.4, z: 0} 相机位置 {x: 0, y: 0, z: 5} 和旋转 {_x: 0.3926990816987242, _y: 0, _z: -0}.

当前代码是:

const target = this.mouseTarget;
const mouseX = clientX - this.halfWidth; //halfWidth is half the screen width, client X is the mouse e.clientX
const mouseY = clientY - this.halfHeight;
target.x += (mouseX - target.x) * 0.02;
target.y += (-mouseY - target.y) * 0.02;
target.z = this.camera.position.z; // assuming the camera is located at ( 0, 0, z );
const maxRotDeg = 30;
const maxRot = this.degToRad(maxRotDeg);
const rotVal = this.mapValue(
  target.x,
  -this.halfWidth,
  this.halfWidth,
  -maxRot,
  maxRot,
); //works to give me the radians that I want for rotation
//this.modelGroup.rotateOnWorldAxis(this.rotationAxisY, rotVal); //doesn't work
//this.modelGroup.lookAt(target); //doesn't work

最简单的 generic 围绕某个任意旋转中心在场景层次结构中的任何位置旋转任何对象的方法是在您希望该旋转中心的位置创建一个 Object3D .我们称它为 "pivotPoint"。然后拿你要旋转的东西,attach它到pivotPoint。旋转枢轴点,然后将对象放回原处(将其重新附加到之前的父对象)。也就是说

const pivotPoint = new THREE.Object3D();
pivotPoint.position.set(...);  // optional
pivotPoint.rotation.set(...);  // optional
someParent.add(pivotPoint)     

然后围绕该点旋转其他东西

const parent = thingToRotate.parent;
pivotPoint.attach(thingToRotate);
pivotPoint.rotation.set( ... );    // do the rotation
parent.attach(thingToRotate);

这是最通用的方式,无论 thingToRotate 在场景层次结构中有多深,都可以在任何方向上围绕任何点旋转。

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 50;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.set(0, 3, 3);
  camera.lookAt(0, 1, 0);

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('white');

  [[-1, 2, 4], [1, 2, -3]].forEach(pos => {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(...pos);
    scene.add(light);
  });

  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

  function makeInstance(parent, pos, rot, scale) {
    const material = new THREE.MeshPhongMaterial({color: Math.random() * 0xFFFFFF | 0});

    const mesh = new THREE.Mesh(geometry, material);
    parent.add(mesh);
    mesh.position.set(...pos);
    mesh.rotation.set(...rot);
    return mesh;
  }
  
  const m1 = makeInstance(scene, [0, 0, 0], [0, 0.7, 0]);
  const m2 = makeInstance(m1, [1, 1, 0], [0.3, 0.5, 0]); 
  const m3 = makeInstance(m1, [-1, 1, 0], [-0.9, 0.5, 0]); 
  const m4 = makeInstance(m2, [1, 1, 0], [-0.4, 1.3, 0.8]); 
  
  let thingToRotate = m3;

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }
  
  const pivotPoint = new THREE.Object3D();
  scene.add(pivotPoint);

  let then = 0;
  function render(now) {
    now *= 0.001;
    delta = now - then;
    then = now;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }
    
    {
       // put pivotPoint at origin of thingToRotate in world space
       // note: an object's origin might not be its center. If oyu
       // want its center you need to compute its center
       thingToRotate.getWorldPosition(pivotPoint.position);
       // reset rotation for pivotPoint
       pivotPoint.rotation.set(0, 0, 0);
       
       // rotate thingToRotate around pivotPoint's yAxis
       const parent = thingToRotate.parent;
       pivotPoint.attach(thingToRotate);
       pivotPoint.rotation.y = delta;
       parent.attach(thingToRotate);
    }
    
    m1.rotation.y -= delta * 0.1;

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  function setClick(selector, thing) {
    document.querySelector(selector).addEventListener('input', () => {
      thingToRotate = thing;
    });
  }
  setClick('#m1', m1);
  setClick('#m2', m2);
  setClick('#m3', m3);
  setClick('#m4', m4);
  
  requestAnimationFrame(render);
}

main();
body {
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
#ui {
  position: absolute;
  left: 0;
  top: 0;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.min.js"></script>
<div id="ui">
<div><input type="radio" name="thing" id="m1"><label for="m1">m1</label></div>
<div><input type="radio" name="thing" id="m2"><label for="m2">m2</label></div>
<div><input type="radio" name="thing" id="m3" checked><label for="m3">m3</label></div>
<div><input type="radio" name="thing" id="m4"><label for="m4">m4</label></div>
</div>

还有很多其他快捷方式,但每个快捷方式都需要准确了解您的场景设置方式。

例如,如果您唯一想做的事情就是围绕世界 Y 轴旋转物体,但这些物体本身有旋转,然后将它们作为其他 Object3D 的父级,将位置设置在 Object3D 和旋转。

示例:假设您有一些像这样定向和旋转的东西

scene.add(thing);
thing.position.set(100, 200, 300);  // some arbitrary position
thing.rotation.set(1, 2, 3);        // some arbitrary rotation

改成这样

const helper = new THREE.Object3D();
scene.add(helper);
helper.position.set(100, 200, 300);  // some arbitrary position on helper
thing.rotation.set(1, 2, 3);         // some arbitrary rotation on thing

现在你可以旋转 helper.rotation.y 并且物体将围绕其原点在世界 Y 轴上旋转。

如果你想围绕某个对象的中心旋转,那么你需要计算它的中心(中心!=原点,尽管它们通常是相同的)并添加其他助手。请参阅底部的 this article,它使 3d 文本围绕其中心旋转,因为其原点位于文本的左侧。

This article 还包括通过像上面那样添加助手来移动旋转点。