在着色器中从深度缓冲区进行采样会返回期望的0到1之间的值。 鉴于相机的近裁剪面和远裁剪面,我如何计算该点的真实z值,即与相机的距离?
来源:http://web.archive.org/web/20130416194336/http://olivers.posterous.com/linear-depth-in-glsl-for-real
// == Post-process frag shader ===========================================
uniform sampler2D depthBuffTex;
uniform float zNear;
uniform float zFar;
varying vec2 vTexCoord;
void main(void)
{
float z_b = texture2D(depthBuffTex, vTexCoord).x;
float z_n = 2.0 * z_b - 1.0;
float z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
}
[编辑] 所以这是解释(有两个错误,见下面Christian的评论):
OpenGL透视矩阵如下所示:
当你将该矩阵乘以一个齐次点[x,y,z,1]时,它会给出:[不关心,不关心,Az + B,-z](其中A和B是矩阵中的两个大组件)。
然后,OpenGl进行透视除法:它将该向量除以其w分量。此操作不在着色器中执行(除了像阴影映射这样的特殊情况),而是在硬件中执行;您无法控制它。 w = -z,因此Z值变为-A/z -B。
现在,我们处于标准化设备坐标中。 Z值介于0和1之间。由于某种愚蠢的原因,OpenGL要求将其移动到[-1,1]范围内(就像x和y一样)。应用比例和偏移。
最终值随后存储在缓冲区中。
上述代码执行相反的操作:
相反的函数是:
varying float depth; // Linear depth, in world units
void main(void)
{
float A = gl_ProjectionMatrix[2].z;
float B = gl_ProjectionMatrix[3].z;
gl_FragDepth = 0.5*(-A*depth + B) / depth + 0.5;
}
Az+B
除以 -z
后,得到的结果应该是 -A-B/z
而不是 -A/z-B
。而且在透视矫正之后,值是在 [-1,1] 范围内,并且需要进行比例缩放和偏移来使其落在 [0,1] 的范围内,然后再写入深度缓冲区,而不是反过来(虽然您的代码做得对,但是解释是错误的)。 - Christian Rau我知道这是一个古老的问题,但我在各种场合多次回到这里,所以我想分享我的代码,可以进行前向和反向转换。
这基于@Calvin1602的答案。这些代码适用于GLSL或普通的C代码。
uniform float zNear = 0.1;
uniform float zFar = 500.0;
// depthSample from depthTexture.r, for instance
float linearDepth(float depthSample)
{
depthSample = 2.0 * depthSample - 1.0;
float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear));
return zLinear;
}
// result suitable for assigning to gl_FragDepth
float depthSample(float linearDepth)
{
float nonLinearDepth = (zFar + zNear - 2.0 * zNear * zFar / linearDepth) / (zFar - zNear);
nonLinearDepth = (nonLinearDepth + 1.0) / 2.0;
return nonLinearDepth;
}
当我遇到类似问题时,尝试解决并最终来到这里。在Nicol Bolas在本页面上的评论让我意识到我的错误。如果你想要得到相机距离而不是相机平面距离,可以按以下方式计算(使用GLSL):
float GetDistanceFromCamera(float depth,
vec2 screen_pixel,
vec2 resolution) {
float fov = ...
float near = ...
float far = ...
float distance_to_plane = near / (far - depth * (far - near)) * far;
vec2 center = resolution / 2.0f - 0.5;
float focal_length = (resolution.y / 2.0f) / tan(fov / 2.0f);
float diagonal = length(vec3(screen_pixel.x - center.x,
screen_pixel.y - center.y,
focal_length));
return distance_to_plane * (diagonal / focal_length);
}