Three.js如何围绕物体自身中心旋转,而不是世界中心

70

在此输入图片描述

场景中有两个物体。

立方体的旋转轴应该是立方体的中心,这是我的期望。

但鞋子模型的旋转轴是世界y轴。

我的原始代码是:

cube.rotation.y += 0.01;
shoe.rotation.y += 0.01;

我在 stackoverflow 找到了一个解决方案,像这样:

cube.rotation.y += 0.01;
var pivot = new THREE.Object3D();
pivot.add(shoe);
pivot.rotation.y += 0.01;

但是它不起作用。然后,我改变了鞋子的位置。

cube.rotation.y += 0.01;
var pivot = new THREE.Object3D();
shoe.position.set(-5,0,0);
pivot.add(shoe);
pivot.rotation.y += 0.01;

现在的结果已经变得更好了,但还不完美。由于有很多鞋款,我无法为每个鞋款确定不同的位置。


你能在不将鞋物体移动到原点的情况下使其正常工作吗?谢谢。 - Snippy Valson
5个回答

69
如果您的网格不围绕其中心旋转,那是因为几何顶点与原点偏离了。您可以通过使用边界框来自动重新定位,定义一个合理的中心,然后像这样偏移网格的位置:
var box = new THREE.Box3().setFromObject( mesh );
box.getCenter( mesh.position ); // this re-sets the mesh position
mesh.position.multiplyScalar( - 1 );

然后将网格添加到一个枢轴对象中:
var pivot = new THREE.Group();
scene.add( pivot );
pivot.add( mesh );

在你的动画循环中,旋转中心点;
pivot.rotation.y += 0.01;

编辑:另一种解决方案是将几何顶点进行翻译,使得几何体围绕或靠近原点居中。
geometry.translate( distX, distY, distZ );

或者,你也可以直接打电话:
geometry.center();

根据几何体的边界框,自动将几何体的顶点居中。

three.js r153


2
请问一个问题,我们如何在加载自定义glTF模型后获取其“geometry”?一旦加载了模型,我们只需通过glTF.scene访问它并将其添加到我们的场景中。不确定从哪里获取模型的几何形状以使其居中:(。我相信我们可以遍历对象并在每个节点上获取几何形状,但我相信应该有更快速的解决方案。 - semuzaboi
@semuzaboi 使用第一种方法,但是不要使用“mesh”,而是使用“glTF.scene”。 - WestLangley
1
@WestLangley 在 box.center(mesh.position) 中,位置已经设置为边界框的中心。将正数乘以-1会产生什么影响? - Ajay
1
@Ajay 如果边界框的中心点为 (100, 0, 0),那么我们需要通过将网格位置设置为 (-100, 0, 0) 来进行补偿。 - WestLangley
@WestLangley 我们不是想把几何图形居中,使得新的中心点为 (0,0,0) 吗?为了实现这一点,我们应该使用 mesh.position.add(new Vector3(-100,0,0)) 进行平移。 - Ajay
1
能否在不将对象移动到原点的情况下实现这一点? - Snippy Valson

29

使用 THREE.Geometry.prototype.center 方法,如下所示:

myGeometry.center();

这就像使用 myGeometery.translate(x,y,z) 一样,但自动将中心点设为 (x,y,z)。


1
这个答案非常简洁明了。以下是 THREE.js 源代码的链接。https://github.com/mrdoob/three.js/blob/51b2c730f13f2b234fc9cc2e328c065ad8d6bed4/src/core/Geometry.js#L325 - Ajay

12

在我的情况下,轴心解决方案不起作用。

对于使用OBJLoader.js(用于加载.obj对象),您需要获取对象的boundingBox,获取其中心,将标量乘以-1,并使用此来翻译每个子几何体的几何体。然后,如果要将其用于某个目的,则需要重新更新boundingBox。

以下是一个函数,它加载obj并“规范化”其几何体顶点,给出OBJ和MTL(纹理)文件的目录和名称(例如,如果OBJ和MTL文件是dir1/myObject.obj和dir1/myObject.mtl,则调用loadObj('dir1','myObject'))。

function loadObj(dir, objName) {
  var onProgress = function (xhr) {
    if (xhr.lengthComputable) {
      var percentComplete = (xhr.loaded / xhr.total) * 100;
      console.log(Math.round(percentComplete, 2) + "% downloaded");
    }
  };

  var onError = function (xhr) {};

  // Manager
  var manager = new THREE.LoadingManager();
  manager.onProgress = function (item, loaded, total) {
    console.log(
      "Started loading file: " +
        item +
        ".\nLoaded " +
        loaded +
        " of " +
        total +
        " files."
    );
  };

  var mtlLoader = new THREE.MTLLoader();
  mtlLoader.setPath(dir);
  mtlLoader.load(objName + ".mtl", function (materials) {
    materials.preload();

    // Model
    var loader = new THREE.OBJLoader(manager);
    loader.setMaterials(materials);
    loader.setPath(dir);
    loader.load(
      objName + ".obj",
      function (object) {
        var objBbox = new THREE.Box3().setFromObject(object);

        // Geometry vertices centering to world axis
        var bboxCenter = objBbox.getCenter().clone();
        bboxCenter.multiplyScalar(-1);

        object.traverse(function (child) {
          if (child instanceof THREE.Mesh) {
            child.geometry.translate(bboxCenter.x, bboxCenter.y, bboxCenter.z);
          }
        });

        objBbox.setFromObject(object); // Update the bounding box

        scene.add(object);
      },
      onProgress,
      onError
    );
  });
}


3
如果有多个网格(这是最常见的情况),则设置场景图中父节点的位置以实现将模型居中对齐。如果场景图像这样:gltfObject -> childObject ->.. -> 网格数组。
const bbox = new THREE.Box3().setFromObject( gltfObject );
const offset = new THREE.Vector3();
bbox.getCenter(offset).negate();
childObject.position.set(offset.x, offset.y, offset.z);

2
这是我如何在r86上使事情正常运转的方法。
// Store original position
let box = new THREE.Box3().setFromObject(this.mesh);
let offset = box.getCenter();

// Center geometry faces
this.geometry.center();

// Add to pivot group
this.group = new THREE.Object3D();
this.group.add(this.mesh);

// Offset pivot group by original position
this.group.position.set(offset.x, offset.y, offset.z);

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