使用四元数进行 OpenGL 旋转[重复]

2024-03-25

因此,我正在编写一个程序,其中对象以 spacesim 方式移动,以便学习如何在 3D 空间中平滑地移动对象。在对欧拉角进行了一番研究之后,它们似乎并不真正适合任意方向上的自由形式 3D 运动,因此我决定继续使用似乎最适合这项工作的方法 - 四元数。我打算让对象始终围绕其局部 X-Y-Z 轴旋转,而不是围绕全局 X-Y-Z 轴旋转。

我尝试使用四元数实现旋转系统,但有些东西不起作用。当沿单个轴旋转物体时,如果之前没有进行任何旋转,物体将沿给定轴精细旋转。然而,当执行一次又一次的旋转时,第二次旋转并不总是沿着它应该旋转的局部轴 - 例如,在绕 Z 轴旋转约 90° 后,绕 Y 轴旋转仍然围绕全局 Y 轴发生,而不是与全局 X 轴对齐的新局部 Y 轴。

呵呵。那么让我们一步一步来看看。错误一定就在这里的某个地方。

第 1 步 - 捕获输入

我认为最好使用欧拉角(或俯仰-偏航-滚动方案)来捕获玩家输入。目前,方向键控制俯仰和偏航,而 Q 和 E 控制横滚。我因此捕获玩家输入(我使用 SFML 1.6):

    ///SPEEDS
    float ForwardSpeed = 0.05;
    float TurnSpeed = 0.5;

    //Rotation
    sf::Vector3<float> Rotation;
    Rotation.x = 0;
    Rotation.y = 0;
    Rotation.z = 0;
    //PITCH
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Up) == true)
    {
        Rotation.x -= TurnSpeed;
    }
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Down) == true)
    {
        Rotation.x += TurnSpeed;
    }
    //YAW
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Left) == true)
    {
        Rotation.y -= TurnSpeed;
    }
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Right) == true)
    {
        Rotation.y += TurnSpeed;
    }
    //ROLL
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Q) == true)
    {
        Rotation.z -= TurnSpeed;
    }
    if (m_pApp->GetInput().IsKeyDown(sf::Key::E) == true)
    {
        Rotation.z += TurnSpeed;
    }

    //Translation
    sf::Vector3<float> Translation;
    Translation.x = 0;
    Translation.y = 0;
    Translation.z = 0;

    //Move the entity
    if (Rotation.x != 0 ||
        Rotation.y != 0 ||
        Rotation.z != 0)
    {
        m_Entity->ApplyForce(Translation, Rotation);
    }

m_Entity 是我想要旋转的东西。它还包含表示对象旋转的四元数和旋转矩阵。

第 2 步 - 更新四元数

我不是 100% 确定这是应该完成的方式,但这是我在 Entity::ApplyForce() 中尝试做的事情:

//Rotation
m_Rotation.x += Rotation.x;
m_Rotation.y += Rotation.y;
m_Rotation.z += Rotation.z;

//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(m_Rotation.x, m_Rotation.y, m_Rotation.z);// * m_qRotation;

m_qRotation.RotationMatrix(m_RotationMatrix);

正如你所看到的,我不确定是否最好从更新的欧拉角构建一个新的四元数,或者我是否应该将代表变化的四元数与代表当前整体旋转的四元数相乘,这是印象我读书时得到本指南 http://www.cprogramming.com/tutorial/3d/quaternions.html。如果是后者,我的代码将如下所示:

//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(Rotation.x, Rotation.y, Rotation.z) * m_qRotation;

m_Rotation是以PYR格式存储的对象的当前旋转;轮换是玩家输入所要求的改变。但无论如何,问题可能出在我的四元数类的实现上。整个事情是这样的:

Quaternion::Quaternion(float Pitch, float Yaw, float Roll)
{
    float Pi = 4 * atan(1);

    //Set the values, which came in degrees, to radians for C++ trig functions
    float rYaw = Yaw * Pi / 180;
    float rPitch = Pitch * Pi / 180;
    float rRoll = Roll * Pi / 180;

    //Components
    float C1 = cos(rYaw / 2);
    float C2 = cos(rPitch / 2);
    float C3 = cos(rRoll / 2);
    float S1 = sin(rYaw / 2);
    float S2 = sin(rPitch / 2);
    float S3 = sin(rRoll / 2);

    //Create the final values
    a = ((C1 * C2 * C3) - (S1 * S2 * S3));
    x = (S1 * S2 * C3) + (C1 * C2 * S3);
    y = (S1 * C2 * C3) + (C1 * S2 * S3);
    z = (C1 * S2 * C3) - (S1 * C2 * S3);
}

