Pil*_*pel 9 opengl directx 3d graphics quaternions
我尝试基于四元数学实现FPS相机.我存储一个旋转四元数变量,_quat
并在需要时将其乘以另一个四元数.这是一些代码:
void Camera::SetOrientation(float rightAngle, float upAngle)//in degrees
{
glm::quat q = glm::angleAxis(glm::radians(-upAngle), glm::vec3(1,0,0));
q*= glm::angleAxis(glm::radians(rightAngle), glm::vec3(0,1,0));
_quat = q;
}
void Camera::OffsetOrientation(float rightAngle, float upAngle)//in degrees
{
glm::quat q = glm::angleAxis(glm::radians(-upAngle), glm::vec3(1,0,0));
q*= glm::angleAxis(glm::radians(rightAngle), glm::vec3(0,1,0));
_quat *= q;
}
Run Code Online (Sandbox Code Playgroud)
应用程序可以通过GetOrientation
简单地将四元数转换为矩阵来请求方向矩阵.
glm::mat4 Camera::GetOrientation() const
{
return glm::mat4_cast(_quat);
}
Run Code Online (Sandbox Code Playgroud)
应用程序按以下方式更改方向:
int diffX = ...;//some computations based on mouse movement
int diffY = ...;
camera.OffsetOrientation(g_mouseSensitivity * diffX, g_mouseSensitivity * diffY);
Run Code Online (Sandbox Code Playgroud)
这会在几乎所有轴周围产生不良的混合旋转.我究竟做错了什么?
正如GuyRT已经指出的那样,你积累的方式并不好.从理论上讲,它会以这种方式运作.但是,浮点数学远非完全精确,并且错误会累积更多的操作.组合两个四元数旋转是28次操作而不是单个操作将值添加到角度(此外,四元数乘法中的每个操作都会以非常明显的方式影响3D空间中的结果旋转).
此外,用于旋转的四元数对于归一化是相当敏感的,并且旋转它们会使它们稍微去标准化(旋转它们很多次将它们去标准化很多,并且用另一个旋转它们,已经去标准化的四元数放大了效果).
为什么我们首先使用四元数?
四元数通常用于以下原因:
这些都不会对你的问题产生影响.
将两个旋转累积为角度(通常不合需要,但在这种情况下完全可以接受),并在需要时创建旋转矩阵.这可以通过组合两个四元数并按照GuyRT的答案转换为矩阵来完成,或者通过直接生成旋转矩阵(这可能更有效,并且OpenGL想要看到的只是一个矩阵).
据我所知,glm::rotate
只有旋转任意轴.你可以,当然使用(但你宁愿合并两个四元!).幸运的是,矩阵的公式结合了围绕x,然后y,然后z的旋转是众所周知且直截了当的,例如在这里的(3)的第二段中找到它.
你不希望绕Z ^,所以cos(gamma) = 1
和sin(gamma) = 0
,从而大大简化公式(写出来的一张纸).
使用旋转角度会让很多人对你大喊大叫(通常不完全不应该).
一个更清晰的选择是跟踪您所看到的方向,或者使用从您的眼睛指向您想要观察的方向的矢量,或者通过记住您在空间中看到的点(这与物理学中的物理结合得非常好).也是第三人称游戏).如果你想允许任意旋转,那还需要一个"向上"向量 - 因为那时"向上"并不总是"向上"的世界空间 - 所以你可能需要两个向量.这更好,更灵活,但也更复杂.
对于您的示例中所需的FPS,您唯一的选择是左右和上下,我发现旋转角度 - 仅适用于相机 - 完全可以接受.
问题是您累积旋转的方式。无论使用四元数还是矩阵,这都是相同的。将代表俯仰和偏航的旋转与另一个旋转相结合将引入侧滚。
到目前为止,实现FPS摄像机最简单的方法是简单地累积航向和俯仰的变化,然后在需要时转换为四分之一(或矩阵)。我会将您的相机类中的方法更改为:
void Camera::SetOrientation(float rightAngle, float upAngle)//in degrees
{
_rightAngle = rightAngle;
_upAngle = upAngle;
}
void Camera::OffsetOrientation(float rightAngle, float upAngle)//in degrees
{
_rightAngle += rightAngle;
_upAngle += upAngle;
}
glm::mat4 Camera::GetOrientation() const
{
glm::quat q = glm::angleAxis(glm::radians(-_upAngle), glm::vec3(1,0,0));
q*= glm::angleAxis(glm::radians(_rightAngle), glm::vec3(0,1,0));
return glm::mat4_cast(q);
}
Run Code Online (Sandbox Code Playgroud)