Vulkan 深度缓冲位置重构
Vulkan depth buffer position reconstruction
我正在重建模板光着色器的位置。
这意味着我有光的球面近似顶点,从中我可以获得世界 space 视图向量 (vertexPos - cameraPos)。代码如下所示:
vec3 construct_pos_ws(float depthBufferZ)
{
vec3 viewRay = normalize(gInVert.vRayWs);
float viewZDist = dot(gInFrame.eyeZAxis, viewRay);
vec3 relPoint = viewRay * linearDepth(depthBufferZ) / viewZDist;
return gInFrame.eye + relPoint;
}
vRayWs : world space view ray (在vertexshader中插值vertexPos - cameraPos)
eyeZAxis : world space 眼睛的 z 轴(相机方向)
viewZDist :深度缓冲区保持与相机表面平行的距离,但我们需要一个距离,这说明了我们观察物体的角度。
问题出在我使用的线性化函数上,基于 wikipedia: z-buffering:
(我省略了 S = 2^d-1,这部分似乎不适用)。
float linearDepth(float z_b) {
float f = z_far;
float n = z_near;
return (-f * n / (z_b (f - n) - f));
}
这行不通,但使用 float n = z_near * 2
却给出了(看似)正确的结果(我只是随机尝试了一下,因为之前的答案看起来很接近正确)。
我也尝试过其他线性化,使用维基百科页面上所有树变体的逆(下面的 3),但 none 有效。
真题:
那么发生了什么?为什么乘以 2 似乎给出了正确答案? vulkan 使用什么公式写入深度缓冲区,因为它似乎没有使用维基百科上的任何公式?
最后,为了完整起见,投影矩阵的构造是这样的:
float yScale = 1.0f / tan(fov / 2.0f);
float xScale = yScale / aspect;
float nearmfar = z_near - z_far;
Matrix m = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, (z_far + z_near) / nearmfar, -1,
0, 0, 2 * z_far * z_near / nearmfar, 0
};
此外,我怎么知道它是正确的:
使用更改和渲染 "position mod 1" 给出第一张图片(移动相机时稳定),而原始函数给出第二张图片(移动相机时非常不稳定)。
Vulkan 使用 [0, 1] 的深度范围(实际上还翻转了 Y 轴),而不是 OpenGL 的 [-1, 1]。您有两种选择来获得类似 OpenGL 的行为:
- 修复着色器中的 NDC Z 以符合 OpenGL 方式,就像您目前所做的那样乘以二
- 或者用矩阵预乘你的投影矩阵
1.0 0.0 0.0 0.0
0.0 -1.0 0.0 0.0
0.0 0.0 0.5 0.5
0.0 0.0 0.0 1.0
看看 https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/ for more details. The Vulkan specification (https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/vkspec.html#vertexpostproc-clipping) 也有更多细节,虽然没有说清楚。
我正在重建模板光着色器的位置。 这意味着我有光的球面近似顶点,从中我可以获得世界 space 视图向量 (vertexPos - cameraPos)。代码如下所示:
vec3 construct_pos_ws(float depthBufferZ)
{
vec3 viewRay = normalize(gInVert.vRayWs);
float viewZDist = dot(gInFrame.eyeZAxis, viewRay);
vec3 relPoint = viewRay * linearDepth(depthBufferZ) / viewZDist;
return gInFrame.eye + relPoint;
}
vRayWs : world space view ray (在vertexshader中插值vertexPos - cameraPos)
eyeZAxis : world space 眼睛的 z 轴(相机方向)
viewZDist :深度缓冲区保持与相机表面平行的距离,但我们需要一个距离,这说明了我们观察物体的角度。
问题出在我使用的线性化函数上,基于 wikipedia: z-buffering: (我省略了 S = 2^d-1,这部分似乎不适用)。
float linearDepth(float z_b) {
float f = z_far;
float n = z_near;
return (-f * n / (z_b (f - n) - f));
}
这行不通,但使用 float n = z_near * 2
却给出了(看似)正确的结果(我只是随机尝试了一下,因为之前的答案看起来很接近正确)。
我也尝试过其他线性化,使用维基百科页面上所有树变体的逆(下面的 3),但 none 有效。
真题: 那么发生了什么?为什么乘以 2 似乎给出了正确答案? vulkan 使用什么公式写入深度缓冲区,因为它似乎没有使用维基百科上的任何公式?
最后,为了完整起见,投影矩阵的构造是这样的:
float yScale = 1.0f / tan(fov / 2.0f);
float xScale = yScale / aspect;
float nearmfar = z_near - z_far;
Matrix m = {
xScale, 0, 0, 0,
0, yScale, 0, 0,
0, 0, (z_far + z_near) / nearmfar, -1,
0, 0, 2 * z_far * z_near / nearmfar, 0
};
此外,我怎么知道它是正确的:
使用更改和渲染 "position mod 1" 给出第一张图片(移动相机时稳定),而原始函数给出第二张图片(移动相机时非常不稳定)。
Vulkan 使用 [0, 1] 的深度范围(实际上还翻转了 Y 轴),而不是 OpenGL 的 [-1, 1]。您有两种选择来获得类似 OpenGL 的行为:
- 修复着色器中的 NDC Z 以符合 OpenGL 方式,就像您目前所做的那样乘以二
- 或者用矩阵预乘你的投影矩阵
1.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.5 0.5 0.0 0.0 0.0 1.0
看看 https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/ for more details. The Vulkan specification (https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/vkspec.html#vertexpostproc-clipping) 也有更多细节,虽然没有说清楚。