//Overload the multiplier operator
Quaternion Quaternion::operator* (Quaternion OtherQuat)
{
    float A = (OtherQuat.a * a) - (OtherQuat.x * x) - (OtherQuat.y * y) - (OtherQuat.z * z);
    float X = (OtherQuat.a * x) + (OtherQuat.x * a) + (OtherQuat.y * z) - (OtherQuat.z * y);
    float Y = (OtherQuat.a * y) - (OtherQuat.x * z) - (OtherQuat.y * a) - (OtherQuat.z * x);
    float Z = (OtherQuat.a * z) - (OtherQuat.x * y) - (OtherQuat.y * x) - (OtherQuat.z * a);
    Quaternion NewQuat = Quaternion(0, 0, 0);
    NewQuat.a = A;
    NewQuat.x = X;
    NewQuat.y = Y;
    NewQuat.z = Z;
    return NewQuat;
}

//Calculates a rotation matrix and fills Matrix with it
void Quaternion::RotationMatrix(GLfloat* Matrix)
{
    //Column 1
    Matrix[0] = (a*a) + (x*x) - (y*y) - (z*z);
    Matrix[1] = (2*x*y) + (2*a*z);
    Matrix[2] = (2*x*z) - (2*a*y);
    Matrix[3] = 0;
    //Column 2
    Matrix[4] = (2*x*y) - (2*a*z);
    Matrix[5] = (a*a) - (x*x) + (y*y) - (z*z);
    Matrix[6] = (2*y*z) + (2*a*x);
    Matrix[7] = 0;
    //Column 3
    Matrix[8] = (2*x*z) + (2*a*y);
    Matrix[9] = (2*y*z) - (2*a*x);
    Matrix[10] = (a*a) - (x*x) - (y*y) + (z*z);
    Matrix[11] = 0;
    //Column 4
    Matrix[12] = 0;
    Matrix[13] = 0;
    Matrix[14] = 0;
    Matrix[15] = 1;
}

这里面可能有一些东西会让比我聪明的人感到畏缩,但我看不到。为了从欧拉角转换为四元数,我使用了“第一种方法”这个来源 http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm,这似乎也表明该方程自动创建一个单位四元数(“明显标准化”)。为了乘以四元数,我再次利用这个 C++ 指南 http://www.cprogramming.com/tutorial/3d/quaternions.html.

第 3 步 - 从四元数导出旋转矩阵

一旦完成,按照 R. Martinho Fernandes 的回答这个问题 https://stackoverflow.com/questions/7938373/from-quaternions-to-opengl-rotations,我尝试从四元数构建一个旋转矩阵,并使用它来更新对象的旋转,在下面的行中使用上面的 Quaternion::Rotation Matrix() 代码:

m_qRotation.RotationMatrix(m_RotationMatrix);

我应该注意到 m_RotationMatrix 是GLfloat m_RotationMatrix[16], 按照glMultMatrix 所需的参数 http://www.opengl.org/sdk/docs/man/xhtml/glMultMatrix.xml,我相信稍后在显示对象时应该使用它。它被初始化为:

m_RotationMatrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};

