我正在使用以下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));
这一切似乎运作得相当好,但是我有兴趣知道如何仅根据当前投影矩阵进行上述计算,而不需要单独的zNear
和zFar
值。
我的初步尝试涉及将(vec4(tcscreen.x, tcScreen.y, depth, 1.0) * 2.0 - 1.0)
乘以投影矩阵的倒数,将结果除以w
,然后将得到的z
值作为距离,但这并没有'似乎工作。这里的正确方法是什么?
此外,当使用倾斜平截头体剪裁将近平面移动到选定的剪切平面时,近平面距离现在可能对于每个像素都不同吗?如果是这样,这是否意味着任何计算与深度纹理的距离的着色器需要知道这种情况并且不假设恒定的近平面距离?
谢谢!
答案 0 :(得分:2)
事实证明,我忘记了否定最终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)
作为常数提供,也可以优化近距离和远距离数学,但是我没有看到任何可测量的改进。
有趣的是,当你将上面的结果与使用倾斜平截头体剪裁的投影矩阵结合起来时,我无法从中得到任何明智的结果,但是当使用具有相同投影矩阵的近距离和远距离方程时,我确实获得了合理的输出虽然看起来有一些深度值的失真(尽管这可能仅仅是由于斜截头体剪切中固有的深度精度的损失)。如果有人能够在数学上对这里的确切内容有所了解,我会很感激,尽管这应该是一个不同的问题。
答案 1 :(得分:1)
我在闪电着色器中使用以下代码,以便计算闪电方向。 通过将屏幕位置与投影矩阵的倒数相乘来计算黄金位置。
不幸的是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;
工作正常,所以我认为Worldposition是正确的!