【问题标题】:Bind pre rendered depth texture to fbo or to fragment shader?将预渲染深度纹理绑定到 fbo 或片段着色器?
【发布时间】:2013-08-16 16:23:59
【问题描述】:

在延迟着色框架中,我使用不同的帧缓冲区对象来执行各种渲染通道。在第一遍中,我将整个场景的DEPTH_STENCIL_ATTACHMENT 写入纹理,我们称之为DepthStencilTexture。 要从不同的渲染通道访问存储在DepthStencilTexture 中的深度信息,为此我使用不同的帧缓冲区对象,我知道两种方法:
1)我将DepthStencilTexture绑定到着色器并在片段着色器中访问它,在这里我手动进行深度,像这样

uniform vec2 WinSize; //windows dimensions
vec2 uv=gl_FragCoord.st/WinSize;
float depth=texture(DepthStencilTexture ,uv).r;
if(gl_FragCoord.z>depth) discard;

我也设置了glDisable(GL_DEPTH_TEST)glDepthMask(GL_FALSE)

2) 我将DepthStencilTexture 绑定到帧缓冲区对象为DEPTH_STENCIL_ATTACHMENT 并设置glEnable(GL_DEPTH_TEST)glDepthMask(GL_FALSE)(编辑:在这种情况下,我不会将DepthStencilTexture 绑定到着色器,以避免循环反馈,请参阅 Nicol Bolas 的回答,如果我需要片段着色器中的深度,我将使用 gl_FragCorrd.z)

在某些情况下,例如绘制光体积,我需要模板测试并写入模板缓冲区,我会选择解决方案 2)。 在其他情况下,我完全忽略 Stencil,只需要存储在 DepthStencilTexture 中的深度,选项 1) 是否比更“自然”的选项 2) 有任何优势?

例如,我对此有一个(我认为是愚蠢的)怀疑。有时在我的片段着色器中,我从深度计算 WorldPosition。在情况 1) 中是这样的

uniform mat4 invPV; //inverse PV matrix 
vec2 uv=gl_FragCoord.st/WinSize;
vec4 WorldPosition=invPV*vec4(uv, texture(DepthStencilTexture ,uv).r ,1.0f );
WorldPosition=WorldPosition/WorldPosition.w;

在情况 2 中)会是这样(编辑:这是错误的,gl_FragCoord.z 是当前片段的深度,而不是存储在纹理中的实际深度)

uniform mat4 invPV; //inverse PV matrix 
vec2 uv=gl_FragCoord.st/WinSize;
vec4 WorldPosition=invPV*vec4(uv, gl_FragCoord.z, 1.0f );
WorldPosition=WorldPosition/WorldPosition.w;

我假设情况 2) 中的 gl_FragCoord.z 与情况 1) 中的 texture(DepthStencilTexture ,uv).r 相同,或者换句话说,存储在 DepthStencilTexture 中的深度相同。这是真的吗? gl_FragCoord.z 是从当前绑定的DEPTH_STENCIL_ATTACHMENT 中读取的,还带有glDisable(GL_DEPTH_TEST)glDepthMask(GL_FALSE) 吗?

【问题讨论】:

    标签: opengl fbo deferred-shading


    【解决方案1】:

    严格遵守 OpenGL 规范,选项 2 是不允许的。如果您还从该纹理中读取,则不会。

    是的,我知道您正在使用写入掩码来防止深度写入。没关系; OpenGL规范很清楚。根据 OpenGL 4.4 的 9.3.1,在以下情况下建立反馈循环:

    • 来自纹理对象 T 的图像在附着点 A 处附加到当前绑定的绘制帧缓冲区对象

    • 纹理对象 T 当前绑定到纹理单元 U,并且

    • 当前可编程顶点和/或片段处理状态使其 可能(见下文)从绑定到纹理的纹理对象 T 中采样 单位U

    您的代码就是这种情况。所以从技术上讲,你有未定义的行为。

    这是未定义的一个原因是,只需更改写入掩码就不必执行清除帧缓冲区和/或纹理缓存之类的操作。

    话虽如此,如果您雇用NV_texture_barrier,则可以选择选项 2。尽管有这个名字,但它在 AMD 硬件上非常普遍。这里要做的主要事情是在完成所有深度写入后发出屏障,以保证所有后续读取都能正常工作。屏障将完成所有缓存清除以及您需要的操作。

    否则,选项 1 是唯一的选择:手动进行深度测试。

    我假设情况 2 中的 gl_FragCoord.z 与情况 1 中的纹理(DepthStencilTexture ,uv).r)相同,或者换句话说,存储在 DepthStencilTexture 中的深度。是真的吗?

    两者都不是。 gl_FragCoord 是正在处理的片段的坐标。这是 rasterizer 根据被光栅化的图元数据生成的片段。它与帧缓冲区的内容无关。

    【讨论】:

    • 这解释了很多,谢谢。但在我的选项 2) 中,我不从着色器中的纹理中采样,而是使用 gl_FragCoord.z 访问深度信息。所以没有达到反馈回路的第三个假设。我对吗? (将编辑我的问题以使其更清楚)。
    • @darius:那么,如果您不阅读深度,您如何重建延迟渲染的位置?你真的在写这个职位吗?
    • 没有。我在问题的最后写了这个:使用 gl_FragCoord.z。我的想法是:我将 DepthStencilTexture 作为 DEPTH_STENCIL_COMPONENT 绑定到 FBO,所以 gl_FragCoord.z 应该给我相同的结果,就好像我从纹理中采样一样,但实际上没有从中采样。
    • @darius:这不是它的工作原理。 gl_FragCoord当前片段的坐标。它与帧缓冲区完全无关。
    • 啊,好的!当然!这是我在这里的大错误。这真的让我头脑清醒了。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多