如何在片段着色器中使用gl_FragCoord.z在现代OpenGL中线性渲染深度?

时间:2011-10-15 12:51:05

标签: opengl glsl shader depth depth-buffer

我阅读了很多关于使用片段着色器获取深度的信息。

,例如

http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=234519

但我仍然不知道gl_FragCoord.z是否是线性的。

GLSL规范称屏幕上的范围为[0,1]而未提及线性与否。

我认为线性是至关重要的,因为我将使用渲染模型来匹配Kinect的深度图。

那么如果它不是线性的,如何在世界空间中线性化呢?

4 个答案:

答案 0 :(得分:20)

  

但我仍然不知道gl_FragCoord.z是否是线性的。

gl_FragCoord.z是否为线性取决于投影矩阵。 虽然对于正投影gl_FragCoord.z是线性的,但对于透视投影,它不是线性的。

一般来说,深度(gl_FragCoord.zgl_FragDepth)的计算方法如下(参见GLSL gl_FragCoord.z Calculation and Setting gl_FragDepth):

float ndc_depth = clip_space_pos.z / clip_space_pos.w;
float depth = (((farZ-nearZ) * ndc_depth) + nearZ + farZ) / 2.0;

投影矩阵描述了从场景的3D点到视口的2D点的映射。它从眼睛空间转换到剪辑空间,通过用剪辑坐标 w 组件划分,剪辑空间中的坐标转换为标准化设备坐标(NDC)

正投影

在Orthographic Projection中,眼睛空间中的坐标线性映射到标准化设备坐标。

Orthographic Projection

正交投影矩阵:

r = right, l = left, b = bottom, t = top, n = near, f = far 

2/(r-l)         0               0               0
0               2/(t-b)         0               0
0               0               -2/(f-n)        0
-(r+l)/(r-l)    -(t+b)/(t-b)    -(f+n)/(f-n)    1

在正交投影中,Z分量由线性函数计算:

z_ndc = z_eye * -2/(f-n) - (f+n)/(f-n)

Orthographic Z function

透视投影

在Perspective Projection中,投影矩阵描述了从针孔相机到视口的2D点看世界中3D点的映射。
相机平截头体(截头金字塔)中的眼睛空间坐标被映射到立方体(标准化设备坐标)。

Perspective Projection

透视投影矩阵:

r = right, l = left, b = bottom, t = top, n = near, f = far

2*n/(r-l)      0              0               0
0              2*n/(t-b)      0               0
(r+l)/(r-l)    (t+b)/(t-b)    -(f+n)/(f-n)    -1    
0              0              -2*f*n/(f-n)    0

在Perspective Projection中,Z分量由有理函数

计算
z_ndc = ( -z_eye * (f+n)/(f-n) - 2*f*n/(f-n) ) / -z_eye

Perspective Z function

深度缓冲区

由于归一化设备坐标在范围(-1,-1,-1)到(1,1,1)范围内,Z坐标必须映射到深度缓冲区,范围为[0,1]:< / p>

depth = (z_ndc + 1) / 2 


  

那么如果它不是线性的,如何在世界空间中线性化它?

要将深度缓冲区的深度转换为原始Z坐标,必须知道投影(正交或透视)以及近平面和远平面。

正交投影

n = near, f = far

z_eye = depth * (f-n) + n;

透视投影

n = near, f = far

z_ndc = 2.0 * depth - 1.0;
z_eye = 2.0 * n * f / (f + n - z_ndc * (f - n));

如果透视投影矩阵已知,则可以按如下方式完成:

A = prj_mat[2][2]
B = prj_mat[3][2]
z_eye = B / (A + z_ndc)

另见

的答案

How to recover view space position given view space depth value and ndc xy

答案 1 :(得分:8)

一旦预测它会失去线性,gl_FragCoord.z就不是线性的。

要恢复为线性,您应执行以下两个步骤:

1)将变量gl_FragCoord.z转换为[-1,1]范围内的规范化设备坐标

z = gl_FragCoord.z * 2.0 - 1.0 

2)应用投影矩阵(IP)的逆。 (您可以对x和y使用任意值),并对最后一个组件进行标准化。

unprojected = IP * vec4(0, 0, z, 1.0)
unprojected /= unprojected.w

您将获得在znear和zfar之间具有线性z的视图空间(或相机空间,您的名称)中的点。

答案 2 :(得分:2)

由您自己决定是否需要线性Z,每个因素都取决于您的投影矩阵。你可能会读到这个:

http://www.songho.ca/opengl/gl_projectionmatrix.html

这很好地解释了投影矩阵的工作原理。为了在前景中具有更好的精度而在背景中具有更少的精度,具有非线性Z可能更好,在远处时深度伪像不太可见......

答案 3 :(得分:2)

gl_FragCoord.z是否为线性取决于您的转换矩阵。 gl_FragCoord.z是通过为三角形的所有顶点计算gl_Position.z / gl_Position.w,然后在该三角形的所有片段上插值结果来确定的。

因此,当变换矩阵为gl_FragCoord.z(通常在 ortho 投影矩阵中发生)时,gl_Position.w是线性的,并且{{1}时是非线性的}取决于输入向量的gl_Position.wxy坐标(使用透视投影矩阵)。