正确调用 glm::unproject() ,困惑
calling glm::unproject() correctly, confused
我在 x/z-plane 上尝试使用 glm::unproject() 将我的 SDL 鼠标坐标转换为世界位置向量。基本上我想弄清楚用户用鼠标点击了哪个 "x/z" 坐标。
从 other stack overflow answers 我开始需要调用 glm::unproject()。我认为我向它传递了错误的参数,因为我为世界位置返回的值(打印标准 std::cerr)不是我期望的世界位置值。
我是否在下面正确构造了 glm::unproject() 的参数?具体来说,我应该结合相机的世界位置和视图矩阵(使用 glm::lookAt 计算)来计算传递给 glm::unproject 的模型视图矩阵吗?
struct Dimensions {
int x, y, w, h;
};
glm::mat4
Camera::view_matrix() const
{
// VIEW matrix is created by looking at some target member
auto const& target = target_->translation;
auto const position_xyz = world_position();
glm::vec3 const UP{0, 1, 0};
return glm::lookAt(position_xyz, target, UP);
}
glm::mat4
Camera::projection_matrix() const
{
auto const fov = glm::radians(90.0f);
return glm::perspective(fov, 4.0f/3.0f, 0.1f, 200.0f);
}
glm::vec3
calculate_worldpos(Camera const& camera, int const mouse_x, int const mouse_y)
{
float const width = 1024.0f, height = 768.0f;
glm::vec4 const viewport = glm::vec4(0.0f, 0.0f, width, height);
glm::mat4 const modelview = camera.view_matrix();
glm::mat4 const projection = camera.projection_matrix();
float z = 0.0;
glm::vec3 screenPos = glm::vec3(mouse_x, height - mouse_y - 1, z);
std::cerr << "screenpos: xyz: '" << glm::to_string(screenPos) << "'\n";
glm::vec3 worldPos = glm::unProject(screenPos, modelview, projection, viewport);
std::cerr << "worldpos: xyz: '" << glm::to_string(worldPos) << "'\n";
return worldPos;
}
在下图中,我有以下设置。
相机注视目标 = (0, 0, 0)
相机世界位置 = (-0.009, 5.107, -0.368)
(mouse_x, mouse_y, mouse_z) = (286, 393, 0)
如果你看下图,你会看到我的鼠标悬停在世界位置 (3, 0, 0) 上,如网格所示。我希望计算我的鼠标的世界位置(如图所示)会 return 我的向量 (3, 0, 0)。它没有,而是我得到了向量:(0.049, 5.007, -0.360).
有人看到我可能哪里出错了吗?我假设我在某处做出了某种错误的假设。
您的假设是错误的:glm::unproject returns 输入的世界空间坐标由像素坐标中的 xy 位置和存储深度值的 z 坐标给出。在屏幕上的每个像素上,世界空间中有无数个点投射到该像素(所有点都位于从投影中心通过该像素的光线上)。你想要哪一个是通过选择深度坐标来识别的,而不是在这条射线上产生一个特定的点。选择 z = 0 意味着结果将始终是相机近平面上的一个点。
您实际上要寻找的是这条射线(通过相机位置和计算点)与 xz 平面(其中 y=0)的交点。
射线由其上的两个点(相机位置C,近平面点P)给出如下:
-0.009 0.058
C + l * (P-C) = ( 5.107 ) + l * ( -0.100 )
-0.368 0.008
,其中 l 是一个自由变量。
前面已经说过,我们要找的是y=0平面的交点(a,b),因此我们可以制定如下等式:
-0.009 0.058 a
( 5.107 ) + l * ( -0.100 ) = ( 0 )
-0.368 0.008 b
求解 l 的 y 方程 (5.107 + l * -0.1 = 0
) 得到 l = 51.07
。粘贴回 x 和 z 的方程式得到:
a = -0.009 + 51.07 * 0.058 = 2.95306
b = -0.368 + 51.07 * 0.008 = 0.04056
接近预期的世界空间位置。差异很可能是因为您只是在问题中显示了四舍五入的数字。出于准确性原因,我也不会在近平面上计算一个点,而是在远平面 (z=1) 上计算一个点,因为近平面距离通常很小并且可能导致数值问题。
结论:提供的所有值都是正确的,但您只是没有计算出预期的结果。
我在 x/z-plane 上尝试使用 glm::unproject() 将我的 SDL 鼠标坐标转换为世界位置向量。基本上我想弄清楚用户用鼠标点击了哪个 "x/z" 坐标。
从 other stack overflow answers 我开始需要调用 glm::unproject()。我认为我向它传递了错误的参数,因为我为世界位置返回的值(打印标准 std::cerr)不是我期望的世界位置值。
我是否在下面正确构造了 glm::unproject() 的参数?具体来说,我应该结合相机的世界位置和视图矩阵(使用 glm::lookAt 计算)来计算传递给 glm::unproject 的模型视图矩阵吗?
struct Dimensions {
int x, y, w, h;
};
glm::mat4
Camera::view_matrix() const
{
// VIEW matrix is created by looking at some target member
auto const& target = target_->translation;
auto const position_xyz = world_position();
glm::vec3 const UP{0, 1, 0};
return glm::lookAt(position_xyz, target, UP);
}
glm::mat4
Camera::projection_matrix() const
{
auto const fov = glm::radians(90.0f);
return glm::perspective(fov, 4.0f/3.0f, 0.1f, 200.0f);
}
glm::vec3
calculate_worldpos(Camera const& camera, int const mouse_x, int const mouse_y)
{
float const width = 1024.0f, height = 768.0f;
glm::vec4 const viewport = glm::vec4(0.0f, 0.0f, width, height);
glm::mat4 const modelview = camera.view_matrix();
glm::mat4 const projection = camera.projection_matrix();
float z = 0.0;
glm::vec3 screenPos = glm::vec3(mouse_x, height - mouse_y - 1, z);
std::cerr << "screenpos: xyz: '" << glm::to_string(screenPos) << "'\n";
glm::vec3 worldPos = glm::unProject(screenPos, modelview, projection, viewport);
std::cerr << "worldpos: xyz: '" << glm::to_string(worldPos) << "'\n";
return worldPos;
}
在下图中,我有以下设置。
相机注视目标 = (0, 0, 0)
相机世界位置 = (-0.009, 5.107, -0.368)
(mouse_x, mouse_y, mouse_z) = (286, 393, 0)
如果你看下图,你会看到我的鼠标悬停在世界位置 (3, 0, 0) 上,如网格所示。我希望计算我的鼠标的世界位置(如图所示)会 return 我的向量 (3, 0, 0)。它没有,而是我得到了向量:(0.049, 5.007, -0.360).
有人看到我可能哪里出错了吗?我假设我在某处做出了某种错误的假设。
您的假设是错误的:glm::unproject returns 输入的世界空间坐标由像素坐标中的 xy 位置和存储深度值的 z 坐标给出。在屏幕上的每个像素上,世界空间中有无数个点投射到该像素(所有点都位于从投影中心通过该像素的光线上)。你想要哪一个是通过选择深度坐标来识别的,而不是在这条射线上产生一个特定的点。选择 z = 0 意味着结果将始终是相机近平面上的一个点。
您实际上要寻找的是这条射线(通过相机位置和计算点)与 xz 平面(其中 y=0)的交点。
射线由其上的两个点(相机位置C,近平面点P)给出如下:
-0.009 0.058
C + l * (P-C) = ( 5.107 ) + l * ( -0.100 )
-0.368 0.008
,其中 l 是一个自由变量。
前面已经说过,我们要找的是y=0平面的交点(a,b),因此我们可以制定如下等式:
-0.009 0.058 a
( 5.107 ) + l * ( -0.100 ) = ( 0 )
-0.368 0.008 b
求解 l 的 y 方程 (5.107 + l * -0.1 = 0
) 得到 l = 51.07
。粘贴回 x 和 z 的方程式得到:
a = -0.009 + 51.07 * 0.058 = 2.95306
b = -0.368 + 51.07 * 0.008 = 0.04056
接近预期的世界空间位置。差异很可能是因为您只是在问题中显示了四舍五入的数字。出于准确性原因,我也不会在近平面上计算一个点,而是在远平面 (z=1) 上计算一个点,因为近平面距离通常很小并且可能导致数值问题。
结论:提供的所有值都是正确的,但您只是没有计算出预期的结果。