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

Posted

技术标签:

【中文标题】threejs - 如何将对象相对于鼠标旋转到从其当前中心点设置的度数【英文标题】:threejs - how to rotate an object in relation to mouse to a degree setting from its current center point 【发布时间】:2020-08-27 19:11:48 【问题描述】:

我在一个场景中有一个 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

【问题讨论】:

【参考方案1】:

围绕某个任意旋转中心旋转场景层次结构中任何位置的任何对象的最简单通用方法是在您希望该旋转中心所在的位置创建Object3D。让我们称之为“枢轴点”。然后把你想要旋转的东西,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 还通过添加上述帮助器来涵盖移动旋转点。

【讨论】:

以上是关于threejs - 如何将对象相对于鼠标旋转到从其当前中心点设置的度数的主要内容,如果未能解决你的问题,请参考以下文章

如何保持鼠标相对于屏幕的位置并忽略组件旋转?

three.js如何让场景中模型跟随鼠标旋转呀

鼠标点击时对象的动画(旋转、移动)

Three.js如何让对象相对于另一个旋转?

threejs设置物体位置

子对象相对于父对象的旋转