如何在 Java 3D 中旋转对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1330727/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to rotate an object in Java 3D?
提问by Cuga
I have a Cone I drew in Java 3D with the following code:
我有一个用 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);
Suppose I have the cone sitting at point (1,1,1) and I want the tip of the cone to point down an imaginary line running through (0,0,0) and (1,1,1)... how can I do this?
假设我让圆锥体位于 (1,1,1) 点,并且我希望圆锥体的尖端指向一条穿过 (0,0,0) 和 (1,1,1) 的假想线......我可以这样做吗?
Here's an example of what I've been trying:
这是我一直在尝试的一个例子:
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!
在此先感谢所有帮助!
采纳答案by Cuga
I finally figured out what I wanted to do by using Quaternions, which I learned about here: http://www.cs.uic.edu/~jbell/Courses/Eng591_F1999/outline_2.htmlHere's my solution.
我终于通过使用四元数弄清楚了我想要做什么,我在这里了解到:http: //www.cs.uic.edu/~jbell/Courses/Eng591_F1999/outline_2.html这是我的解决方案。
Creating the cone:
创建锥体:
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);
}
Now, when I want to rotate the cone to point in a certain direction specified as the vector from the point (0,0,0) to (direction.x, direction.y, direction.z), I use:
现在,当我想旋转锥体以指向指定为从点 (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);
}
回答by Farrell
I'm just learning Java 3D myself at the moment, and from my current knowledge, the rotation methods set the transform to a rotation about that axis only.
Therefore, if you wish to perform rotations about multiple axes, then you will need to use a second Transform3D.
ie:
我目前只是在学习 Java 3D,根据我目前的知识,旋转方法将变换设置为仅绕该轴旋转。因此,如果您希望围绕多个轴执行旋转,则需要使用第二个 Transform3D。
IE:
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.
As for the reason for Math.PI, this is because it uses radians instead of degrees, where Math.PI is equivalent to 180 degrees.
至于Math.PI的原因,这是因为它使用弧度而不是度数,其中Math.PI相当于180度。
Finding the angle between your current orientation and your intended orientation isn't too hard - you could use Vector3fs, with the angle() method. A Vector would be set up with the initial orientation, and another in the intended. However, this doesn't tell you in which axes the angle lies. Doing so would require examination of the vectors to see which segments are set. [of course, there may be something that I am currently unaware of in the API]
找到当前方向和预期方向之间的角度并不太难 - 您可以使用 Vector3fs 和angle() 方法。Vector 将设置为初始方向,另一个设置为预期方向。但是,这并不能告诉您角度位于哪个轴上。这样做需要检查向量以查看设置了哪些段。[当然,API中可能有我目前不知道的东西]
回答by Goz
This is not a java3D specific answer.
这不是特定于 java3D 的答案。
In general a matrix can be built such that there are 4 vectors that describe it.
通常,可以构建一个矩阵,以便有 4 个向量来描述它。
1) A side (or lateral) vector
2) An up vector
3) A direction vector
4) A position
1) 侧面(或横向)矢量
2) 向上矢量
3) 方向矢量
4) 位置
Each row of a 4x4 matrix.
4x4 矩阵的每一行。
Thus for a simple identity matrix we have the following matrix (I'll define a column major matrix, for a row major matrix all you need to do is swap the matrix indices around such that row 2 col 3 becomes row 3 col 2 throughout the matrix).
因此,对于一个简单的单位矩阵,我们有以下矩阵(我将定义一个列主矩阵,对于行主矩阵,您需要做的就是交换矩阵索引,以便第 2 列 3 行在整个过程中变为第 3 列 2矩阵)。
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
in this the first column is the side vector. The second column the up vector. The third the direction and the fourth the position.
在这个第一列是边向量。第二列向上向量。第三个方向,第四个位置。
Logically we can see that the vector (1, 0, 0, 0) points along the x axis (and thus is the side vector). The vector (0, 1, 0, 0) points along the y axis (and thus is the up vector). The third (0, 0, 1, 0) points along the Z-axis (and thus is the direction vector). The fourth (0, 0, 0, 1) indicates that the objects does not move at all.
从逻辑上讲,我们可以看到向量 (1, 0, 0, 0) 指向 x 轴(因此是边向量)。向量 (0, 1, 0, 0) 指向 y 轴(因此是向上向量)。第三个 (0, 0, 1, 0) 点沿 Z 轴(因此是方向向量)。第四个 (0, 0, 0, 1) 表示对象根本不移动。
Now lets say we wanted to face along the X-axis.
现在假设我们想要沿着 X 轴面对。
Obviously that would mean we have a vector of (1, 0, 0, 0 ) for our direction vector. Up would still be (0, 1, 0, 0) and position still 0, 0, 0 1. So what would our side vector be? Well, logically it would point along the z-axis. But which way? Well hold your fingers such that one finger points forward, one to the side and one up. Now rotate so that the forward finger is facing the same direction as the side pointing finger. Which way is the side pointing finger pointing now? The opposite direction to the original direction pointing finger. Thus the matrix is
显然,这意味着我们的方向向量有一个 (1, 0, 0, 0 ) 向量。Up 仍然是 (0, 1, 0, 0) 而位置仍然是 0, 0, 0 1。那么我们的边向量是什么?好吧,从逻辑上讲,它会沿着 z 轴指向。但哪种方式?握住您的手指,使一个手指指向前方,一个指向侧面,一个指向上方。现在旋转,使向前的手指与侧面的手指朝向相同的方向。侧面的手指现在指向哪个方向?手指指向原方向相反的方向。因此矩阵是
0 0 1 0
0 1 0 0
-1 0 0 0
0 0 0 1
At this point things seemingly get a little more complicated. It is simple enough to take an arbitrary position and an arbitrary point to look at (I'll call them vPos and vFocus). It is easy enough to form a vector from vPos to vFocus by subtracting vPos from vFocus (vFocus.x - vPos.x, vFocus.y - vPos.y, vFocus.z - vPos.z, vFocus.w - vPos.w ). Bear in mind all positions should be defined with a '1' in the w position where all directions should have a '0'. This is automatically taken care of when you do the subtraction above as the 1 in both ws will cancel out and leave 0. Anyway, we now have a vector pointing from the position towards vFocus we'll call it vDir. Unfortunately it has the length of the difference between vPos and vFocus. However if we divide the vDir vector by its length (vDir.x / length, vDir.y / length, vDir.z / length, vDir.w / length) then we normaliseit and we have a direction with a total length of 1.
在这一点上,事情似乎变得有点复杂。取任意位置和任意点进行查看非常简单(我将它们称为 vPos 和 vFocus)。通过从 vFocus (vFocus.x - vPos.x, vFocus.y - vPos.y, vFocus.z - vPos.z, vFocus.w - vPos.w ) 中减去 vPos 可以很容易地形成从 vPos 到 vFocus 的向量. 请记住,所有位置都应在 w 位置定义为“1”,其中所有方向都应为“0”。当你做上面的减法时,这会自动处理,因为两个 ws 中的 1 将抵消并留下 0。无论如何,我们现在有一个从位置指向 vFocus 的向量,我们称之为 vDir。不幸的是,它具有 vPos 和 vFocus 之间的差异。但是,如果我们将 vDir 向量除以其长度 (vDir.x / length, vDir.归一化它,我们有一个总长度为 1 的方向。
At this ponit we now have our 3rd and 4th columns of our matrix. Now, lets assuem up is still (0, 1, 0, 0) or vUp. We can assume that the crossproduct of the direction and vUp will produce a vector that is perpendicular (and also of unit length) to the plane formed by vDir and vUp. This gives us our side vector or vLat. Now .. we did kind of assume the up vector so its not strictly correct. We can now calculate it exactly by taking the cross product of vLat and vDir and we have all 4 vectors.
在这一点上,我们现在有了矩阵的第 3 列和第 4 列。现在,让我们假设 up 仍然是 (0, 1, 0, 0) 或 vUp。我们可以假设方向和 vUp 的叉积将产生一个向量,该向量垂直于(也是单位长度)由 vDir 和 vUp 形成的平面。这给了我们我们的侧向量或 vLat。现在..我们确实假设了向上向量,所以它不是严格正确的。我们现在可以通过取 vLat 和 vDir 的叉积来精确计算它,我们有所有 4 个向量。
The final matrix is thus defined as follows
最终矩阵定义如下
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
This isn't strictly the full answer as you will get problems as you look towards a point near to your (0, 1, 0, 0) vector but that should work for mostcases.
这并不是严格的完整答案,因为当您看向 (0, 1, 0, 0) 向量附近的点时会遇到问题,但这应该适用于大多数情况。
回答by guest
you can give your Transform3D a rotation matrix. you can get a rotation matrix using Rotation matrix calculator online: http://toolserver.org/~dschwen/tools/rotationmatrix.htmlhere's my example:
你可以给你的 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);
回答by Zed
I think this should do it:
我认为应该这样做:
coneTransform.rotX(Math.PI / 4);
coneTransform.rotY(Math.PI / 4);

