opengl中鼠标输入背后的数学和偏航/俯仰值

Math behind the mouse input in opengl and the yaw/pitch values

本文关键字:鼠标 输入 背后 opengl      更新时间:2023-10-16

嗨,我正在尝试一些c++opengl代码,并制作了一台相机。我想给场景一个鼠标输入来四处查看,所以我添加了这个代码,就像这里的教程一样。https://learnopengl.com/Getting-started/Camera

然而,关于偏航和俯仰值,我不理解一些数学概念。这是鼠标移动的回调函数。

void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
if (firstMouse) //preventing large jumps
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;
glm::vec3 front;
front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(pitch));
front.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
//cameraFront is the direction vector of the camera. Where the camera is looking at
cameraFront = glm::normalize(front);
}

以下是全局变量及其初始值,用于mouse_callback函数以防万一。

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
//lookAt function requires position, target's position, up vector respectively.

有两件事我不明白。据我所知,偏航和俯仰分别是从y轴和x轴计算得出的。用我们的右手,把拇指朝着轴的+方向,其他手指弯曲的方向就是角度的正方向。

现在假设我在不改变yoffset的情况下将鼠标向右移动。然后,根据mouse_callback函数,由于xoffset为正,因此偏航值会增加。由于y轴的正方向指向我们正在观看的窗口的顶部,因此偏航的增加意味着相机的方向矢量应该向左旋转?但在节目中,镜头转向并显示了场景的右侧部分。我不明白发生了什么。

如果我知道这里发生了什么,我想我可以理解为什么获得yoffset的计算顺序与获得xoffset值不同。

如有任何帮助,我们将不胜感激。如果我弄错了任何数学概念或其他什么,请告诉我。提前感谢!

由于y轴的正方向指向我们正在观看的窗口的顶部,偏航的增加意味着相机的方向矢量应该向左旋转?

否。y轴的方向与此处的任何内容都无关。当pitch为0时,给出的公式等于:

front.x = cos(glm::radians(yaw))
front.y = 0
front.z = sin(glm::radians(yaw));

因此,如果偏航为0,则会得到(1,0,0)(右(。如果你把它增加到90度,你会得到(0,0,1),它在右手坐标系中直接指向后面,所以你就向右转了。

你似乎以某种方式将其与正旋转方向联系起来,当绕y旋转时,正旋转方向总是从z到x。但这些公式并没有实现绕y轴的正旋转角度yaw,但它们实际上旋转了-yaw:由于系统设置为在角度0处产生+x,我们可以将其视为正向矢量v= (1,0,0)绕轴y的旋转,因此经典旋转矩阵将产生:

(  cos(yaw)      0     sin(yaw) )           ( cos(yaw) )
v' =  (     0          1        0     ) * v   =   (     0    )
(  -sin(yaw)     0     cos(yaw) )           (-sin(yaw) )

然而,当你以负旋转方向旋转时,你最终得到的是转置矩阵,只是翻转sin:的减号

(  cos(yaw)      0    -sin(yaw) )           ( cos(yaw) )
v' =  (     0          1        0     ) * v   =   (     0    )
(  sin(yaw)      0     cos(yaw) )           ( sin(yaw) )

所以它只是

front = R_y ( -yaw) * (1,0,0)^T

如果你看看pitch和偏航的完整公式,你会注意到它将等于:

front = R_y(-yaw) * R_z(pitch) * (1,0,0)^T

这只是一个复数旋转,首先以正缠绕顺序绕z轴旋转(1,0,0(角度pitch,然后以负缠绕顺序绕y轴旋转角度yaw

我还认为,你在这里引用的源代码的作者要么a(很匆忙,要么b(只是对这里的数学运算有点困惑。我这么说有两个原因:

  1. 默认方向为(0,0,-1(,但欧拉角设置为pitch=0yaw=0产生(1,0,0(观察方向,默认为yaw=-90。人们本可以用一种更干净、更直观的方式来表述它,这样零角度就可以产生默认的前瞻性方向。

  2. 这里完全不需要使用lookAt。它将在内部进行正交归一化只是浪费处理能力(以今天的标准来看,这不是一个很大的问题,但尽管如此(。使用(0,1,0)作为上矢量在极点附近会变得非常不稳定,将pitch限制为[-89,89]只是防止这种情况发生的一种方法。实际上,在这个导航模型中,让相机直视上下并没有错(因为你只沿着2D平面移动,所以即使直视上下,前进方向仍然由yaw单独定义(。这种情况引起的万向节锁定也不相关,因为接下来根本没有第三次旋转。

    实际上,直接从两个旋转角度和相机位置创建视图矩阵要容易得多,并且完全避免在接近或完全90度时出现任何问题。