我相信这是“中性”OpenGL 旋转矩阵(每 4 个值一起代表一列,对吗?同样,我从glMultMatrix 页面 http://www.opengl.org/sdk/docs/man/xhtml/glMultMatrix.xml).

第 4 步 - 显示!

最后,我们得到了为应该显示它的对象每个周期运行的函数。

glPushMatrix();

glTranslatef(m_Position.x, m_Position.y, m_Position.z);
glMultMatrixf(m_RotationMatrix);

//glRotatef(m_Rotation.y, 0.0, 1.0, 0.0);
//glRotatef(m_Rotation.z, 0.0, 0.0, 1.0);
//glRotatef(m_Rotation.x, 1.0, 0.0, 0.0);

//glRotatef(m_qRotation.a, m_qRotation.x, m_qRotation.y, m_qRotation.z);

//[...] various code displaying the object's VBO

glPopMatrix();

我把之前失败的尝试留在那里,并注释掉。

结论 - 悲伤的熊猫

这就是玩家输入生命周期的结束,从摇篮到 OpenGL 管理的坟墓。

我显然不明白一些事情,因为我得到的行为不是我想要或期望的行为。但我对矩阵数学或四元数并不是特别有经验,所以我没有足够的洞察力来发现我的方法中的错误。

有人可以帮我吗?


您所做的就是用四元数有效地实现欧拉角。那没有帮助。

欧拉角的问题在于,当您计算矩阵时,每个角度都相对于它之前的矩阵的旋转。你想要的是获取一个对象的当前方向,并沿某个轴应用旋转,产生新的方向。

你不能用欧拉角做到这一点。您可以使用矩阵,也可以使用四元数(因为它们只是矩阵的旋转部分)。但你不能通过假装它们是欧拉角来做到这一点。

这是通过不存储角度来完成的all。相反,您只有一个代表对象当前方向的四元数。当您决定对其应用旋转(以某个轴的某个角度)时,您可以构造一个四元数来表示围绕该轴的某个角度的旋转。然后将该四元数与当前方向四元数右乘,生成新的当前方向。

当您渲染对象时,您使用当前方向作为...方向。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用四元数进行 OpenGL 旋转[重复] 的相关文章

  • Accept() 是线程安全的吗?

    我目前正在用 C 语言为我正在做的课程编写一个简单的网络服务器 我们的一项要求是实现一个线程池来使用 pthread 处理连接 我知道我将如何粗略地执行此操作 在主线程中调用accept并将文件描述符传递给freee线程 但是我的朋友建议了
  • 获取 std::variant 当前持有的 typeid(如 boost::variant type())

    我已经从 boost variant 迁移到 std variant 但遇到了障碍 我在 boost type 中使用了一个很好的函数 它可以让你获取当前持有的 typeid 看https www boost org doc libs 1
  • 使用API​​隐藏程序标题栏

    它可以使用 c 和 windows api 删除窗口控制台标题栏 如果是的话如何 请 这个简单的应用程序隐藏并显示其所在控制台的标题栏 它会立即将控制台标题更改为 guid 以查找窗口句柄 然后 它使用 ToggleTitleBar 使用找
  • C# 中四舍五入到偶数

    我没有看到 Math Round 的预期结果 return Math Round 99 96535789 2 MidpointRounding ToEven returning 99 97 据我了解 MidpointRounding ToE
  • 访问“if”语句之外的变量

    我怎样才能使insuranceCost以外可用if陈述 if this comboBox5 Text Third Party Fire and Theft double insuranceCost 1 在 if 语句之外定义它 double
  • 如何在不实例化一个类的情况下检查它是否继承了另一个类? [复制]

    这个问题在这里已经有答案了 假设我有一个如下所示的类 class Derived some inheritance stuff here 我想在我的代码中检查类似的内容 Derived is SomeType 但看起来像is运算符需要 De
  • Qt 计算和比较密码哈希

    目前正在 Qt 中为测验程序构建面向 Web 的身份验证服务 据我了解 在数据库中存储用户密码时 必须对其进行隐藏 以防落入坏人之手 流行的方法似乎是添加的过程Salt https en wikipedia org wiki Salt cr
  • 如何在 Asp.net Gridview 列中添加复选框单击事件

    我在 asp 中有一个 gridview 其中我添加了第一列作为复选框列 现在我想选择此列并获取该行的 id 值 但我不知道该怎么做 这是我的 Aspx 代码
  • Paradox 表 - Oledb 异常:外部表不是预期的格式

    我正在使用 Oledb 从 Paradox 表中读取一些数据 我遇到的问题是 当我将代码复制到控制台应用程序时 代码可以工作 但在 WinForms 中却不行 两者都以 x86 进行调试 我实际上只是复制代码 在 WinForms 应用程序
  • 有没有办法使用 i387 fsqrt 指令获得正确的舍入?

    有没有办法使用 i387 fsqrt 指令获得正确的舍入 除了改变精确模式在 x87 控制字中 我知道这是可能的 但这不是一个合理的解决方案 因为它存在令人讨厌的重入型问题 如果 sqrt 操作中断 精度模式将出错 我正在处理的问题如下 x
  • 方法“xxx”不能是事件的方法,因为该类派生的类已经定义了该方法

    我有一个代码 public class Layout UserControl protected void DisplayX DisplayClicked object sender DisplayEventArgs e CurrentDi
  • 我可以仅在少数情况下关闭模拟吗

    我有一个始终使用模拟的应用程序 但是 当用户以管理员身份登录时 一些操作需要他们写入服务器本身 现在 如果这些用户在实际服务器上没有权限 有些用户没有 则不会让他们写入 我想做的是关闭几个命令的模拟 有没有办法做这样的事情 using Ho
  • 更改 IdentityServer4 实体框架表名称

    我正在尝试更改由 IdentityServer4 的 PersistedGrantDb 和 ConfigurationDb 创建的默认表名称 并让实体框架生成正确的 SQL 例如 而不是使用实体IdentityServer4 EntityF
  • 在简单注入器中注册具有多个构造函数和字符串依赖项的类型

    我正在尝试弄清楚如何使用 Simple Injector 我在项目中使用了它 注册简单服务及其组件没有任何问题 但是 当组件具有两个以上实现接口的构造函数时 我想使用依赖注入器 public DAL IDAL private Logger
  • 从事务范围调用 WCF 服务方法

    我有这样的代码 using TransactionScope scope TransactionScopeFactory CreateTransactionScope some methodes calls for which scope
  • 不兼容的类型 - 是因为数组已经是指针吗?

    在下面的代码中 我创建一个基于书籍结构的对象 并让它保存多个 书籍 我设置的是一个数组 即定义 启动的对象 然而 每当我去测试我对指针的了解 实践有帮助 并尝试创建一个指向创建的对象的指针时 它都会给我错误 C Users Justin D
  • 使用 foreach 循环和 XmlNodeList C# 将新节点附加到节点列表

    目前我处理的是这样的XML类型 XML FILE http 20drive google com open id 0By5BxgNi9eGcRldxcEZNU0FDTzQ 参考XML文件 我想检查一个节点 如果找不到该节点 我必须将该节点附
  • 从 C 线程调用 Python 代码

    我对从 C 或 C 线程调用 Python 代码时如何确保线程安全感到非常困惑 The Python 文档 http docs python org c api init html non python created threads似乎是
  • 纯虚函数可能没有内联定义。为什么?

    纯虚函数是那些虚函数并且具有纯说明符 0 第 10 4 条第 2 款C 03 的内容告诉我们什么是抽象类 顺便说一句 如下 注意 函数声明不能 同时提供纯说明符和定义 尾注 示例 struct C virtual void f 0 ill
  • g++ C++0x 枚举类编译器警告

    我一直在将可怕的 C 类型安全伪枚举重构为新的 C 0x 类型安全枚举 因为它们是way更具可读性 不管怎样 我在导出的类中使用它们 所以我明确地将它们标记为导出 enum class attribute visibility defaul

随机推荐

  • Python 字典键中的空格

    我知道 Python 字典键中可以有空格 但这被认为是糟糕的编程吗 我在 PEP 中找不到任何与此相关的内容 编辑以澄清 在我正在做的一个项目中 我正在研究解析 Apache 的记分板输出的东西mod status 请参阅下面的示例输出 我
  • 在 ASP.NET MVC3 中正确使用 TempData?

    我有一个 ASP NET MVC3 应用程序 其中我的操作生成了一个 id 列表 我希望将其提供给后续 AJAX 请求 这样我就可以在后台运行一个很长的进程并对其进行轮询 id 列表是这个长时间运行的进程的必要输入 我不想将它们作为参数传递
  • python正则表达式中匹配unicode字符

    我已经阅读了 Stackoverflow 上的其他问题 但仍然没有更进一步 抱歉 如果这个问题已经得到解答 但我没有得到任何建议可以工作 gt gt gt import re gt gt gt m re match r by tag P
  • 具有 VCL 样式的默认按钮

    我对 Default True 的 TButtons 的样式感到困惑 问题是 至少对于某些样式 例如 Luna 最近聚焦的按钮突出显示为橙色 这与默认按钮使用的样式相同 因此 我担心用户可能会对哪个按钮是默认按钮感到困惑 或者至少会认为两个
  • 多次渲染一个组件 React.js

    这是一个简单计数器的代码 但是 当我渲染视图时 我没有得到任何输出 请告诉我代码有什么问题 按下按钮 计数器就会递增并呈现在屏幕上 var Title React createClass getInitialState function r
  • Java utils 类、静态方法与注入 utils 类

    你们如何创建 utils 类 有标准的方法吗 就像标题所说 你可以有这样的东西 public class Utils public static method1 public static method2 并通过调用来使用它Utils me
  • “未定义不是对象” this.state 未绑定

    我的反应本机组件 我通过创建React createClass 似乎没有约束力this关键字正确 导致我无法访问this state 这是我得到的错误 代码如下 我没有看到与网站上的示例有任何本质上的不同 所以我无法弄清楚我做错了什么 我怎
  • 如何在两列上指定 OrderBy 子句

    我们希望在 Seam EntityQuery 接口以及 JPA 模型中对 2 列进行排序 我们如何做到这一点 Entity public class A OrderBy should this be hardcoded here is it
  • 如何让JHC与android ndk合作?

    JHC 是一个 Haskell 编译器 它可移植生成 C 代码 然后调用编译器后端来生成可执行文件 我需要转储 JHC 运行时系统的头文件和库 以便 android ndk 可以使用它来编译生成的 C 代码 或者 我需要弄清楚如何将 and
  • iOS到Mac OS X【核心】蓝牙数据传输

    我的目标是在运行 iOS 的设备 to a 运行 Mac OS X 的设备 通过蓝牙 我知道我也许可以使用 CoreBluetooth 来实现此目的 但我不明白如何使用 因为我没有看到在iOS设备并将其作为可用服务进行广播运行 Mac OS
  • 在 dplyr mutate 或 summarize 中有效分配具有多个输出的函数

    我注意到这里有很多使用的例子dplyr mutate与返回多个输出的函数结合以创建多个列 例如 tmp lt mtcars gt group by cyl gt summarise min summary mpg 1 median summ
  • 在 R 中的值更改之前删除特定值的行

    我有一个如下所示的数据框 dat lt data frame Target c rep 01 times 8 rep 02 times 5 rep 03 times 4 targ2clicks c 1 1 1 1 0 0 0 1 1 0 0
  • 如何在 Java 中将 RGB 值添加到 setColor() 中?

    如何向我的 Java 添加 红 绿 蓝 值 例如 setColor 255 0 0 上下文看起来像这样 public void render BufferStrategy bs getBufferStrategy if bs null cr
  • 正则表达式可以匹配两个正则表达式之间的交集吗?

    给定几个正则表达式 我们能写出一个等于它们交集的正则表达式吗 例如 给定两个正则表达式c a z a z and a z aeiou t 它们的交集包含cat and cut甚至可能更多 我们如何为它们的交集编写正则表达式 Thanks 正
  • Symfony 2 如何从另一个包导入 LESS 文件

    LESS 具有 import 其他 LESS 文件的能力 这个问题旨在找到一种解决方案 从 Symfony 项目中的另一个 Bundle 导入 LESS 文件中的 LESS 文件 我正在开发一个 Symfony2 项目 使用 LESS 和
  • jquery ui 对话框作为确认

    我正在尝试使用 jquery 对话框复制 javascript 的 确认 框 这是我的代码 function customConfirm customMessage popUp html customMessage popUp dialog
  • 多语言网站 SEO:无需更改 URL 即可获得特定于语言的结果?

    我有一个有两种语言的网站 英语和瑞典语 我想要的是 如果有人用谷歌搜索瑞典的网站 它应该显示瑞典语的结果 也就是说 我希望瑞典的 Google google se 抓取该网站的瑞典语版本 对于任何其他地方 我希望抓取英文版本 我读了以下内容
  • 如何在 Vim 中打开之前打开的缓冲区?

    如何在 Vim 中打开之前打开的缓冲区 我有 4 个缓冲区 例如 buf1 到 buf4 目前我已经打开 buf1 然后打开 buf3 执行 b3 现在 如何返回 buf1 之前打开的缓冲区 而不执行 b1 Ctrl P will go t
  • 控制器方法中 requestBody 上的 Spring boot @Valid 不起作用

    我正在尝试验证由 Validated 注释的 RestController 中由 Valid 注释注释的简单请求正文 验证对请求中的原始变量 以下示例中的年龄 正常工作 但对 pojo 请求主体无效 Valid 注释对请求主体 Person
  • 使用四元数进行 OpenGL 旋转[重复]

    这个问题在这里已经有答案了 因此 我正在编写一个程序 其中对象以 spacesim 方式移动 以便学习如何在 3D 空间中平滑地移动对象 在对欧拉角进行了一番研究之后 它们似乎并不真正适合任意方向上的自由形式 3D 运动 因此我决定继续使用