如何在Java 3D中旋转一个对象?

3

我用以下代码在Java 3D中绘制了一个圆锥体:

Cone cone = new Cone(2f, 3f);

Transform3D t3d = new Transform3D();
TransformGroup coneTransform = new TransformGroup(t3d);
coneTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

t3d.setTranslation(new Vector3f(0f,0f,0f);
coneTransform.setTransform(t3d);
coneTransform.addChild(cone);

this.addChild(coneTransform);

假设我有一个圆锥体,它位于点(1,1,1),我希望圆锥的尖端指向通过(0,0,0)和(1,1,1)运行的虚拟线路...我该如何做到这一点?

以下是我一直在尝试的示例:

Transform3D t3d = new Transform3D();  

Vector3f direction = new Vector3f(1,2,1);    

final double angleX = direction.angle(new Vector3f(1,0,0));
final double angleY = direction.angle(new Vector3f(0,1,0));
final double angleZ = direction.angle(new Vector3f(0,0,1));

t3d.rotX(angleX);
t3d.rotY(angleY);
t3d.rotZ(angleZ);

t3d.setTranslation(direction);

coneTransform.setTransform(t3d);

Thanks in advance for all help!

5个回答

4

我目前正在学习Java 3D,据我的了解,旋转方法只会将变换设置为围绕该轴的旋转。 因此,如果您希望绕多个轴旋转,则需要使用第二个Transform3D。
例如:

Transform3D rotation = new Transform3D();
Transform3D temp = new Transform3D();

rotation.rotX(Math.PI/2);
temp.rotZ(Math.PI/2);
rotation.mul(temp); // multiply the 2 transformation matrices together.

关于为什么要使用Math.PI,这是因为它使用弧度而不是角度,其中Math.PI等同于180度。
查找当前方向和预期方向之间的角度并不难 - 您可以使用Vector3fs,其中包括angle()方法。一个向量将设置初始方向,另一个向量将设置预期方向。 然而,这并没有告诉您角度在哪些轴上。这需要检查向量以查看哪些部分已设置。[当然,API中可能有我目前不知道的东西]

啊...所以我需要为每个我想要旋转的轴创建一个单独的Transform3D吗?我试图在同一个Transform3D实例上执行rotX(),rotY(),rotZ()。我会尝试一下的。 - Cuga
我选择使用四元数进行旋转。绕x和y轴的旋转一直给我带来不可靠的问题。显然,用四元数可以避免万向锁问题。 - Cuga

2

这不是针对Java3D的特定答案。

通常可以构建一个矩阵,其中有4个向量来描述它:

1)一个侧(或横向)向量
2)一个上向量
3)一个方向向量
4)一个位置

每个4x4矩阵的行。

因此,对于一个简单的单位矩阵,我们有以下矩阵(我将定义一个列主要矩阵,对于一个行主要矩阵,您只需要交换矩阵索引,使得整个矩阵中的第2行第3列变为第3行第2列)。

1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

在这里,第一列是侧向量。第二列是上向量。第三列是方向向量,第四列是位置向量。

从逻辑上我们可以看到,向量(1,0,0,0)沿着x轴指向(因此是侧向量)。 向量(0,1,0,0)沿着y轴指向(因此是上向量)。 第三个向量(0,0,1,0)沿着Z轴指向(因此是方向向量)。 第四个向量(0,0,0,1)表示对象根本没有移动。

现在假设我们想面向X轴。

显然,这意味着我们的方向向量为(1,0,0,0)。上仍然是(0,1,0,0),位置仍为0,0,01。那么我们的侧向量将是什么?嗯,从逻辑上讲,它应该沿z轴指向。但是哪个方向?好吧,把手指拿起来,让一个手指指向前面,一个手指指向侧面,一个手指指向上面。 现在旋转,使得前指向的手指与侧指向的手指指向相同的方向。侧指向的手指现在指向哪个方向?与原始指向手指的方向相反。因此,矩阵是

 0 0 1 0
 0 1 0 0
-1 0 0 0
 0 0 0 1

