AR云渲染服务中旋转欧拉角度与四元组之间的转换公式

当前正在做AR云渲染服务,用到了需要把从AREngine中获取得到的旋转四元组配置到Unity上,怎么配置也没有配置正确。查了一些资料,把角度与四元组之间的转换公式记录一下。

四元组与欧拉角的转换具体的原理我在这就不讲了,大家可以自行查询资料。我们知道不同的XYZ相乘顺序,得到的四元组是不一样的。注意,在AREngine中,使用XYZ顺序,在Unity中,使用的是YXZ方式。

    /**
     * Sets the quaternion to the given euler angles.
     *
     * @param order X Y Z的顺序
     * @param x     the x in degrees,Angle of rotation around the x-axis
     * @param y     the y in degress, Angle of rotation around the y-axis
     * @param z     the z in degess, Angle of rotation around the z-axi
     * @return this quaternion
     */
    public static Quaternion EulerAngleToQuaternion(String order, double x, double y, double z) {
        Quaternion q = new Quaternion();

        //角度转换成弧度
        float xRotation = (float) Math.toRadians(x);
        float yRotation = (float) Math.toRadians(y);
        float zRotation = (float) Math.toRadians(z);

        double c1 = Math.cos(xRotation / 2);
        double c2 = Math.cos(yRotation / 2);
        double c3 = Math.cos(zRotation / 2);
        double s1 = Math.sin(xRotation / 2);
        double s2 = Math.sin(yRotation / 2);
        double s3 = Math.sin(zRotation / 2);

        switch (order) {

            case "XYZ":
                q.x = s1 * c2 * c3 + c1 * s2 * s3;
                q.y = c1 * s2 * c3 - s1 * c2 * s3;
                q.z = c1 * c2 * s3 + s1 * s2 * c3;
                q.w = c1 * c2 * c3 - s1 * s2 * s3;
                break;

            case "YXZ":
                q.x = s1 * c2 * c3 + c1 * s2 * s3;
                q.y = c1 * s2 * c3 - s1 * c2 * s3;
                q.z = c1 * c2 * s3 - s1 * s2 * c3;
                q.w = c1 * c2 * c3 + s1 * s2 * s3;
                break;

            case "ZXY":
                q.x = s1 * c2 * c3 - c1 * s2 * s3;
                q.y = c1 * s2 * c3 + s1 * c2 * s3;
                q.z = c1 * c2 * s3 + s1 * s2 * c3;
                q.w = c1 * c2 * c3 - s1 * s2 * s3;
                break;

            case "ZYX":
                q.x = s1 * c2 * c3 - c1 * s2 * s3;
                q.y = c1 * s2 * c3 + s1 * c2 * s3;
                q.z = c1 * c2 * s3 - s1 * s2 * c3;
                q.w = c1 * c2 * c3 + s1 * s2 * s3;
                break;

            case "YZX":
                q.x = s1 * c2 * c3 + c1 * s2 * s3;
                q.y = c1 * s2 * c3 + s1 * c2 * s3;
                q.z = c1 * c2 * s3 - s1 * s2 * c3;
                q.w = c1 * c2 * c3 - s1 * s2 * s3;
                break;

            case "XZY":
                q.x = s1 * c2 * c3 - c1 * s2 * s3;
                q.y = c1 * s2 * c3 - s1 * c2 * s3;
                q.z = c1 * c2 * s3 + s1 * s2 * c3;
                q.w = c1 * c2 * c3 + s1 * s2 * s3;
                break;

            default:
                Log.d("RemoteRender", "EulerAngleToQuaternion encountered an unknown order: " + order);

        }
        return q;
    }

    public static float[] ConvertToMatrix(float[] position, Quaternion quaternion, float[] scale) {
        float[] matrix = new float[16];
        float x = (float) quaternion.x;
        float y = (float) quaternion.y;
        float z = (float) quaternion.z;
        float w = (float) quaternion.w;
        float x2 = x + x,
                y2 = y + y,
                z2 = z + z;
        float xx = x * x2,
                xy = x * y2,
                xz = x * z2;
        float yy = y * y2,
                yz = y * z2,
                zz = z * z2;
        float wx = w * x2,
                wy = w * y2,
                wz = w * z2;
        float sx = scale[0], //scale
                sy = scale[1],
                sz = scale[2];
        matrix[0] = (1 - (yy + zz)) * sx;
        matrix[1] = (xy + wz) * sx;
        matrix[2] = (xz - wy) * sx;
        matrix[3] = 0;
        matrix[4] = (xy - wz) * sy;
        matrix[5] = (1 - (xx + zz)) * sy;
        matrix[6] = (yz + wx) * sy;
        matrix[7] = 0;
        matrix[8] = (xz + wy) * sz;
        matrix[9] = (yz - wx) * sz;
        matrix[10] = (1 - (xx + yy)) * sz;
        matrix[11] = 0;
        matrix[12] = position[0];
        matrix[13] = position[1];
        matrix[14] = position[2];
        matrix[15] = 1;
        return matrix;
    }

    public static float clamp(float value, float min, float max) {
        return Math.max(min, Math.min(max, value));
    }

    public static float[] QuaternionToEulerAngle(String order, Quaternion quaternion) {
        float[] euler = new float[3];
        float[] matrix = ConvertToMatrix(new float[]{0.f, 0.f, 0.f}, quaternion, new float[]{1.f, 1.f, 1.f});
        float m11 = matrix[0],
                m12 = matrix[4],
                m13 = matrix[8];
        float m21 = matrix[1],
                m22 = matrix[5],
                m23 = matrix[9];
        float m31 = matrix[2],
                m32 = matrix[6],
                m33 = matrix[10];
        switch (order) {
            case "XYZ":
                euler[1] = (float) Math.asin(clamp(m13, -1, 1));

                if (Math.abs(m13) < 0.9999999) {
                    euler[0] = (float) Math.atan2(-m23, m33);
                    euler[2] = (float) Math.atan2(-m12, m11);
                } else {
                    euler[0] = (float) Math.atan2(m32, m22);
                    euler[2] = (float) 0;
                }

                break;

            case "YXZ":
                euler[0] = (float) Math.asin(-clamp(m23, -1, 1));

                if (Math.abs(m23) < 0.9999999) {
                    euler[1] = (float) Math.atan2(m13, m33);
                    euler[2] = (float) Math.atan2(m21, m22);
                } else {
                    euler[1] = (float) Math.atan2(-m31, m11);
                    euler[2] = (float) 0;
                }

                break;

            case "ZXY":
                euler[0] = (float) Math.asin(clamp(m32, -1, 1));

                if (Math.abs(m32) < 0.9999999) {
                    euler[1] = (float) Math.atan2(-m31, m33);
                    euler[2] = (float) Math.atan2(-m12, m22);
                } else {
                    euler[1] = (float) 0;
                    euler[2] = (float) Math.atan2(m21, m11);
                }

                break;

            case "ZYX":
                euler[1] = (float) Math.asin(-clamp(m31, -1, 1));

                if (Math.abs(m31) < 0.9999999) {
                    euler[0] = (float) Math.atan2(m32, m33);
                    euler[2] = (float) Math.atan2(m21, m11);
                } else {
                    euler[0] = (float) 0;
                    euler[2] = (float) Math.atan2(-m12, m22);
                }

                break;

            case "YZX":
                euler[2] = (float) Math.asin(clamp(m21, -1, 1));

                if (Math.abs(m21) < 0.9999999) {
                    euler[0] = (float) Math.atan2(-m23, m22);
                    euler[1] = (float) Math.atan2(-m31, m11);
                } else {
                    euler[0] = (float) 0;
                    euler[1] = (float) Math.atan2(m13, m33);
                }

                break;

            case "XZY":
                euler[2] = (float) Math.asin(-clamp(m12, -1, 1));

                if (Math.abs(m12) < 0.9999999) {
                    euler[0] = (float) Math.atan2(m32, m22);
                    euler[1] = (float) Math.atan2(m13, m11);
                } else {
                    euler[0] = (float) Math.atan2(-m23, m33);
                    euler[1] = (float) 0;
                }

                break;

            default:
                Log.e("RemoteRender", "QuaternionToEulerAngle() encountered an unknown order: " + order);
        }


        //弧度转换成角度
        euler[0] = (float) Math.toDegrees(euler[0]);
        euler[1] = (float) Math.toDegrees(euler[1]);
        euler[2] = (float) Math.toDegrees(euler[2]);
        return euler;
    }

你写完代码之后,可以在https://quaternions.online/ 或者 https://www.andre-gaschler.com/rotationconverter/ 2个在线测试工具进行测试。

 

参考文献:

https://blog.csdn.net/weixin_40599145/article/details/90345547

https://github.com/mrdoob/three.js/blob/a3114f0e3d399cd8c22eb687d1c1e3eb30d62a86/src/math/Quaternion.js

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XR风云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值