嗨,我正在尝试一些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轴计算的。通过使用我们的右手并将拇指指向轴的+方向,其他手指弯曲的方向就是该 Angular 正方向。

现在,假设我将鼠标移至右侧,而未更改yoffset。然后根据mouse_callback函数,因为xoffset为正,所以偏航值会增加。由于y轴的正方向指向我们正在观看的窗口的顶部,所以偏航的增加意味着相机的方向 vector 应该向左旋转,对不对?但是在程序中,相机转动并显示场景的右侧部分。我不明白发生了什么。

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

任何帮助将不胜感激。告诉我是否误解了任何数学概念或其他东西。提前致谢!

最佳答案


否。y轴的方向与此处无关。将pitch保留为0,给出的公式等于:

front.x = cos(glm::radians(yaw))
front.y = 0
front.z = sin(glm::radians(yaw));
因此,如果yaw为0,则结果为(1,0,0)(右)。如果将其增加到90度,则最终得到(0,0,1),它在右手坐标系中直接指向背面,因此您只是向右转。
您似乎以某种方式将其与正旋转方向相关联,当绕y旋转时,正旋转方向始终从z到x。但是这些公式并未实现绕y轴正旋转 Angular yaw,但实际上却旋转了-yaw:
由于系统设置为在 Angular 0处屈服+ x,因此我们可以将其视为正向 vector 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和yaw的完整公式,您会发现它等于:
front = R_y(-yaw) * R_z(pitch) * (1,0,0)^T
这只是一个整数旋转,首先以正缠绕顺序围绕z轴旋转 Angular pitch(1,0,0),然后以负顺序围绕y轴旋转 Angular yaw的结果。
我还认为您在这里引用的源代码的作者要么是a)着急,要么b)有点困惑这里的数学计算方式。我说这有两个原因:
  • 默认方向为(0,0,-1),但设置了欧拉角,以便pitch=0yaw=0导致(1,0,0)可视方向,默认为yaw=-90。可以用一种更简洁,更直观的方式来制定它,以便零 Angular 产生默认的前视方向。
  • 这里完全不需要lookAt的用法。它在内部执行的正交归一化只是浪费处理能力(尽管对于当今的标准来说这不是很大的能力,但是仍然如此)。将(0,1,0)用作向上 vector 将在极点附近变得非常不稳定,并且将pitch限制为[-89,89]只是防止这种情况发生的一种手段。实际上,在此导航模型中使摄像机直视上或下是没有错的(由于您仅沿2D平面移动,因此向前方向仍由yaw单独定义,即使直视上或下也是如此)。这种情况引起的万向节锁定也​​不重要,因为根本就没有第三次旋转。
    实际上,直接从两个旋转 Angular 和相机位置创建 View 矩阵要容易得多,并且完全避免了在90度或全90度附近的任何问题。
  • 09-06 02:22