如何实现基于四元数的相机?


14

更新这里的错误是一个非常简单的错误。我错过了弧度到度的转换。如果您还有其他问题,则无需阅读全文。

我看了几本关于此的教程,当我以为自己理解时,便尝试实现基于四元数的相机。问题是,旋转大约10分钟后,它无法正常工作。10度会跳回-10度。我不知道怎么了 我正在使用openTK,它已经有一个四元数类。我是opengl的菜鸟,我只是出于娱乐目的而做,并不真正了解四元数,所以我可能在这里做一些愚蠢的事情。这是一些代码:(实际上,除了加载和绘制vbo的方法外,几乎所有代码(它取自于演示vbo-s的OpenTK示例))

我将多维数据集加载到vbo中并初始化相机的四元数

protected override void OnLoad(EventArgs e) {
    base.OnLoad(e);

    cameraPos = new Vector3(0, 0, 7);
    cameraRot = Quaternion.FromAxisAngle(new Vector3(0,0,-1), 0);

    GL.ClearColor(System.Drawing.Color.MidnightBlue);
    GL.Enable(EnableCap.DepthTest);

    vbo = LoadVBO(CubeVertices, CubeElements);
}

我在这里加载透视投影。每次我调整窗口大小时,它都会在开始时加载。

protected override void OnResize(EventArgs e) {
    base.OnResize(e);

    GL.Viewport(0, 0, Width, Height);

    float aspect_ratio = Width / (float)Height;

    Matrix4 perpective = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspect_ratio, 1, 64);
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadMatrix(ref perpective);
}

在这里,我得到了最后一个旋转值,并创建了一个仅代表最后一个旋转的新四元数,并将其与摄影机四元数相乘。之后,我将其转换为axis-angle,以便opengl可以使用它。(这是我从几个在线四元数教程中了解的方式)

protected override void OnRenderFrame(FrameEventArgs e) {
    base.OnRenderFrame(e);

    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    double speed = 1;
    double rx = 0, ry = 0;

    if (Keyboard[Key.A]) {
        ry = -speed * e.Time;
    }

    if (Keyboard[Key.D]) {
        ry = +speed * e.Time;
    }

    if (Keyboard[Key.W]) {
        rx = +speed * e.Time;
    }

    if (Keyboard[Key.S]) {
        rx = -speed * e.Time;
    }

    Quaternion tmpQuat = Quaternion.FromAxisAngle(new Vector3(0,1,0), (float)ry);
    cameraRot = tmpQuat * cameraRot;
    cameraRot.Normalize();

    GL.MatrixMode(MatrixMode.Modelview);
    GL.LoadIdentity();

    Vector3 axis;
    float angle;

    cameraRot.ToAxisAngle(out axis, out angle);
    //////////////////////////////////////////////////////////////////////
    // THIS IS WHAT I DID WRONG: I NEED TO CONVERT FROM RADIANS TO DEGREES
    //////////////////////////////////////////////////////////////////////
    //BEFORE
    //GL.Rotate(angle, axis);
    //AFTER
    GL.Rotate(angle * (float)180.0/(float)Math.PI, axis);
    GL.Translate(-cameraPos);

    Draw(vbo);
    SwapBuffers();
}

这里有2张图片可以更好地解释:我旋转了一段时间,并从此开始:

这个

它跳到这个

在此处输入图片说明

任何帮助表示赞赏。

Update1:我将它们添加到写入文件的流写入器中:

    sw.WriteLine("camerarot: X:{0} Y:{1} Z:{2} W:{3} L:{4}", cameraRot.X, cameraRot.Y, cameraRot.Z, cameraRot.W, cameraRot.Length);
    sw.WriteLine("ry: {0}", ry);

该日志可在此处找到:http : //www.pasteall.org/26133/text。在770行,当camerarot.Y改变符号时,立方体从右跳到左。我不知道这是否正常。

Update2 是完整的项目。


2
我不明白这个问题。您是否尝试过打印用于渲染的四元数?
Nicol Bolas

1
同意,调试您的rx,ry和四元数值。
deceleratedcaviar

您为什么不将整个项目上传到某个地方进行RapidShare?会更容易。
bobobobo

好的,我到家后会上传它。
gyozo kudor

Answers:


9

虽然您没有在此处显示必要的代码来验证我的假设,但我几乎可以保证您的问题实际上是此行:

cameraRot.ToAxisAngle(out axis, out angle);

返回以弧度表示的角度值,而

GL.Rotate(angle, axis);

希望角度在要被提供

要修复它,您需要在将其传递到GL.Rotate()时转换角度值,如下所示:

GL.Rotate(angle * 180.0/3.141593, axis);

是的,这就是问题所在。:) 非常感谢。我知道opengl期望度数,我假设ToAxisAngle返回度数。我只是看了opentk代码,它是弧度。顺便说一句,我在最后提供了完整的源代码。
gyozo kudor 2011年

@NickCaplinger正确地指出,在C#中,存在一个Math.Pi可用常量,该常量应优先于我在最终计算中使用的文字3.141593值使用。
Trevor Powell

1

别。拥有可以像其他场景对象一样操作的摄像机似乎工作量较小,但从长远来看,最好使用摄像机定义位置,眼睛方向和上矢量。尤其是当您开始对运动模型进行编程时,使用四元数确实很痛苦。


1
同意 我也不喜欢设计应用程序的“如果很难说它必须更好”的方法,也==
Patrick Hughes

四元数很难发音吗?
notlesh 2011年

1
一点幽默就很有意义= D通过推断,我暗示这个问题来自一个不知道为什么他想要四元数相机的人,只是这是一个时髦的流行语,OMG,我现在想它太糟糕了!如果没有正确处理上面的损坏代码中的弧度和度数参数之类的问题,当然不是要解决的问题。
Patrick Hughes

3
这个问题不是真的与相机有关;它是关于如何在游戏代码中表示旋转,以及如何将这些游戏表示转换为OpenGL可以使用它们的形式。这是一个完全合理的问题,我认为嘲笑OP缺乏域经验不会以任何方式有所帮助。并不是说OP犯了一个愚蠢的错误;OpenGL在表达角度时坚持使用度数的想法是非常奇怪的。 每个人都是第一次使用OpenGL犯此错误。所以,下车吧,人们。真是的
特雷弗·鲍威尔

我只是想了解基于四元数的相机是如何工作的,所以我在没有其他事情要做的那天就完成了这个项目。我对此没有真正的计划。
gyozo kudor 2011年

0

对OpenTK内部了解不多。以我的经验,我没有看到正确执行quat的数学库可以保持所需的精度(机器),因为它们都会影响e ^ 2-e ^ 3错误。因此,您看到的效果是:精度(和epsilon值)约为10 ^ -5,并且“跳”近零。这是我的猜测:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.