嗨,我正在尝试一些 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)只是对这里的数学计算方式有点困惑。我这么说有两个原因:
默认方向为 (0,0,-1),但设置欧拉角后,
pitch=0
、yaw=0
产生 (1,0,0) 观看方向,默认值为yaw=-90
。人们可以用一种更清晰、更直观的方式来表述它,这样零角度就会产生默认的前视方向。这里完全没有必要使用
lookAt
。它在内部进行的正交归一化只是浪费处理能力(按照今天的标准,这不是一个很大的浪费,但尽管如此)。使用(0,1,0)
作为向上 vector 在极点附近会变得非常不稳定,并且将pitch
限制为[-89,89]
只是一个防止这种情况发生的 hack。实际上,在这个导航模型中让相机向上或向下看并没有什么问题(因为您只沿着 2D 平面移动,所以即使在看时,前进方向仍然可以单独由yaw
明确定义)直接向上或向下)。由这种情况引起的万向节锁定也不相关,因为根本没有第三次旋转。直接从两个旋转角度和相机位置创建 View 矩阵确实要容易得多,并且完全避免接近或完全 90 度的任何问题。
关于c++ - opengl 中鼠标输入背后的数学和偏航/俯仰值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51341126/