处理:使用四元数旋转3D对象适用于x轴,但不适用于y或z轴吗?

时间:2018-07-20 01:39:21

标签: java math rotation processing quaternions

我正在根据来自串行设备的输入创建四元数。在处理中,我在以下代码中绕x轴旋转。我的四元数对象接受输入并使用set函数设置值,欧拉角和规格化。数学有问题吗?

我注释了z和y的旋转,但是与x轴相比,基本上该对象旋转得不是很好,或者是抖动的,所以效果很好。我在下面的代码中做错了什么?

作为参考,下面的shape(model)行是使用loadShape加载的.obj文件中加载的3d对象,shape函数将其显示在绘制循环中。

Quaternion q = new Quaternion(s);
q.set(x, y, z, w);
q = q.Euler(q.eulerAngles);
translate(x, y);
rotateX(q.eulerAngles.x);
//rotateY(q.eulerAngles.y);
//rotateZ(q.eulerAngles.z);
shape(model);
rotateX(-q.eulerAngles.x);
translate(-x, -y);

这是四元数类的一部分:

public class Quaternion {

   PApplet s;
   public float w,x,y,z;
   public PVector eulerAngles;

   public Quaternion(PApplet s, float x, float y, float z, float w){
     this.s = s;
     this.x = x;
     this.y = y;
     this.z = z;
     this.w = w;
     normalize();
   }

   public Quaternion(Quaternion q){
     this.s = q.s;
     this.w = q.w;
     this.x = q.x;
     this.y = q.y;
     this.z = q.z;
   }

   public Quaternion normalize() {
     float magnitude = w*w + x*x + y*y + z*z;
     if(magnitude != 0.0 && magnitude != 1.0){
       magnitude = 1.0f / s.sqrt(magnitude);
       w *= magnitude;
       x *= magnitude;
       y *= magnitude;
       z *= magnitude;
     }
     eulerAngles = setEulerAngles();
     return this;
   }

   public Quaternion set(float x, float y, float z, float w) {
     this.x = x;
     this.y = y;
     this.z = z;
     this.w = w;
     return normalize();
   }

   // Returns a rotation that rotates z degrees around 
   // the z axis, x degrees around the x axis, and y 
   // degrees around the y axis.
   public Quaternion Euler(){
      float roll = eulerAngles.x;
      float pitch = eulerAngles.y;
      float yaw = eulerAngles.z;

      float cr = (float)Math.cos(roll * 0.5);
      float sr = (float)Math.sin(roll * 0.5);
      float cp = (float)Math.cos(pitch * 0.5);
      float sp = (float)Math.sin(pitch * 0.5);
      float cy = (float)Math.cos(yaw * 0.5);
      float sy = (float)Math.sin(yaw * 0.5);

      w = cy*cr*cp + sy*sr*sp;
      x = cy*sr*cp - sy*cr*sp;
      y = cy*cr*sp + sy*sr*cp;
      z = sy*cr*cp - cy*sr*sp;
      return normalize();
   }


   // set euler angle representation of 
   // the rotation in 3-dim PVector
   private PVector setEulerAngles(){

     // roll: x-axis rotation
     float sinr = 2.0f * (w*x + y*z);
     float cosr = 1.0f - 2.0f * (x*x + y*y);
     float roll = (float)Math.atan2(sinr, cosr);

     // pitch: y-axis rotation
     float sinp = 2.0f * (w*y - z*x);
     float pitch = 0.0f;
     if(Math.abs(sinp) >= 1){
       pitch = (float)Math.copySign(Math.PI/2, sinp);
     } else {
       pitch = (float)Math.asin(sinp);
     }

     // yaw: z-axis rotation
     float siny = 2.0f * (w*z + x*y);
     float cosy = 1.0f - 2.0f * (y*y + z*z);
     float yaw = (float)Math.atan2(siny, cosy);

     return new PVector(roll, pitch, yaw);
   }
}

1 个答案:

答案 0 :(得分:0)

据我所知,从方法中获得的欧拉角应以ZYX顺序而不是XYZ顺序应用。但是无论如何,除非确实需要,否则不要弄乱欧拉角。而在这种情况下,您不会。

相反,convert the quaternion to a rotation matrix并使用applyMatrix()应用此变换。这里不会有歧义。

要还原变换,请不要应用逆变换(就像对rotateX(-q.eulerAngles.x)translate(-x, -y)所做的那样)。在开发过程中容易混淆顺序或忘记转换。而是使用pushMatrix() / popMatrix()resetMatrix

顺便说一句,我发现您的四元数类的定义非常令人困惑。有些方法返回的值是我不希望返回的值(例如normalize())。此外,我不认为将欧拉角表示与四元数一起存储不是一个好主意。而且即使您认为是这样,我也无法理解方法Euler()的目的,因为它既没有参数,也不能从外部设置欧拉角。