从着色器中的深度缓冲区进行采样,返回0到1之间的值,如预期的那样。 给定相机的近平面和远平面平面,如何计算此时的真实z值,即距相机的距离?
答案 0 :(得分:53)
来自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));
}
[编辑]所以这里是解释(有2个错误,见下面的Christian的评论):
OpenGL透视矩阵如下所示:
当你将这个矩阵乘以同质点[x,y,z,1]时,它会给你:[不在乎,不在乎,Az + B,-z](用A和B矩阵中的2个大组件。)
OpenGl接下来进行透视划分:它将此向量除以其w组件。此操作不是在着色器中完成的(除了像shadowmapping这样的特殊情况),而是在硬件中完成;你无法控制它。 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;
}
答案 1 :(得分:9)
我知道这是一个陈旧的问题,但我发现自己在不同的场合不止一次回到这里,所以我想我会分享我的代码来进行正向和反向转换。
这是基于@ 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;
}
答案 2 :(得分:0)
当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);
}
(源)感谢github用户cassfalg: https://github.com/carla-simulator/carla/issues/2287