OpenGL将全局旋转应用于局部欧拉角

时间:2018-04-13 20:48:46

标签: java android opengl-es quaternions rotational-matrices

我正在使用OpenGL开发Android应用程序。

在数据库中,我使用局部Euler旋转,x,y,然后z来存储对象的旋转,但是在编辑器中,我想通过x,y或z全局轴应用全局旋转。我采取了两种方法,概述如下。

我已简化这些方法以删除不相关的Android代码。

我尝试过采用矩阵方法,但是在第二次调用该方法后,该对象似乎在未与全局x,y或z对齐的轴上旋转。我已经读过某个地方浮点错误随着时间的推移而积累,使得旋转矩阵在数值上不稳定,我认为这是第一种方法中发生的事情。

// rotAxis = 0 means rotation around the X global axis
// rotAxis = 1 means rotation around the Y global axis
// rotAxis = 2 means rotation around the Z global axis
public void executeRotationWithMatrix(float rotAngle, int rotAxis){
    float[] rotationMatrix = new float[16];

    // Matrix class is in android.opengl
    Matrix.setIdentityM(rotationMatrix, 0);

    switch (rotAxis){
        case 0:
            Matrix.rotateM(rotationMatrix, 0, rotAngle, 1.f, 0.f, 0.f);
            break;
        case 1:
            Matrix.rotateM(rotationMatrix, 0, rotAngle, 0.f, 1.f, 0.f);
            break;
        case 2:
            Matrix.rotateM(rotationMatrix, 0, rotAngle, 0.f, 0.f, 1.f);
            break;
    }

    float rotx = getLocalRotationOfObjectOnX(); // Pseudocode
    float roty = getLocalRotationOfObjectOnY(); // Pseudocode
    float rotz = getLocalRotationOfObjectOnZ(); // Pseudocode

    Matrix.rotateM(rotationMatrix, 0, rotx, 1.f, 0.f, 0.f);
    Matrix.rotateM(rotationMatrix, 0, roty, 0.f, 1.f, 0.f);
    Matrix.rotateM(rotationMatrix, 0, rotz, 0.f, 0.f, 1.f);

    Vector3f rotationVector = rotationMatrixToEulerAngles(rotationMatrix);

saveLocalRotationOfObjectOnX(rotationVector.x); // Pseudocode
saveLocalRotationOfObjectOnY(rotationVector.y); // Pseudocode
saveLocalRotationOfObjectOnZ(rotationVector.z); // Pseudocode
}

在第二种方法中,我尝试通过应用旋转来采用旋转四元数方法,但每当我尝试使用此方法时,我都会得到更奇怪的结果。

// rotAxis = 0 means rotation around the X global axis
// rotAxis = 1 means rotation around the Y global axis
// rotAxis = 2 means rotation around the Z global axis
public void executeRotationWithQuat(float rotAngle, int rotAxisInd){
    Quat4f rotationQuat = new Quat4f(0, 0, 0, 1);
    Quat4f tempQuat = new Quat4f(0, 0, 0, 1);

    switch (rotAxisInd){
        case 0:
            QuaternionUtil.setRotation(tempQuat, new Vector3f(1, 0, 0), rotAngle);
            break;
        case 1:
            QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 1, 0), rotAngle);
            break;
        case 2:
            QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 0, 1), rotAngle);
            break;
    }

    tempQuat.normalize();
    rotationQuat.mul(tempQuat);
    rotationQuat.normalize();

    float rotx = getLocalRotationOfObjectOnX(); // Pseudocode
    float roty = getLocalRotationOfObjectOnY(); // Pseudocode
    float rotz = getLocalRotationOfObjectOnZ(); // Pseudocode

    QuaternionUtil.setRotation(tempQuat, new Vector3f(1, 0, 0), rotx); tempQuat.normalize();
    rotationQuat.mul(tempQuat);
    rotationQuat.normalize();

    QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 1, 0), roty); tempQuat.normalize();
    rotationQuat.mul(tempQuat);
    rotationQuat.normalize();

    QuaternionUtil.setRotation(tempQuat, new Vector3f(0, 0, 1), rotz); tempQuat.normalize();
    rotationQuat.mul(tempQuat);
    rotationQuat.normalize();

    float qw = rotationQuat.w;
    float qx = rotationQuat.x;
    float qy = rotationQuat.y;
    float qz = rotationQuat.z;

    float[] rotationMatrix = new float[]{
            1.0f - 2.0f*qy*qy - 2.0f*qz*qz, 2.0f*qx*qy - 2.0f*qz*qw, 2.0f*qx*qz + 2.0f*qy*qw, 0.0f,
            2.0f*qx*qy + 2.0f*qz*qw, 1.0f - 2.0f*qx*qx - 2.0f*qz*qz, 2.0f*qy*qz - 2.0f*qx*qw, 0.0f,
            2.0f*qx*qz - 2.0f*qy*qw, 2.0f*qy*qz + 2.0f*qx*qw, 1.0f - 2.0f*qx*qx - 2.0f*qy*qy, 0.0f,
            0.0f, 0.0f, 0.0f, 1.0f
    };

    Vector3f rotationVector = rotationMatrixToEulerAngles(rotationMatrix);

saveLocalRotationOfObjectOnX(rotationVector.x); // Pseudocode
saveLocalRotationOfObjectOnY(rotationVector.y); // Pseudocode
saveLocalRotationOfObjectOnZ(rotationVector.z); // Pseudocode
}

以下是上述两种方法中使用的辅助方法。

public Vector3f rotationMatrixToEulerAngles(float[] m){
    float sy = (float)Math.sqrt(m[6]*m[6] + m[10]*m[10]);

    float x, y, z;

    x = (float)Math.atan2(m[6], m[10]);
    y = (float)Math.atan2(-m[2], sy);
    z = (float)Math.atan2(m[1], m[0]);

    //convert angles from radians to degrees
    float conFactor = (float)(180/Math.PI);
    x *= conFactor;
    y *= conFactor;
    z *= conFactor;

    return new Vector3f(x, y, z);
}

public class QuaternionUtil {
    public static void setRotation(Quat4f q, Vector3f axis, float angle) {
        float d = axis.length();
        assert (d != 0f);
        float s = (float)Math.sin(angle * 0.5f) / d;
        q.set(axis.x * s, axis.y * s, axis.z * s, (float) Math.cos(angle * 0.5f));
    }
}

public class Vector3f{
    public final float length() {
        return (float)Math.sqrt((double)(this.x * this.x + this.y * this.y + this.z * this.z));
    }
}

非常感谢任何帮助!

0 个答案:

没有答案