如何在three.js中绕轴旋转3D对象?

42

我在three.js中遇到了一个关于旋转的问题。我想在我的游戏中旋转我的3D立方体。

//init
geometry = new THREE.CubeGeometry grid, grid, grid
material = new THREE.MeshLambertMaterial {color:0xFFFFFF * Math.random(), shading:THREE.FlatShading, overdraw:true, transparent: true, opacity:0.8}
for i in [1...@shape.length]
    othergeo = new THREE.Mesh new THREE.CubeGeometry(grid, grid, grid)
    othergeo.position.x = grid * @shape[i][0]
    othergeo.position.y = grid * @shape[i][1]
    THREE.GeometryUtils.merge geometry, othergeo
@mesh = new THREE.Mesh geometry, material

//rotate
@mesh.rotation.y += y * Math.PI / 180
@mesh.rotation.x += x * Math.PI / 180
@mesh.rotation.z += z * Math.PI / 180

如果 (x, y, z) 可以是 (1, 0, 0),那么该立方体可以旋转,但问题在于它会绕自己的轴旋转,因此旋转后无法按预期旋转。

我找到了网页如何绕一个轴旋转 Three.js 中的 Vector3?,但它只能让一个 Vector3 点绕世界轴旋转?

我尝试使用 matrixRotationWorld

@mesh.matrixRotationWorld.x += x * Math.PI / 180
@mesh.matrixRotationWorld.y += y * Math.PI / 180
@mesh.matrixRotationWorld.z += z * Math.PI / 180

但它没有起作用,我不知道是我使用方法有误还是还有其他方法…

那么,如何让这个三维立方体围绕世界坐标轴旋转?


4
那个@符号是什么意思? - Daniel Node.js
4
在 CoffeeScript 中,'@' 的意思是 JavaScript 中的 'this.',它是一个简写。 - Alex Mund
9个回答

71

自发布r59版本以来,three.js提供这三个函数用于围绕对象轴旋转对象。

object.rotateX(angle);
object.rotateY(angle);
object.rotateZ(angle);

1
谢谢。这应该是最佳答案,大多数其他答案都已弃用或效率更低。做得好! - Hugo Nava Kopp
4
不应该用于动画!引用自Three.js文档: “这通常是一次性操作,不要在循环中使用。对于典型的实时网格旋转,请使用Object3D.rotation。” - Matt C
@MatthewCliatt 你说得对。你能给出一个更好或更高效的动画解决方案吗? - Hetong
@Hetong 我自己还在寻找正确的解决方案。 - Matt C

55

这是我使用的两个函数。它们基于矩阵旋转,可以绕任意轴旋转。如果要使用世界坐标系旋转,您应该使用第二个函数rotateAroundWorldAxis()。

// Rotate an object around an arbitrary axis in object space
var rotObjectMatrix;
function rotateAroundObjectAxis(object, axis, radians) {
    rotObjectMatrix = new THREE.Matrix4();
    rotObjectMatrix.makeRotationAxis(axis.normalize(), radians);

    // old code for Three.JS pre r54:
    // object.matrix.multiplySelf(rotObjectMatrix);      // post-multiply
    // new code for Three.JS r55+:
    object.matrix.multiply(rotObjectMatrix);

    // old code for Three.js pre r49:
    // object.rotation.getRotationFromMatrix(object.matrix, object.scale);
    // old code for Three.js r50-r58:
    // object.rotation.setEulerFromRotationMatrix(object.matrix);
    // new code for Three.js r59+:
    object.rotation.setFromRotationMatrix(object.matrix);
}

var rotWorldMatrix;
// Rotate an object around an arbitrary axis in world space       
function rotateAroundWorldAxis(object, axis, radians) {
    rotWorldMatrix = new THREE.Matrix4();
    rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);

    // old code for Three.JS pre r54:
    //  rotWorldMatrix.multiply(object.matrix);
    // new code for Three.JS r55+:
    rotWorldMatrix.multiply(object.matrix);                // pre-multiply

    object.matrix = rotWorldMatrix;

    // old code for Three.js pre r49:
    // object.rotation.getRotationFromMatrix(object.matrix, object.scale);
    // old code for Three.js pre r59:
    // object.rotation.setEulerFromRotationMatrix(object.matrix);
    // code for r59+:
    object.rotation.setFromRotationMatrix(object.matrix);
}

