代码之家  ›  专栏  ›  技术社区  ›  Soroush Falahati

将Eular角从世界轴转换为局部轴

  •  0
  • Soroush Falahati  · 技术社区  · 10 年前

    我正在尝试使用iPhone的运动管理器对象创建一个VR应用程序。我所说的VR是指一个在摄像头上显示地点的应用程序。

    我可以成功地使用Z轴的偏航、俯仰和滚动来可视化iPhone的方向->X->Y旋转顺序。

    以下是我迄今为止所做的事情的图片:

    enter image description here enter image description here

    因此,我可以旋转设备,它将在我为监控而创建的Windows应用程序中正确旋转。 这是正确的,稍后我将显示代码。但这不是我想做的。

    我想做的其实与此相反。我不想让设备移动。我希望它周围的世界旋转。所以,如果用户将相机指向东方,他应该看到“东方标志”,如果他将方向改为北方,他应该在屏幕上看到“北方标志”。但准确。不像其他应用程序那样取消了滚动运动。

    这里的问题是,当我将设备从放在桌子上移动到纵向模式时,向右和向左旋转会导致旋转不正确。如果我把它放在横向模式,那么从上到下的旋转是不正确的。换言之,旋转是基于世界轴的,只有当设备放置在地面上时,旋转才会正确。因为这是我认为的参考系。

    这里我想问的是如何转换这些角度,这样我就能看到我期望的结果。应该有一种基于三角的方法。

    这些是我用来计算旋转矩阵的函数:

        private static Matrix4 CreateRotationMatrix(char axis, float radians, bool rightHanded = true)
        {
            float c = (float)Math.Cos(radians);
            float s = (float)Math.Sin(radians) * (rightHanded ? 1 : -1);
            switch (axis)
            {
                case 'X':
                    return new Matrix4(
                        new Vector4(1.0f, 0.0f, 0.0f, 0.0f),
                        new Vector4(0.0f, c, -s, 0.0f),
                        new Vector4(0.0f, s, c, 0.0f),
                        new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
                case 'Y':
                    return new Matrix4(
                        new Vector4(c, 0.0f, s, 0.0f),
                        new Vector4(0.0f, 1.0f, 0.0f, 0.0f),
                        new Vector4(-s, 0.0f, c, 0.0f),
                        new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
                case 'Z':
                    return new Matrix4(
                        new Vector4(c, -s, 0.0f, 0.0f),
                        new Vector4(s, c, 0.0f, 0.0f),
                        new Vector4(0.0f, 0.0f, 1.0f, 0.0f),
                        new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
                default:
                    return Matrix4.Identity;
            }
        }
    
        public static Matrix4 MatrixFromEulerAngles(
            Vector3 euler,
            string order,
            bool isRightHanded = true,
            bool isIntrinsic = true)
        {
            if (order.Length != 3) throw new ArgumentOutOfRangeException("order", "String must have exactly 3 charecters");
            // X = Pitch
            // Y = Yaw
            // Z = Roll
            return isIntrinsic
                       ? CreateRotationMatrix(order[2], GetEulerAngle(order[2], euler), isRightHanded)
                         * CreateRotationMatrix(order[1], GetEulerAngle(order[1], euler), isRightHanded)
                         * CreateRotationMatrix(order[0], GetEulerAngle(order[0], euler), isRightHanded)
                       : CreateRotationMatrix(order[0], GetEulerAngle(order[0], euler), isRightHanded)
                         * CreateRotationMatrix(order[1], GetEulerAngle(order[1], euler), isRightHanded)
                         * CreateRotationMatrix(order[2], GetEulerAngle(order[2], euler), isRightHanded);
        }
    
        private static float GetEulerAngle(char angle, Vector3 euler)
        {
            switch (angle)
            {
                case 'X':
                    return euler.X;
                case 'Y':
                    return euler.Y;
                case 'Z':
                    return euler.Z;
                default:
                    return 0f;
            }
        }
    

    这就是我如何将矩阵应用于OpenGL:

     Matrix4 projectionMatrix = Helper.MatrixFromEulerAngles(new Vector3(pitch, yaw, roll), "YXZ", true, true);
     GL.LoadMatrix(ref projectionMatrix);
    
    1 回复  |  直到 10 年前
        1
  •  0
  •   Soroush Falahati    10 年前

    好的,反转是答案的一部分。这帮了大忙。谢谢@dari和@Spektre的建议。

    但完整的答案是,改变旋转方向(右手->左手),改变旋转顺序(YXZ->ZXY,换句话说,从内到外),并反转结果矩阵。 在询问之前,我曾单独尝试过这三种方法,但从未想过将它们一起使用。

    因此:

    Matrix4 projectionMatrix = Helper.MatrixFromEulerAngles(new Vector3(pitch, yaw, roll), "YXZ", false, false);
    projectionMatrix.Invert();
    GL.LoadMatrix(ref projectionMatrix);