深度测试后将两个单独的帧缓冲区合并到默认帧缓冲区

Merging two separate framebuffers onto default framebuffer after depth testing

我有两个帧缓冲区,我正在向它们渲染两个不同的对象。当我使用默认的帧缓冲区时,我在同一个对象上渲染了两个对象。

我希望此行为在使用多个帧缓冲区时起作用!如何合并两个帧缓冲区并将获胜的片段渲染在顶部(深度测试)!基本上像 Photoshop 层合并,但有深度测试!

我已经将单个帧缓冲区 blitting 到默认帧缓冲区,但我不知道如何将两个帧缓冲区合并在一起!

注意:我有一个颜色和深度附件到帧缓冲区。

编辑:

好的。除了一件小事外,我几乎已经设置了渲染到四边形的工作。我的颜色缓冲区使用统一采样器正确发送到着色器,但我的深度值 return 始终从深度缓冲区发送到“0”。

这就是我在帧缓冲区中设置深度缓冲区的方式。

  glGenFramebuffers(1, &_fbo);
  glBindFramebuffer(GL_FRAMEBUFFER, _fbo);

  glGenTextures(1, &_cbo);
  glGenTextures(1, &_dbo);

  {
    glBindTexture(GL_TEXTURE_2D, _cbo);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim.x, dim.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

  }

  {
    glBindTexture(GL_TEXTURE_2D, _dbo);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, dim.x, dim.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
  }

  glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _cbo, 0);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _dbo, 0);

这就是我将统一采样器发送到着色器的方式。

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,cbo1);
glUniform1i(glGetUniformLocation(QuadShader.Program, "color1"),0);


glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, cbo2);
glUniform1i(glGetUniformLocation(QuadShader.Program, "color2"), 1);

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, dbo1);
glUniform1i(glGetUniformLocation(QuadShader.Program, "depth1"), 2);


glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, dbo2);
glUniform1i(glGetUniformLocation(QuadShader.Program, "depth2"), 3);

glBindVertexArray(BgVao);
glDrawArrays(GL_TRIANGLES, 0, 6);

这是我的着色器的样子:

uniform sampler2D color1;
uniform sampler2D color2;
uniform sampler2D depth1;
uniform sampler2D depth2;

out vec4 FragColor;

void main()
{
  ivec2 texcoord = ivec2(floor(gl_FragCoord.xy));

  vec4 depth1 = texelFetch(depth1, texcoord,0);
  vec4 depth2 = texelFetch(depth2, texcoord,0);

  if(depth1.z > depth2.z)
  {
    FragColor = texelFetch(color1, texcoord, 0);
  }
  else
  {
   FragColor = texelFetch(color2, texcoord, 0);
  }

}

Blitting 永远不会使用深度测试,因此您必须使用全屏着色器通道来组合两个帧缓冲区。有两个选项:

  1. 将两个帧缓冲区合并为第三个。这要求两个输入 FBO 的颜色附件和深度附件都是纹理。然后渲染全屏四边形并从颜色缓冲区和深度纹理中采样。您基本上通过比较两个深度来在着色器中手动进行深度测试,以决定将两个颜色值中的哪一个用作片段的最终输出颜色。

  2. 您使用真实深度测试将一个帧缓冲区合成到另一个帧缓冲区中。在那种情况下,只有一个 FBO 必须使用纹理,另一个可以使用渲染缓冲区或 window-系统提供的缓冲区。您只需渲染一个全屏四边形,这次仅从一个输入 FBO 采样深度和颜色纹理,并在启用深度测试的情况下渲染到输出 FBO。您只需将颜色值设置为片段着色器的输出,并将深度值额外输出到 gl_FragDepth

这应该可以通过使用来自两个帧缓冲区的颜色数据和来自两个帧缓冲区的深度数据的片段着色器来实现,并通过为每个片段评估与获得深度的片段对应的纹理来填充每个像素测试。

#version 430

layout(location = 0) in vec2 tex_coord;

layout(binding = 0) uniform sampler2D color_texture_1;
layout(binding = 1) uniform sampler2D color_texture_2;
layout(binding = 2) uniform sampler2D depth_texture_1;
layout(binding = 3) uniform sampler2D depth_texture_2;

layout(location = 0) out vec4 fragment_color;

void main() {
    float depth_1 = texture(depth_texture_1, tex_coord).z;
    float depth_2 = texture(depth_texture_2, tex_coord).z;
    if(!/*check to verify *something* was rendered here in either framebuffer*/)
        discard;
    else {
        if(depth_1 > depth_2) //I'm pretty sure positive z values face the user
            fragment_color = texture(color_texture_1, tex_coord);
        else
            fragment_color = texture(color_texture_2, tex_coord);
    }
}

您将渲染一个(大概是全屏)四边形,将直通顶点着色器与该片段着色器一起使用,并附加相应的纹理。

我不知道你的深度纹理是什么格式;我的假设是它包含代表各个片段坐标的向量,并且 z 坐标包含它的深度。如果不是这种情况,您需要进行调整。

您将需要一个着色器来实现这一点。没有使用深度值进行位块传输的内置方法。这是一种结合两个 FBO 内容的方法。

顶点着色器(假设四边形是从 (-1,-1) 到 (1,1) 绘制的)

layout(location = 0) in vec4 Position;

void main()
{
    // Snap the input coordinates to a quad with it lower-left at (-1, -1, 0)
    // and its top-right at (1, 1, 0)
    gl_Position = vec4(sign(Position.xy), 0.0, 1.0);
}

像素着色器可能如下所示:

uniform sampler2D Color0;
uniform sampler2D Color1;
uniform sampler2D Depth0;
uniform sampler2D Depth1;

in vec2 TexCoords;
layout(location = 0) out vec4 FragColor;

void main()
{
    ivec2 texcoord = ivec2(floor(gl_FragCoord.xy));
    float depth0 = texelFetch(Depth0, texcoord, 0).r;
    float depth1 = texelFetch(Depth1, texcoord, 0).r;

    // possibly reversed depending on your depth buffer ordering strategy
    if (depth0 < depth1) {
        FragColor = texelFetch(Color0, texcoord, 0);
    } else {
        FragColor = texelFetch(Color1, texcoord, 0);
    }
}

另请参阅OpenGL - How to access depth buffer values? - Or: gl_FragCoord.z vs. Rendering depth to texture了解如何访问深度纹理。

请注意,我在这里使用 texelFetch(),因为线性插值深度值不会给出有效结果。