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

发布于 2024-12-10 12:03:32 字数 435 浏览 5 评论 0原文

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

例如

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

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

GLSL 规范表示其范围是屏幕空间中的 [0,1],但没有提及它是否是线性的。

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

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

I read lots of information about getting depth with fragment shader.

such as

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

but I still don't know whether or not the gl_FragCoord.z is linear.

GLSL specification said its range is [0,1] in screen sapce without mentioning it's linear or not.

I think linearity it is vital since I will use the rendered model to match depth map from Kinect.

Then if it is not linear, how to linearlize it in the world space?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

独留℉清风醉 2024-12-17 12:03:32

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

gl_FragCoord.z 是否是线性的取决于投影矩阵。
对于正交投影,gl_FragCoord.z 是线性的,而对于透视投影,它不是线性的。

一般来说,深度(gl_FragCoord.zgl_FragDepth)计算如下(参见GLSL gl_FragCoord.z计算和设置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

)

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

正交投影

正交投影矩阵:

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)

正交 Z 函数

透视投影

透视投影中的投影矩阵描述了 3D 的映射从针孔相机看到的世界上的点到视口的 2D 点。
相机视锥体(截棱锥体)中的眼睛空间坐标被映射到立方体(标准化设备坐标)。

透视投影

透视投影矩阵:

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

在透视投影中,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]:

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)

的答案

另请参阅如何给定视图空间深度值和 ndc xy 恢复视图空间位置

but I still don't know whether or not the gl_FragCoord.z is linear.

Whether gl_FragCoord.z is linear or not depends on, the projection matrix.
While for Orthographic Projection gl_FragCoord.z is linear, for Perspective Projection it is not linear.

In general, the depth (gl_FragCoord.z and gl_FragDepth) is calculated as follows (see 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;

The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. It transforms from eye space to the clip space, and the coordinates in the clip space are transformed to the normalized device coordinates (NDC) by dividing with the w component of the clip coordinates

Orthographic Projection

At Orthographic Projection the coordinates in the eye space are linearly mapped to normalized device coordinates.

Orthographic Projection

Orthographic Projection Matrix:

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

At Orthographic Projection, the Z component is calculated by the linear function:

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

Orthographic Z function

Perspective Projection

At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport.
The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).

Perspective Projection

Perspective Projection Matrix:

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

At Perspective Projection, the Z component is calculated by the rational function:

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

Perspective Z function

Depth buffer

Since the normalized device coordinates are in range (-1,-1,-1) to (1,1,1) the Z-coordinate has to be mapped to the depth buffer range [0,1]:

depth = (z_ndc + 1) / 2 

Then if it is not linear, how to linearize it in the world space?

To convert form the depth of the depth buffer to the original Z-coordinate, the projection (Orthographic or Perspective), and the near plane and far plane has to be known.

Orthographic Projection

n = near, f = far

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

Perspective Projection

n = near, f = far

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

If the perspective projection matrix is known this can be done as follows:

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

See also the answer to

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

摇划花蜜的午后 2024-12-17 12:03:32

假设通常的透视投影矩阵,一旦执行透视除法(通过gl_Position.w)步骤,深度就会失去线性,因此gl_FragCoord.z不是线性的。如需更详细的解释,请阅读@Dreamer 的回答

要恢复为线性,您应该执行 2 个步骤:

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

您将获得视图空间(或相机空间,您可以命名的)中的一个点,其线性 z 介于 znear 和 zfar 之间。

Assuming a usual perspective projection matrix, once the perspective division (by gl_Position.w) step is performed the depth loses its linearity, so gl_FragCoord.z is not linear. For a more detailed explanation read @Dreamer's answer.

To revert back to linear you should perform 2 steps:

1) Transform the variable gl_FragCoord.z to normalized devices coordinates in the range [-1, 1]

z = gl_FragCoord.z * 2.0 - 1.0 

2) Apply the inverse of the projection matrix (IP). (You can use arbitrary values for x and y), and normalize for the last component.

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

you will obtain a point in view space (or camera space, you name it) that has a linear z between znear and zfar.

渔村楼浪 2024-12-17 12:03:32

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

因此,当变换矩阵为 gl_Position.w 分配常量值时,gl_FragCoord.z 是线性的(这通常发生在正交投影矩阵中)并且是当 gl_Position.w 取决于输入向量的 xyz 坐标时,非线性(这发生在透视投影矩阵)。

Whether gl_FragCoord.z is linear or not depends on your transformation matrix. gl_FragCoord.z is determined by computing gl_Position.z / gl_Position.w for all vertices of your triangle and then interpolating the result over all fragments of that triangle.

So gl_FragCoord.z is linear when your transformation matrix assigns a constant value to gl_Position.w (which usually happens with ortho projection matrices) and is non-linear when gl_Position.w depends on the x, y, or z coordinate of your input vector (which happens with perspective projection matrices).

橪书 2024-12-17 12:03:32

由您决定是否需要线性 Z,一切都取决于您的投影矩阵。您可以阅读以下内容:

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

这很好地解释了投影矩阵的工作原理。最好使用非线性 Z,以便在前景中获得更好的精度,在背景中获得更少的精度,当距离很远时,深度伪像不太明显......

Its up to you to decide if you want linear Z or not, everythings relies on your projection matrix. You may read this:

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

Which explains very well how projection matrices works. It may be better to have non-linear Z in order to have better precision in the foreground and less in the backgrounds, depth artifacts are less visible when far away...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文