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

标签 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轴的正方向指向我们正在观看的窗口的顶部,因此偏航角的增加意味着相机的方向 vector 应该向左旋转,对吧?但在节目中,镜头转动并显示了场景的右侧部分。我不明白发生了什么。

如果我知道这里发生了什么,我想我就能理解为什么获取 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,因此我们可以将其视为前向 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

如果您查看包含俯仰和偏航的完整公式,您会发现它将等于:

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

这只是一个复合旋转,首先按正绕顺序绕 z 轴旋转 (1,0,0) 角度 pitch,然后将结果绕 y 轴旋转角度 偏航按负序排列。

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

  1. 默认方向为 (0,0,-1),但设置欧拉角后,pitch=0yaw=0 产生 (1,0,0) 观看方向,默认值为 yaw=-90。人们可以用一种更清晰、更直观的方式来表述它,这样零角度就会产生默认的前视方向。

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

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

关于c++ - opengl 中鼠标输入背后的数学和偏航/俯仰值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51341126/

相关文章:

c++ - OpenGL Superbible 第一个程序崩溃

opengl - 带有 Oculus Rift 的原生渲染插件

android - 是否可以在不返回应用程序的情况下裁剪从 Android 上的相机捕获的图像?

C++ 类 iostream 重载

java - 在 Windows 2008 上从 JNI 加载 dll 时出错

c++ - Opencv Segmentation Fault 使用 pthread 捕获帧 C++

c++ - ScubaDiv编程,输出时出现逻辑错误

C++,Qt5 : grabFrameBuffer results are wrong when using multisampling

android - 感兴趣区域

c# - ASP.Net 实时流式传输 IP 摄像机视频和音频