如何将屏幕上的一个点取消投影到 vulkan 中的对象 space 坐标?

How to unproject a point on screen to object space coordinates in vulkan?

我需要能够使用 Vulkan 将屏幕像素取消投影到对象 space 中,但是我的数学运算出了问题。

这是今天的着色器供参考:

void main()
{
    //the depth of this pixel is between 0 and 1
    vec4 obj_space = vec4( float(gl_FragCoord.x)/ubo.screen_width, float(gl_FragCoord.y)/ubo.screen_height, gl_FragCoord.z, 1.0f);
    //this puts us in normalized device coordinates [-1,1 ] range
    obj_space.xy = ( obj_space.xy * 2.0f ) -1.0f;

    //this two lines will put is in object space coordinates
    //mvp_inverse is derived from this in the c++ side: 
    //glm::inverse(app.three_d_camera->get_projection_matrix() * app.three_d_camera->view_matrix * model);

    obj_space = ubo.mvp_inverse * obj_space;
    obj_space.xyz /= obj_space.w;


    //the resulting position here is wrong
    out_color = obj_space;
}

当我输出颜色的位置时,颜色是关闭的。我知道我可以简单地将对象 space 位置从顶点着色器传递到片段着色器,但我想了解为什么我的数学不起作用,它会帮助我理解 Vulkan 并可能学习一点数学我自己。

谢谢!

我不完全确定你的问题是什么,但让我们讨论一下潜在的问题。

记住,vulkan 剪辑 space 是:

  • 正 y = 向下,
  • 正 x = 正确,
  • 正 z = out,
  • 居中于屏幕中间。

此外,尽管 OpenGL 的 GLSL 文档说它在 vulkan 中居中于左下角 gl_FragCoord is centered at the top left corner

在这一步中:

obj_space.xy = ( obj_space.xy * 2.0f ) -1.0f;

obj_space 现在是:

  • 左 x : -1.0
  • 右 x : 1.0
  • 顶部 y = -1.0
  • 底部 y = 1.0
  • out z = 1.0
  • 后退 z = 0

我几乎完全确定你的意思不是你的对象 space 让 Y 在顶部是负数。 y 从上到下增加的原因是图像和纹理,它们在 CPU 上的排序方式相同,现在在 vulkan 中也是如此排序。

一些其他注意事项:

你声称你的逆是从 glm::inverse 推导出来的:

glm::inverse(app.three_d_camera->get_projection_matrix() * app.three_d_camera->view_matrix * model);

但是 GLM 使用 OpenGL 符号表示矩阵维度和惯用手性,除非您将其强制到正确的坐标系,否则它将假定右手正 Y 向上,z 负向外。您需要包含以下 #defines 才能正常工作(或实际更改您的计算以适应这一点)。

#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_FORCE_LEFT_HANDED

此外,您需要修改矩阵以说明负 Y 方向。这是我过去如何处理的示例(直接修改透视矩阵):

ubo.model = glm::translate(glm::mat4(1.0f), glm::vec3(pos_x,pos_y,pos_z));
ubo.model *= glm::rotate(glm::mat4(1.0f), time * glm::radians(0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
ubo.view = glm::lookAt(glm::vec3(0.0f, 0.0f, -10.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 100.0f);
ubo.proj[1][1] *= -1; // makes the y axis projected to the same as vulkans