在 JavaFX 中正确旋转 3 轴上的 3D 对象
Rotate a 3D object on 3 axis in JavaFX properly
到目前为止,我在 JavaFX 中使用的旋转对象的方法是将其分为 3 组,每组都附加了一个 Rotate 并锁定到一个轴,如下所示:
Rotate heading, roll, pitch;
Group normalrotate, rollrotate, verticalrotate;
heading.setAxis(new Point3D(0,1,0));
normalrotate.getTransforms().add(heading);
roll.setAxis(new Point3D(0,0,1));
rollrotate.getTransforms().add(roll);
pitch.setAxis(new Point3D(1,0,0));
verticalrotate.getTransforms().add(pitch);
并在每次需要旋转对象时执行 setAngle()。这仅适用于航向和滚动,直到我决定我也需要俯仰。现在很多 OpenGL 和类似的教程都说旋转矩阵或四元数最适合这些类型的旋转,但是 javadoc 缺少任何有用的数据。
示例:当我将对象在 y 轴上旋转 180 度时会发生什么(以及在透明蓝色中实际应该发生什么)
我错过了什么吗?
我们将不胜感激。
所有这些教程都指向旋转矩阵是有原因的:在 3D 中,您不能一个接一个地同时执行旋转,您需要一次执行它们。由于JavaFX仅使用一个角度和一个轴,因此您必须提供将三个轴上的三个旋转转换为一个角度和一个轴的方法。
不久前,我在我的博客 post 中探讨了这些操作背后的所有数学知识,内容是关于使用 Leap Motion 获得手的三个旋转(俯仰、偏航、滚动)来旋转 3D 模型。
所以基本上,从三个旋转:俯仰(围绕其 X 轴)、偏航(围绕其 Y 轴)和滚动(围绕其 Z 轴),您有这些矩阵:
如果将它们组合在一起,您将得到一个矩阵:
无需进一步解释,角度和旋转单一轴分量可以通过以下方式计算:
可以写成:
private void matrixRotateNode(Node n, double alf, double bet, double gam){
double A11=Math.cos(alf)*Math.cos(gam);
double A12=Math.cos(bet)*Math.sin(alf)+Math.cos(alf)*Math.sin(bet)*Math.sin(gam);
double A13=Math.sin(alf)*Math.sin(bet)-Math.cos(alf)*Math.cos(bet)*Math.sin(gam);
double A21=-Math.cos(gam)*Math.sin(alf);
double A22=Math.cos(alf)*Math.cos(bet)-Math.sin(alf)*Math.sin(bet)*Math.sin(gam);
double A23=Math.cos(alf)*Math.sin(bet)+Math.cos(bet)*Math.sin(alf)*Math.sin(gam);
double A31=Math.sin(gam);
double A32=-Math.cos(gam)*Math.sin(bet);
double A33=Math.cos(bet)*Math.cos(gam);
double d = Math.acos((A11+A22+A33-1d)/2d);
if(d!=0d){
double den=2d*Math.sin(d);
Point3D p= new Point3D((A32-A23)/den,(A13-A31)/den,(A21-A12)/den);
n.setRotationAxis(p);
n.setRotate(Math.toDegrees(d));
}
}
其中 alf
是滚动,bet
是俯仰,gam
是偏航。
您可以找到完整的项目 here。
我可以提供一个解决方案。但这有点不文明,可能是一种不鼓励的做法。
Node[] my3dModel = get3DModel();//the method creates a mesh (I suppose that you used Interactive mesh, but it's not important here)
Pane paneForMyModel = new Pane();
paneForMyModel.getChildren.addAll(my3dModel);//you add your model to this pane
//and now
paneForMyModel.getTransforms().add(new Rotate(angle, axis));
到目前为止,我在 JavaFX 中使用的旋转对象的方法是将其分为 3 组,每组都附加了一个 Rotate 并锁定到一个轴,如下所示:
Rotate heading, roll, pitch;
Group normalrotate, rollrotate, verticalrotate;
heading.setAxis(new Point3D(0,1,0));
normalrotate.getTransforms().add(heading);
roll.setAxis(new Point3D(0,0,1));
rollrotate.getTransforms().add(roll);
pitch.setAxis(new Point3D(1,0,0));
verticalrotate.getTransforms().add(pitch);
并在每次需要旋转对象时执行 setAngle()。这仅适用于航向和滚动,直到我决定我也需要俯仰。现在很多 OpenGL 和类似的教程都说旋转矩阵或四元数最适合这些类型的旋转,但是 javadoc 缺少任何有用的数据。
示例:当我将对象在 y 轴上旋转 180 度时会发生什么(以及在透明蓝色中实际应该发生什么)
所有这些教程都指向旋转矩阵是有原因的:在 3D 中,您不能一个接一个地同时执行旋转,您需要一次执行它们。由于JavaFX仅使用一个角度和一个轴,因此您必须提供将三个轴上的三个旋转转换为一个角度和一个轴的方法。
不久前,我在我的博客 post 中探讨了这些操作背后的所有数学知识,内容是关于使用 Leap Motion 获得手的三个旋转(俯仰、偏航、滚动)来旋转 3D 模型。
所以基本上,从三个旋转:俯仰(围绕其 X 轴)、偏航(围绕其 Y 轴)和滚动(围绕其 Z 轴),您有这些矩阵:
如果将它们组合在一起,您将得到一个矩阵:
无需进一步解释,角度和旋转单一轴分量可以通过以下方式计算:
可以写成:
private void matrixRotateNode(Node n, double alf, double bet, double gam){
double A11=Math.cos(alf)*Math.cos(gam);
double A12=Math.cos(bet)*Math.sin(alf)+Math.cos(alf)*Math.sin(bet)*Math.sin(gam);
double A13=Math.sin(alf)*Math.sin(bet)-Math.cos(alf)*Math.cos(bet)*Math.sin(gam);
double A21=-Math.cos(gam)*Math.sin(alf);
double A22=Math.cos(alf)*Math.cos(bet)-Math.sin(alf)*Math.sin(bet)*Math.sin(gam);
double A23=Math.cos(alf)*Math.sin(bet)+Math.cos(bet)*Math.sin(alf)*Math.sin(gam);
double A31=Math.sin(gam);
double A32=-Math.cos(gam)*Math.sin(bet);
double A33=Math.cos(bet)*Math.cos(gam);
double d = Math.acos((A11+A22+A33-1d)/2d);
if(d!=0d){
double den=2d*Math.sin(d);
Point3D p= new Point3D((A32-A23)/den,(A13-A31)/den,(A21-A12)/den);
n.setRotationAxis(p);
n.setRotate(Math.toDegrees(d));
}
}
其中 alf
是滚动,bet
是俯仰,gam
是偏航。
您可以找到完整的项目 here。
我可以提供一个解决方案。但这有点不文明,可能是一种不鼓励的做法。
Node[] my3dModel = get3DModel();//the method creates a mesh (I suppose that you used Interactive mesh, but it's not important here)
Pane paneForMyModel = new Pane();
paneForMyModel.getChildren.addAll(my3dModel);//you add your model to this pane
//and now
paneForMyModel.getTransforms().add(new Rotate(angle, axis));