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

时间:2018-07-14 16:05:49

标签: c++ opengl camera rotation euler-angles

嗨,我正在尝试一些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值的原因。

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

1 个答案:

答案 0 :(得分:1)

  

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

不。 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轴正旋转角度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轴旋转角度pitch(1,0,0),然后将y轴绕结果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平面移动,因此,即使{@ 1}下)。这种情况引起的万向节锁定也​​无关紧要,因为根本没有跟随第三次旋转。

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