opengl 中鼠标输入背后的数学和 yaw/pitch 值

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

嗨,我正在尝试一些 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轴的正方向指向我们正在观看的window的顶部,yaw的增加意味着相机的方向向量应该向左旋转对吗?但在节目中,镜头转向并显示了场景的右侧部分。我不明白发生了什么。

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

如有任何帮助,我们将不胜感激。如果我误解了任何数学概念或其他内容,请告诉我。提前致谢!

Since the positive direction of the y axis points to the top of the window we're watching, the increase in yaw means that the direction vector of the camera should rotate to the left direction right?

没有。 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

这只是一个复合旋转,首先将(1,0,0)按正向顺序绕z轴旋转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] 只是防止这种情况发生的一种技巧。在这个导航模型中让相机直视向上或直视实际上没有错(因为你只沿着二维平面移动,前向仍然由 yaw 单独定义,即使直视或直视吃下)。这种情况引起的万向节锁定也​​无关紧要,因为后面根本没有第三次旋转。

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