GLSL gl_FragCoord.z计算和设置gl_FragDepth

26

所以,我有一个冒充者(真正的几何体是一个立方体,可能被剪裁了,而冒充者的几何形状是一个门格海绵),我需要计算它的深度。

我可以相当容易地计算在世界空间中的偏移量。不幸的是,我已经花了数小时尝试用它来扰动深度,但没有成功。

唯一可以得到正确结果的情况是当我执行以下操作时:

gl_FragDepth = gl_FragCoord.z

我需要知道如何计算gl_FragCoord.z,以便我可以:

  • 从gl_FragCoord.z逆变换到眼空间
  • 添加深度扰动
  • 将这个扰动的深度转换回与原始gl_FragCoord.z相同的空间。

如果这看起来像是一个重复的问题,我很抱歉;这里有一些其他帖子涉及类似的事情。然而,在实现所有这些方法之后,没有一个能正常工作。现在,我请求提供完整的代码以解决这个问题。它应该只是几行代码。


你写了顶点着色器吗?还是只写了片段着色器? - Michael Slade
2
基于一般原则,我不会给你代码,但我可以给你这个OpenGL Wiki链接。还有我的一个教程链接,关于impostors和depth的内容,展示了如何做到这一点。将深度转换回来是微不足道的。 - Nicol Bolas
Michael:是的,但这只是一个通过着色器。恰好计算是在世界空间中完成的,因此我可以在片段程序中计算眼睛空间。Nicol,我已经看过那个页面了。我将其实现为: vec4 clip_pos = gl_ProjectionMatrix * vec4(eye_pos,1.0); float ndc_depth = clip_pos.z / clip_pos.w; gl_FragDepth = (((clip_far-clip_near) * ndc_depth) + clip_near + clip_far) / 2.0; 不幸的是,深度似乎超出了深度范围,即使没有偏移。谢谢。 - geometrian
1
@IanMallett:clip_nearclip_far 是什么?因为这些听起来非常像用于计算透视投影矩阵的近裁剪面和远裁剪面距离。 - Nicol Bolas
他们是这样的吗?我尝试了使用gl_DepthRange和运用了示例,但是初步看来值在我的显卡上似乎不正确。 - geometrian
没事了,我只是个傻瓜。gl_DepthRange默认为0.0到1.0。我将变量设置为这个值,然后它就可以工作了。谢谢! - geometrian
2个回答

37

供将来参考,关键代码为:

float far=gl_DepthRange.far; float near=gl_DepthRange.near;

vec4 eye_space_pos = gl_ModelViewMatrix * /*something*/
vec4 clip_space_pos = gl_ProjectionMatrix * eye_space_pos;

float ndc_depth = clip_space_pos.z / clip_space_pos.w;

float depth = (((far-near) * ndc_depth) + near + far) / 2.0;
gl_FragDepth = depth;

“gl_DepthRange.far”和“gl_DepthRange.near”不应该被替换为剪裁平面的值,而应该用投影矩阵的近和远范围来代替。 - Tara
@Dudeson 剪裁面仅通过投影矩阵确定将映射到 NDC 之外的哪些值(并因此被硬件剪裁)。在指定投影矩阵后,我们就已经完成了它们。同时,gl_DepthRange.(ne|f)ar 是深度缓冲区的范围,并且需要用它来从 NDC 转换为深度缓冲区。希望有所帮助。 - geometrian
没错。很抱歉,我并没有意识到你正在将NDC转换为深度缓冲区(在这种情况下通常使用(ndc.z + 1.0) * 0.5即可)。 我以为你正在将线性深度值转换为深度缓冲区。 - Tara
@Dudeson 注意,对于通常的gl_DepthRange值,源代码中倒数第二行正是这个值 :) - geometrian
这正是我想表达的。(ndc.z + 1.0) * 0.5 计算速度更快。 - Tara
显示剩余2条评论

7

作为将来参考,这是与我在OpenGL 4.0应用程序中使用的imallett提供的相同公式:

vec4 v_clip_coord = modelview_projection * vec4(v_position, 1.0);
float f_ndc_depth = v_clip_coord.z / v_clip_coord.w;
gl_FragDepth = (1.0 - 0.0) * 0.5 * f_ndc_depth + (1.0 + 0.0) * 0.5;

在这里,modelview_projection是4x4模型视图投影矩阵,v_position是正在渲染的像素的对象空间位置(在我的情况下由光线跟踪器计算)。
该方程来自于此手册窗口坐标部分。请注意,在我的代码中,近截面是0.0,远截面是1.0,这是gl_DepthRange的默认值。请注意,gl_DepthRange透视投影矩阵公式中的近/远距离不是相同的东西!唯一的诀窍是使用0.01.0(或实际需要更改它时使用gl_DepthRange),我一直在尝试使用其他深度范围进行折腾——但那已经被“烘焙”在我的(透视)投影矩阵中了。
请注意,通过这种方式,该方程确实只包含一个常数乘法((far - near) / 2)和另一个常数的加法((far + near) / 2)。与imallett的代码中所需的乘法、加法和除法(可能由优化编译器转换为乘法)相比较。

感谢提供有用的信息。请注意,您的公式(1.0 - 0.0) * 0.5 * f_ndc_depth + (1.0 + 0.0) * 0.5在因式分解后和其他人在此处评论中分享的(f_ndc_depth + 1.0) * 0.5是相同的。 - R. Navega

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接