本文主要介绍将屏幕上的二维坐标转换为三维空间中的一个点,该点位于 近 平面上(即 Z 坐标为 -1)。
一、步骤概述
- 屏幕坐标到标准化设备坐标 (NDC): 将屏幕坐标 (x, y) 转换为 NDC 坐标系。
- NDC 到相机空间: 使用逆投影矩阵将 NDC 坐标转换到相机空间。
- 相机空间到世界空间: 使用逆视图矩阵将相机空间坐标转换到世界空间。
二、详细步骤
1. 屏幕坐标到标准化设备坐标 (NDC)
首先,假设 screenX, screenY 是屏幕空间中的像素坐标,screenW, screenH 是屏幕的宽度和高度。我们可以将屏幕坐标转换为标准化设备坐标:
float ndc_x = (2.0f * screenX) / screenW - 1.0f; // 将x坐标映射到 [-1, 1]
float ndc_y = 1.0f - (2.0f * screenY) / screenH; // 将y坐标映射到 [-1, 1],y轴翻转
screenX
,screenY
: 屏幕空间坐标screenW
,screenH
: 屏幕的宽度和高度ndc_x
,ndc_y
: 转换后的 NDC 坐标,范围为 [-1, 1]
2. 从 NDC 到相机空间
假设 z
值为 -1,即该点在近平面上的位置。我们需要通过逆投影矩阵将 NDC 坐标转换到相机空间。
逆投影矩阵是投影矩阵的反向操作。如果我们有一个标准的投影矩阵 P
,你可以用它的逆矩阵 P^-1
来将 NDC 坐标恢复到相机空间。假设 z
值为 -1,NDC 坐标为 (ndc_x, ndc_y)
,那么可以通过以下方式得到相机空间中的坐标:
// 2. Crop spatial coordinates
glm::vec4 clip_coords(ndc_x, ndc_y, -1.0f, 1.0f);// z = -1 => near-plane
// 3. View space coordinates
glm::mat4 P_inv = glm::inverse(projectionMatrix);// to turn clip-space to view-space
glm::vec4 view_coords = P_inv * clip_coords;
得到的 view_coords
是相机空间中的坐标,通常需要进行透视除法,即将 x
, y
, z
坐标通过 w 分量进行归一化:
view_coords /= view_coords.w;// convert to non-homogeneous coordinates, get real view-space coordinates
3. 从相机空间到世界空间
最后,使用逆视图矩阵将相机空间坐标转换为世界空间坐标。V_inv
是视图矩阵的逆矩阵,转换公式如下:
glm::mat4 V_inv = glm::inverse(viewMatrix);// to turn view-space to world-spaceglm::vec3 world_coords = glm::vec3(V_inv * view_coords);
最终代码实现如下:
glm::vec3 mapTo3DXZPlane(int screenX, int screenY, int screenW, int screenH, const glm::mat4& viewMatrix, const glm::mat4& projectionMatrix) {// 1. Normalized Device Coordinates (NDC:[-1,1], bottom-left=(-1,-1), top-right=(1,1))float ndc_x = (2.0f * screenX) / screenW - 1.0f;// Screen[0,screenW] map to NDC[-1,1]float ndc_y = 1.0f - (2.0f * screenY) / screenH;// Screen[0,screenH] map to NDC[1,-1] (openGL Y is up, screen Y is down)// 2. Crop spatial coordinatesglm::vec4 clip_coords(ndc_x, ndc_y, -1.0f, 1.0f);// z = -1 => near-plane// 3. View space coordinatesglm::mat4 P_inv = glm::inverse(projectionMatrix);// to turn clip-space to view-spaceglm::vec4 view_coords = P_inv * clip_coords;view_coords /= view_coords.w;// convert to non-homogeneous coordinates, get real view-space coordinates// 4. World spatial coordinatesglm::mat4 V_inv = glm::inverse(viewMatrix);// to turn view-space to world-spaceglm::vec3 world_coords = glm::vec3(V_inv * view_coords);return world_coords;
}