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) 对这里的数学运算方式有点困惑。我这么说有两个原因:
默认方向为 (0,0,-1),但欧拉角设置为 pitch=0
、yaw=0
导致 (1, 0,0) 观察方向,默认为 yaw=-90
。人们可以用一种更清晰、更直观的方式来表达它,这样零角度就会产生默认的前视方向。
这里完全不需要lookAt
的用法。它将在内部进行的正交归一化只是对处理能力的浪费(按照今天的标准,这不是一个很大的浪费,但仍然如此)。使用 (0,1,0)
作为向上矢量会在两极附近变得非常不稳定,将 pitch
限制为 [-89,89]
只是防止这种情况发生的一种技巧。在这个导航模型中让相机直视向上或直视实际上没有错(因为你只沿着二维平面移动,前向仍然由 yaw
单独定义,即使直视或直视吃下)。这种情况引起的万向节锁定也无关紧要,因为后面根本没有第三次旋转。
直接从两个旋转角度和相机位置创建视图矩阵确实容易得多,并且完全避免了接近或完全 90 度的任何问题。
嗨,我正在尝试一些 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) 对这里的数学运算方式有点困惑。我这么说有两个原因:
默认方向为 (0,0,-1),但欧拉角设置为
pitch=0
、yaw=0
导致 (1, 0,0) 观察方向,默认为yaw=-90
。人们可以用一种更清晰、更直观的方式来表达它,这样零角度就会产生默认的前视方向。这里完全不需要
lookAt
的用法。它将在内部进行的正交归一化只是对处理能力的浪费(按照今天的标准,这不是一个很大的浪费,但仍然如此)。使用(0,1,0)
作为向上矢量会在两极附近变得非常不稳定,将pitch
限制为[-89,89]
只是防止这种情况发生的一种技巧。在这个导航模型中让相机直视向上或直视实际上没有错(因为你只沿着二维平面移动,前向仍然由yaw
单独定义,即使直视或直视吃下)。这种情况引起的万向节锁定也无关紧要,因为后面根本没有第三次旋转。直接从两个旋转角度和相机位置创建视图矩阵确实容易得多,并且完全避免了接近或完全 90 度的任何问题。