在这一点上,事情似乎变得有些复杂。取一个任意的位置和一个任意的点来查看是很简单的(我将它们称为vPos和vFocus)。通过从vFocus中减去vPos(vFocus.x - vPos.x,vFocus.y - vPos.y,vFocus.z - vPos.z,vFocus.w - vPos.w),形成一个指向vFocus的向量,这很容易。请记住,所有位置都应该在w位置定义为“1”,而所有方向都应该具有“0”。当您执行上述减法时,这自动处理,因为两个ws中的1将会相互抵消并留下0。无论如何,现在我们有了一个指向vFocus的矢量,我们称之为vDir。不幸的是,它的长度等于vPos和vFocus之间的差异。然而,如果我们将vDir向量除以其长度(vDir.x / length,vDir.y / length,vDir.z / length,vDir.w / length),则我们可以将其标准化,并且我们有一个总长度为1的方向。
此时,我们现在拥有矩阵的第三列和第四列。现在,假设up仍为(0,1,0,0)或vUp。我们可以假设方向和vUp的叉积将产生一个垂直于由vDir和vUp形成的平面的向量(也是单位长度)。这给我们了我们的侧向量或vLat。现在..我们有点假设了up向量,所以它不完全正确。现在,通过取vLat和vDir的叉积,我们可以精确地计算出它,并获得所有4个向量。
因此,最终矩阵的定义如下:
vLat.x vUp.x vDir.x vPos.x
vLat.y vUp.y vDir.y vPos.y
vLat.z vUp.z vDir.z vPos.z
vLat.w vUp.w vDir.w vPos.w

这并不是完整的答案,因为当你接近于(0, 1, 0, 0)向量附近时会遇到问题,但这对大多数情况应该有效。


1

我终于通过使用四元数找到了自己想要做的事情,这是我在这里学到的:http://www.cs.uic.edu/~jbell/Courses/Eng591_F1999/outline_2.html 这是我的解决方案。

创建圆锥:

 private void attachCone(float size) {
        Cone cone = new Cone(size, size* 2);

        // The group for rotation
        arrowheadRotationGroup = new TransformGroup();
        arrowheadRotationGroup.
             setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        arrowheadRotationGroup.addChild(cone);

        // The group for positioning the cone
        arrowheadPositionGroup = new TransformGroup();
        arrowheadPositionGroup. 
              setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        arrowheadPositionGroup.addChild(arrowheadRotationGroup);

        super.addChild(arrowheadPositionGroup);
    }

现在,当我想要将圆锥旋转到指向某个方向时,该方向由从点(0,0,0)到(direction.x,direction.y,direction.z)的向量指定,我使用以下代码:
private final Vector3f yAxis = new Vector3f(0f, 1f, 0f);
private Vector3f direction; 

private void rotateCone() {
        // Get the normalized axis perpendicular to the direction 
        Vector3f axis = new Vector3f();
        axis.cross(yAxis, direction);
        axis.normalize();

        // When the intended direction is a point on the yAxis, rotate on x
        if (Float.isNaN(axis.x) && Float.isNaN(axis.y) && Float.isNaN(axis.z)) 
        {
            axis.x = 1f;
            axis.y = 0f;
            axis.z = 0f;
        }
        // Compute the quaternion transformations
        final float angleX = yAxis.angle(direction);
        final float a = axis.x * (float) Math.sin(angleX / 2f);
        final float b = axis.y * (float) Math.sin(angleX / 2f);
        final float c = axis.z * (float) Math.sin(angleX / 2f);
        final float d = (float) Math.cos(angleX / 2f);

        Transform3D t3d = new Transform3D();
        Quat4f quat = new Quat4f(a, b, c, d);
        t3d.set(quat);
        arrowheadRotationGroup.setTransform(t3d);

        Transform3D translateToTarget = new Transform3D();
        translateToTarget.setTranslation(this.direction);
        arrowheadPositionGroup.setTransform(translateToTarget);
    }

0

我认为这应该可以解决:

coneTransform.rotX(Math.PI / 4);
coneTransform.rotY(Math.PI / 4);

谢谢。t3d.rotX(Math.PI / 2);以及类似的Y,Z旋转圆锥。您能描述如何计算旋转X和Y的角度吗?例如,如果我从(0,0,0)到(1,1,1)绘制一条线,如何计算旋转量? - Cuga
1
旋转函数以弧度值作为输入。一整个旋转是2*PI,或者垂直线之间有PI/2度(这意味着我错了,你只需要旋转PI/4)。 通常情况下,您不需要沿着所有三个角度旋转,因为您可以通过沿两个轴旋转来描述任何旋转。 - Zed
哈哈——我刚刚意识到我不需要所有三个旋转。我现在正在纸上计算我需要的角度。感谢你的帮助。 - Cuga
围绕x和y轴旋转一直给我带来不可靠的问题。最终,我发现了一种方法,你可以在我发布的答案中看到。 - Cuga

0

你可以为Transform3D设置一个旋转矩阵。你可以使用在线的旋转矩阵计算器http://toolserver.org/~dschwen/tools/rotationmatrix.html来获取旋转矩阵。这是我的示例:

    Matrix3f mat = new Matrix3f(0.492403876506104f, 0.586824088833465f,
            -0.642787609686539f, 0.413175911166535f, 0.492403876506104f,
            0.766044443118978f, 0.766044443118978f, -0.642787609686539f, 0f);

    Transform3D trans = new Transform3D();

    trans.set(mat);

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