将深度纹理采样转换为距离

6

我目前正在使用以下GLSL代码,在后处理景深着色器中从深度纹理中读取:

vec4 depthSample = texture2D(sDepthTexture, tcScreen);
float depth = depthSample.x * 255.0 / 256.0 +
              depthSample.y * 255.0 / 65536.0 +
              depthSample.z * 255.0 / 16777216.0;

然后将深度值转换为视图空间距离,基于近和远平面的距离:

float zDistance = (zNear * zFar) / (zFar - depth * (zFar - zNear));

这一切似乎都运行得很好,但是我想知道如何仅基于当前投影矩阵进行上述计算,而不需要单独的zNearzFar值。

我的初步尝试涉及将(vec4(tcscreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0)乘以投影矩阵的逆矩阵,通过w除以结果,然后将生成的z值作为距离,但这似乎不起作用。这里的正确方法是什么?

此外,当使用斜截体裁剪将近平面移动到所选裁剪平面时,每个像素的近平面距离现在是否可能不同?如果是,那么这是否意味着任何从深度纹理计算距离的着色器都需要了解这种情况,并且不能假设一个恒定的近平面距离?

谢谢!


3
我其实遇到了相反的问题,所以你的问题陈述最终成为了我的解决方案。谢谢! - geometrian
2个回答

4

事实证明,我忘记了对最终的Z值取反以获得近平面前面的正距离(OpenGL相机朝下看-Z)。供参考,获取近平面前面距离的GLSL代码如下:

float depth = /* sampled from depth texture as in the original question */ ;

vec4 screenPos = vec4(tcScreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0;
vec4 viewPosition = projectionMatrixInverse * screenPos;

float z = -(viewPosition.z / viewPosition.w);

如果你需要世界空间位置(就像SuperPro在使用的那样),那么可以通过组合视图矩阵和投影矩阵,然后使用该矩阵的逆矩阵而不仅仅是使用投影矩阵的逆矩阵来找到。
因为只需要viewPosition的Z和W分量,所以上面用于计算viewPosition的GLSL可以简化。两个点积就足够了,而且没有必要将完整的逆投影矩阵馈送到着色器中,因为只需要底部两行即可。
vec2 viewPositionZW = vec2(
    dot(projectionMatrixInverseRow2, screenPos),
    dot(projectionMatrixInverseRow3, screenPos)
);

float z = -(viewPositionZW.x / viewPositionZW.y);

这种方法的性能略逊于使用近距离和远距离的方法,可能是因为有额外的点积计算,我得到了约5%的减少。通过将 (zNear * zFar)(zFar - zNear) 作为常量输入,也可以优化近距离和远距离的计算,但我并没有看到任何可衡量的改进。
有趣的是,当你将上述内容与使用斜截面裁剪的投影矩阵相结合时,我无法得到任何合理的结果,但是当使用近距离和远距离公式与相同的投影矩阵一起使用时,我得到了合理的输出,尽管似乎存在一些深度值的扭曲(虽然这可能只是由斜截面裁剪中固有的深度精度损失所致)。如果有人能够在数学上解释这里到底发生了什么,我将不胜感激,不过或许这应该是另一个问题的内容。

2

我在闪电着色器中使用以下代码来计算闪电的方向。通过将屏幕位置乘以投影矩阵的逆矩阵,也可以计算世界位置。

不幸的是,HLSL:

float depth = tex2D(DepthMapSampler, PSIn.TexCoord).r;

float4 screenPos;
screenPos.x = PSIn.TexCoord.x*2.0f-1.0f;
screenPos.y = -(PSIn.TexCoord.y*2.0f-1.0f);
screenPos.z = depth;
screenPos.w = 1.0f; 

float4 worldPos = mul(screenPos, xViewProjectionInv);
worldPos /= worldPos.w;

运行良好,所以我认为世界位置是正确的!


谢谢确认我走在正确的道路上 :-),我已经在下面张贴了我的解决方案和GLSL代码。 - Richard Viney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接