首先,我必须说,我在这里要做的全部是关于点的,而不是对象或其他任何东西。我在这里使用OpenGL的目的更多是为了进行计算。

所以这是我的问题。我想在我的2d上下文中显示一些3d点(例如winform或图像);
为此,我编写了以下函数:(它在C#中,并且使用OpenTK(OpenGL包装器),但是我敢肯定,如果您了解OpenGL,则无论您对C#的了解如何都可以理解它)

    private Vector3 GetProjectedLocation(
        Vector3 pointPosition,
        Vector3 cameraPosition,
        Matrix4 cameraRotationMatrix)
    {
        float horizentalFov = MathHelper.DegreesToRadians(60.0f);
        float viewRatio = (float)this.RenderSize.Width / this.RenderSize.Height;
        Vector3 cameraReference = new Vector3(0, 0, -1);
        Vector3 transformedReference = Vector3.Transform(cameraReference, Matrix4.Invert(cameraRotationMatrix));
        Vector3 cameraLookatTarget = cameraPosition + transformedReference;
        Matrix4 modelMatrix = Matrix4.Identity;
        Matrix4 viewMatrix = Matrix4.LookAt(cameraPosition, cameraLookatTarget, new Vector3(0, 1, 0));
        Matrix4 projectionMatrix = Matrix4.CreatePerspectiveFieldOfView(horizentalFov, viewRatio, 0.1f, 100.0f);
        Matrix4 viewModelMatrix = viewMatrix * modelMatrix;
        return Helper.Project(
            pointPosition,
            viewModelMatrix,
            projectionMatrix,
            new Rectangle(new Point(), this.RenderSize));
    }

我还编写了另一个名为Project cuz的函数,但我在OpenTK中找不到它:
    public static Vector3 Project(Vector3 point, Matrix4 viewmodel, Matrix4 projection, Rectangle viewport)
    {
        Vector4 point4 = new Vector4(point, 1.0f);
        Vector4 pointModel = Vector4.Transform(point4, viewmodel);
        Vector4 pointProjection = Vector4.Transform(pointModel, projection);
        pointProjection.W = (float)(1.0 / pointProjection.W);
        pointProjection.X *= pointProjection.W;
        pointProjection.Y *= pointProjection.W;
        pointProjection.Z *= pointProjection.W;
        return new Vector3
                   {
                       X = (float)((pointProjection.X * 0.5 + 0.5) * viewport.Width + viewport.X),
                       Y = (float)((pointProjection.Y * 0.5 + 0.5) * viewport.Height + viewport.Y),
                       Z = (float)((1.0 + pointProjection.Z) * 0.5)
                   };
    }

当所有三个值(偏航角,俯仰角和侧倾角)均为零时,它似乎可以正常工作。我可以通过更改偏航角来水平移动该点,并使用俯仰值来垂直移动该点。滚动也将其移动到应有的状态。

当我将音高设置为90deg时,问题就会显现出来。在这种情况下,偏航不是将其水平移动,而是将其垂直移动,更糟糕的是,仅将其向下移动。无论我走到正侧还是负侧(-20deg或+ 20deg),它总是会下降。有趣的是,在俯仰角为90度时,Roll具有相同的效果,并且只能像偏航一样将其垂直和向下移动。将音高设置为-90deg具有相同的效果,但是这次偏航和横摇只会将其向上移动。

当我的两个轴平行并且失去一个方向(在这种情况下为滚动)时,似乎出现了万向节锁定问题。我仍然不明白为什么偏航只在一个方向上起作用。

无论如何,问题在于在垂直看时会发生这种锁定,而这正是我的程序经常出现的地方。我已经读到,如果更改旋转顺序,可以将云台锁移至顶部(查看频率较低的地方)。我尝试了一些订单,但找不到一个好的订单。所以这是我的代码,也许我做错了:
    private Matrix4 CreateRotationMatrix(char axis, float radians)
    {
        float c = (float)Math.Cos(radians);
        float s = (float)Math.Sin(radians);

        if (axis == 'X')
        {
            return new Matrix4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, c, -s, 0.0f, 0.0f, s, c, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
        }
        if (axis == 'Y')
        {
            return new Matrix4(c, 0.0f, s, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -s, 0.0f, c, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
        }
        return new Matrix4(c, -s, 0.0f, 0.0f, s, c, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
    }

    private Matrix4 MatrixFromEulerAngles(Vector3 euler, string order)
    {
        return CreateRotationMatrix(order[2], GetEulerAngle(order[2], euler))
               * CreateRotationMatrix(order[1], GetEulerAngle(order[1], euler))
               * CreateRotationMatrix(order[0], GetEulerAngle(order[0], euler));
    }

    private float GetEulerAngle(char angle, Vector3 euler)
    {
        if (angle == 'X') return euler.X;
        if (angle == 'Y') return euler.Y;
        return angle == 'Z' ? euler.Z : 0f;
    }

你有什么主意吗?

这是我知道的命令:
“XYZ”,“XZY”,“YXZ”,“YZX”,“ZXY”,“ZYX”

我可以提供一个示例程序来亲自查看问题。

最佳答案

我认为部分问题是由于您在第一段代码中使用了LookAt函数。这些函数的工作方式是通过查看全局“向上”向量的垂直于您所看方向的部分来计算局部“向上”向量。在您进行查找的情况下,全局“向上”向量(上面的代码中的(0,1,0))没有垂直于查找方向的分量,并且结果可能是不确定的或不稳定的。当您往下看时,同样适用。

相反,请尝试计算LookAt函数的“向上”向量,如下所示:

Vector3 localUpVec = Vector3.Transform(new Vector3(0, 1, 0), Matrix4.Invert(cameraRotationMatrix));
Matrix4 viewMatrix = Matrix4.LookAt(cameraPosition, cameraLookatTarget, localUpVec);

甚至更好的是,您可以完全绕过LookAt函数并自己进行计算:
Matrix4 viewMatrix = Matrix4.Subtract( Matrix4.Transpose(cameraRotationMatrix), Matrix4.Translation(cameraPosition) );

10-06 10:29