题
我目前从场着色器的一个后处理深度的深度纹理使用以下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
值作为距离,但这似乎没有工作。这里有什么正确的做法?
此外,使用斜截锥削波到近平面移位到一个选择的剪切平面时是近平面距离现在对每个像素可能不同?如果是的话,这是否意味着,从一个深度纹理计算距离的任何着色器需要了解这种情况下,并不能假设恒定的近平面距离?
谢谢!
解决方案
原来,我忘了否定最后的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);
如果你想世界空间位置,而不是(像超强使用),那么,可以通过组合视图和投影矩阵,然后使用该矩阵的逆,而不是仅仅使用投影矩阵逆找到。
由于仅需要在Z和viewPosition
W的部件,用于计算viewPosition
上述GLSL可以稍微简化。两个点积就足够了,而不是一个完整的矩阵乘法,而且也没有必要全逆投影矩阵馈送到着色器需要为只有底部两行:
vec2 viewPositionZW = vec2(
dot(projectionMatrixInverseRow2, screenPos),
dot(projectionMatrixInverseRow3, screenPos)
);
float z = -(viewPositionZW.x / viewPositionZW.y);
这样做的性能比使用,因为额外的点积想必近及远的距离,稍微差一些,我得到了〜5%的减少。近和远距离的数学也可以通过在作为常量供给(zNear * zFar)
和(zFar - zNear)
优化,但是我没有通过这样做看到任何可测量的改善。
有趣的是,当你把上面与使用斜截锥削波,我不能得到任何合理的出它的投影矩阵,但我得到合理的输出使用具有相同投影矩阵近及远的距离公式时,尽管这似乎是深度值的一些失真(尽管这可能仅仅是由于深度精度在斜截锥削波固有的损失)。如果任何人都可以揭示究竟是怎么回事数学这里一些轻我会很感激的,但也许这应该是一个不同的问题。
其他提示
我使用下面的代码在闪电着色器,在为了计算闪电方向。 尔德位置也通过与投影矩阵的逆屏幕位置相乘来计算。
不幸的是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是正确!