所以你应该在你的 anim 函数内调用这些函数(requestAnimFrame 回调),这会导致绕 x 轴旋转 90 度:

var xAxis = new THREE.Vector3(1,0,0);
rotateAroundWorldAxis(mesh, xAxis, Math.PI / 180);

3
我得到了一个人在 GitHub 上发布的两个函数。我必须编辑它们,以便在最新版本的 threejs 中使用新的函数名称等。我自己也一直在思考这个问题。垃圾收集器会定期清除对象。我查看了 Chrome 中的内存使用图表,它会随着对象的创建和销毁而上下波动,但迄今为止,我使用的所有内容的动画效果都很好。 - Cory Gross
我该如何使用这个来围绕平面边缘旋转? - Tibor Szasz
2
这种方法需要注意!在Three.js中,应该使用Object3D上的直接矩阵操作(使用object.matrixAutoUpdate = false),或者对旋转、位置等属性进行操作。three.js文档 此外,rotateAroundWorldAxis不适用于对象非均匀缩放的情况。你需要从object.matrix中提取旋转,与rotWorldMatrix相乘,并将object.rotation的组件设置为结果。 - Michbeckable
@Michbeckable,你能否在这里发布一个你所描述的示例? - Tschallacka
@MichaelDibbets 一段时间过去了,但问题是直接使用 object.matrix 进行乘法运算,因为该矩阵是旋转、缩放和变换的组合。为了在非均匀缩放时保险起见,您可以使用 object.matrix.extractRotation(m) 并将矩阵 mrotWorldMatrix 相乘。最后,您可以执行 object.rotation.setFromRotationMatrix(m)。如果您的 matrixAutoUpdate = true,则 ThreeJS 将自动将对旋转属性的更改合成到 object.matrix 中。 - Michbeckable
显示剩余2条评论

13

我需要使用rotateAroundWorldAxis函数,但是上面的代码在最新版本(r52)中不起作用。看起来getRotationFromMatrix()setEulerFromRotationMatrix()替换了。

function rotateAroundWorldAxis( object, axis, radians ) {

    var rotationMatrix = new THREE.Matrix4();

    rotationMatrix.makeRotationAxis( axis.normalize(), radians );
    rotationMatrix.multiplySelf( object.matrix );                       // pre-multiply
    object.matrix = rotationMatrix;
    object.rotation.setEulerFromRotationMatrix( object.matrix );
}

9

使用r55版本时,您需要将以下代码:
rotationMatrix.multiplySelf( object.matrix );
更改为以下代码:
rotationMatrix.multiply( object.matrix );


8
在Three.js R59中,object.rotation.setEulerFromRotationMatrix(object.matrix);已经被更改为object.rotation.setFromRotationMatrix(object.matrix); 3js的变化如此之快 :D

6

提醒一下,在r52版本中,该方法被称为setEulerFromRotationMatrix而不是getRotationFromMatrix


4

大约在r59版本之后,这个变得更容易了(绕x轴旋转):

bb.GraphicsEngine.prototype.calcRotation = function ( obj, rotationX)
{
    var euler = new THREE.Euler( rotationX, 0, 0, 'XYZ' );
    obj.position.applyEuler(euler);
}

2

3
链接的失效速度惊人地快,这将使得这个答案变得不太有用。你能否将解决方案的相关部分提炼成文字或代码放在回答的正文中? - user1531971

2

在Three.js R66版本中,这是我使用的代码(CoffeeScript版本):

THREE.Object3D.prototype.rotateAroundWorldAxis = (axis, radians) ->
  rotWorldMatrix = new THREE.Matrix4()
  rotWorldMatrix.makeRotationAxis axis.normalize(), radians
  rotWorldMatrix.multiply this.matrix
  this.matrix = rotWorldMatrix
  this.rotation.setFromRotationMatrix this.matrix

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