3

I have two framebuffers that I am rendering two different objects to. When I use the default framebuffer, I have both the objects rendering on the same one.

I want this behaviour to work when using multiple framebuffers! How do I merge two framebuffers and render the winning fragments on top (Depth tested)! Basically like a Photoshop Layer Merge but with depth testing!

I got as far as blitting a single framebuffer onto the default framebuffer, but I'm lost as to how I would merge two framebuffers together!

Note: I have a color and a depth attachment to the framebuffers.

Edit:

Alright. I almost have the setup of rendering to a quad working except for one little thing. My color buffes are properly sent to the shader using uniform samplers but my depth values return '0' all the time from the depth buffers.

This is how I have my depth buffers setup within the framebuffer.

  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);

This is how I send uniform samplers to the shader.

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);

This is how my shader looks:

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);
  }

}
Rathnavel
  • 85
  • 1
  • 15

3 Answers3

4

You will need a shader to achieve this. There is no built-in way to blit with depth values. Here is one way to do it that combines the contents of both FBOs.

Vertex shader (assumes a quad is drawn from (-1,-1) to (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);
}

The pixel shader could look like this:

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);
    }
}

See also OpenGL - How to access depth buffer values? - Or: gl_FragCoord.z vs. Rendering depth to texture for how to access the depth texture.

Note that I use texelFetch() here because linearly interpolating depth values does not give valid results.

bernie
  • 9,820
  • 5
  • 62
  • 92
  • Hi Bernie. Thanks for your help. I have it mostly working except that depth is returned '0'. I have edited my question with code. Kindly take a look! – Rathnavel Nov 29 '17 at 00:43
  • I don't see anything wrong with your code. Perhaps depth testing or depth writing is turned off when you render to your FBOs? This could explain depth values of 0. Can you confirm that you get the output of `Color1` then after compositing since all depth values == 0? – bernie Nov 29 '17 at 01:05
  • Yes you are right! I am getting the value of color1 or color2 based on the < > sign for depthvalues = 0. I am enabling depth testing right after I bind the framebuffers using glEnable(GL_DEPTH_TEST). Should I do anything in addition ? – Rathnavel Nov 29 '17 at 01:22
  • This means that depth writes are probably not happening. Make sure you have `glEnable(GL_DEPTH_TEST)`, `glDepthMask(GL_TRUE)`, `glDepthRange(0, 1)` when rendering to your FBOs. You can also try clearing one of the FBO's depth buffer to a value using `glClearDepth(1.0)` and `glClear()` and see if you get values different from 0. – bernie Nov 29 '17 at 13:46
  • Hey Bernie, Apparently I need to change the depth comparison to this. if(depth1.r < depth2.r){} instead of if(depth1.z > depth2.z){}. I have it working fine now! Btw, should I type this as an answer ? – Rathnavel Nov 29 '17 at 15:01
  • Ah you got it! Yes there was a bug in my shader (now fixed, sorry). Since it's a minor change, you could have edited my answer to fix the bug. I would suggest creating your own answer when it's substantially different from the ones given by other users which is not really the case here. – bernie Nov 29 '17 at 15:08
  • Cool Thanks mate! Just a quick question as far as achieving this effect with fixed function. I'm not sure of how would I go about sampling depth or color values in a fixed function setup ? – Rathnavel Nov 29 '17 at 15:39
  • It's been a long time since I last used fixed function. I don't think it's possible. – bernie Nov 29 '17 at 15:47
3

Blitting will never use the depth test, so you have to use a full-screen shader pass to combine both framebuffers. There are two options:

  1. Combine both framebuffers into a third one. This requires that both the color attachments as well as the depth attachments of both input FBOs are textures. You then render a full-screen quad and sample from both color buffers and depth textures. You basically do the depth test manually in the shader by comparing the two depth to decide which of the two color values you use as final output color for the fragment.

  2. You composite one of the framebuffers into the other, using the real depth test. In that case, only one of the FBOs has to use textures, the other one can use renderbuffers or the window-system provided buffers. You just have to render a full-screen quad, this time sampling the depth and color textures only from one input FBO, and render into the output FBO with depth testing enabled. YOu just set the color value as output of the fragment shader and additionally output the depth value to gl_FragDepth.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
derhass
  • 43,833
  • 2
  • 57
  • 78
1

This should be possible with a fragment shader that uses the color data from both framebuffers and the depth data from both framebuffers and fills in each pixel by evaluating, for each fragment, the texture corresponding to the fragment that won the depth test.

#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);
    }
}

You'd render a (full-screen, presumably) quad, use a pass-through vertex shader with this fragment shader, and attach the respective textures.

I don't know what format your Depth Texture is in; my assumption is that it contains vectors representing the individual fragment coordinates, and that the z-coordinate contains its depth. If that isn't the case, you'll need to make adjustments.

Xirema
  • 19,889
  • 4
  • 32
  • 68
  • Thanks Xirema! I have the quad rendering setup ready, except that depth values are all zero! Can you please take a look at the edited question with code! Thanks for your help! – Rathnavel Nov 29 '17 at 00:44