Problem accessing MRT samples in deferred shading

Hello,

I’ve played around with deferred shading recently and during several optimization approaches I’ve tried to pack additional values like specular or ambient factors in already existing buffers.
So I came up with the Idea of using the fourth alpha-channel in my normal buffer for that as suggested by several papers. The Problem however is that whenever I try to access the alpha channel of the normal buffer to light up some pixels, the whole scene gets partially transparent. It seems that the deferred fragment shader mixes alpha values from different buffers (normal and albedo).

During geometry processing my fragment shader does this:


// albedo
lo_fragcolor1 = texture(u_texture, gs_texcoord);
// normal with ambient light factor attached
lo_fragcolor2 = vec4(normalize(gs_normal) * 0.5 + 0.5, gs_ambient.r);

During deferred shading my fragment shader does this:


vec4 c = texture(u_colormap, gs_texcoord);
vec4 n = texture(u_normalmap, gs_texcoord);
// this causes objects to be partially transparent but I'm only touching RGB!
lo_fragcolor1 = vec4(c.rgb * n.a, 1);
// however this causes NO transparency
//lo_fragcolor1 = vec4(c.rgb * .3, 1);

My FBO setup looks like this:


// color / albedo
  glGenTextures(1, &ids[colorbuffer]);
    glBindTexture(GL_TEXTURE_2D, ids[colorbuffer]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, dim[0], dim[1], 0, GL_RGBA,
            GL_UNSIGNED_BYTE, NULL);
    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);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D, ids[colorbuffer], 0);
  glBindTexture(GL_TEXTURE_2D, 0);

  // normal
  glGenTextures(1, &ids[normalbuffer]);
    glBindTexture(GL_TEXTURE_2D, ids[normalbuffer]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, dim[0], dim[1], 0, GL_RGBA,
            GL_FLOAT, NULL);
    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);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
            GL_TEXTURE_2D, ids[normalbuffer], 0);
    glBindTexture(GL_TEXTURE_2D, 0);

Could someone point out the flaw in my thinking?

Regards
Saski

is your shader compiling correctly? double check it. everything goes semi-transparent or randomly colored if your shader failed to compile. and you should post full shaders with the code where you supply uniforms.

also,

lo_fragcolor2 = vec4(normalize(gs_normal) * 0.5 + 0.5, gs_ambient.r);

you are using float render target. it supports negative values. so there’s no need for “* 0.5 + 0.5”.

hi,
well I assume that the shader compiles correctly. glGetShaderiv() always returns a good status and there are no warnings whatsoever. And there is no random “jittering” in the scene. the depth issue remains constant. I also checked the shader on two different platforms (i965 and nvidia 8600) both show the same output (screenshot attached).
Ok, I’m going ahead and post some more details here:

complete FBO setup:



    // create main framebuffer object
  glGenFramebuffers(1, &ids[framebuffer]);
  glBindFramebuffer(GL_FRAMEBUFFER, ids[framebuffer]);

  // create MRT textures
// color / diffuse
  glGenTextures(1, &ids[colorbuffer]);
    glBindTexture(GL_TEXTURE_2D, ids[colorbuffer]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, dim[0], dim[1], 0, GL_RGBA,
            GL_UNSIGNED_BYTE, NULL);
    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);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D, ids[colorbuffer], 0);
  glBindTexture(GL_TEXTURE_2D, 0);

  // normal
  glGenTextures(1, &ids[normalbuffer]);
    glBindTexture(GL_TEXTURE_2D, ids[normalbuffer]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, dim[0], dim[1], 0, GL_RGBA,
            GL_FLOAT, NULL);
    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);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
            GL_TEXTURE_2D, ids[normalbuffer], 0);
    glBindTexture(GL_TEXTURE_2D, 0);

  // position
  glGenTextures(1, &ids[posbuffer]);
    glBindTexture(GL_TEXTURE_2D, ids[posbuffer]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, dim[0], dim[1], 0, GL_RGB,
            GL_FLOAT, NULL);
    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);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2,
            GL_TEXTURE_2D, ids[posbuffer], 0);
    glBindTexture(GL_TEXTURE_2D, 0);

    // depth
    glGenTextures(1, &ids[depthbuffer]);
    glBindTexture(GL_TEXTURE_2D, ids[depthbuffer]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, dim[0], dim[1], 0,
            GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    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);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
            GL_TEXTURE_2D, ids[depthbuffer], 0);
    glBindTexture(GL_TEXTURE_2D, 0);

   // populate textures to framebuffer (color and normals)
  GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
          GL_COLOR_ATTACHMENT2};
    glDrawBuffers(3, buffers);

  // extra status check
  GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  if(status!= GL_FRAMEBUFFER_COMPLETE) {
  C_Print(LOG_ERROR, "CreateMRTBuffers(): failed with [%i]!
", status);
  }

  glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind fbo for now

The shader program setup binds the output fragments and sets the texture units:


  glBindFragDataLocation(si->pgm, 0, fcolor1_id); // color
  glBindFragDataLocation(si->pgm, 1, fcolor2_id); // normal
  glBindFragDataLocation(si->pgm, 2, fcolor3_id); // position
  glUniform1i(si->loc[colormap_loc], 0);
  glUniform1i(si->loc[normalmap_loc],1);
  glUniform1i(si->loc[depthmap_loc], 2);
  glUniform1i(si->loc[posmap_loc],   3);

The geometry pass (quite messy right now) looks like this:


 // enable for writing to FBO
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ids[framebuffer]);
  // Only the geometry pass updates the depth buffer
  SubSys_Set3DMode();
  glPushAttrib(GL_VIEWPORT_BIT);
  SubSys_SetViewport(SubSys_GetVideoResolution());
  glClearColor(0,0,1,1);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);
  glDepthRange(0.0, 1.0);
  glDisable(GL_ALPHA_TEST);
  SubSys_LookAt(eye, dir);
  SubSys_SetState(r_cull, true);
  SubSys_SetState(r_blend, true);
  SubSys_EnableShaderStage(shader_fillMRT);
  DR_Draw(dr_geometry, 0xffff);
  SubSys_DisableShaderStage();
  SubSys_SetState(r_blend, false);
  SubSys_SetState(r_cull, false);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // disable writing
  glPopAttrib();
 

The code to draw the FullScreenQuad and to fire up the deferred shader:


  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_ALPHA_TEST);
    glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, ids[colorbuffer]);
    glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, ids[normalbuffer]);
    glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, ids[depthbuffer]);
    glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, ids[posbuffer]);
    SubSys_EnableShaderStage(shader_deferred);
    ....draw the quad...
  SubSys_DisableShaderStage();
  glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
  glEnable(GL_DEPTH_TEST);
  glPopMatrix();

The VS/FS Shader to fill the MRT Buffers:


#version 130 
precision mediump float;

uniform mat4 u_pjmatrix;
uniform mat4 u_mvmatrix;

in vec4 li_vtx;
in vec3 li_normal;
in vec2 li_texcoord;
in vec4 li_ambient;

out vec2 gs_texcoord;
out vec4 gs_ambient;
out vec3 gs_normal;
out vec3 gs_position;

void main(void) {
  gs_ambient  = li_ambient;
  gs_texcoord = li_texcoord;  
  gs_normal   = vec3(u_mvmatrix * vec4(li_normal, 0.0));
  gs_position = vec3(u_mvmatrix * li_vtx);
  gl_Position = u_pjmatrix * u_mvmatrix * li_vtx;
}


#version 130 
precision mediump float;

uniform sampler2D u_texture;
in vec2 gs_texcoord;
in vec4 gs_ambient;
in vec3 gs_normal;
in vec3 gs_position;

out vec4 lo_fragcolor1; // diffuse
out vec4 lo_fragcolor2; // normal
out vec3 lo_fragcolor3; // position

void main(void) {
    lo_fragcolor1 = texture(u_texture, gs_texcoord);
    lo_fragcolor2 = vec4(normalize(gs_normal), gs_ambient.r); // using alpha channel to store ambient factor
    lo_fragcolor3 = gs_position;    
}

And finally the deferred VS/FS code:


#version 130 
precision mediump float;

uniform mat4 u_mvmatrix;
uniform vec3 u_lightpos;

in vec2 li_vtx;
in vec2 li_texcoord;

out vec2 gs_texcoord;
out vec3 gs_lightpos;

void main(void) {
  gs_texcoord = li_vtx;
  gs_lightpos = vec3(u_mvmatrix * vec4(u_lightpos, 1));
  gs_viewpos  = vec3(u_mvmatrix * vec4(u_viewpos, 1));
  gl_Position = vec4(li_vtx, 0, 1) * 2.0 - 1.0;
}


#version 130 
precision mediump float;

uniform sampler2D u_colormap;
uniform sampler2D u_normalmap;
uniform sampler2D u_depthmap;
uniform sampler2D u_posmap;

in vec2 gs_texcoord;
in vec3 gs_fragpos;
in vec3 gs_lightpos;

out vec4 lo_fragcolor1;

void main(void) {
  vec4 c = texture(u_colormap, gs_texcoord);
  vec4 n = texture(u_normalmap, gs_texcoord); // stores ambient factor in alpha channeö
  vec3 p = texture(u_posmap, gs_texcoord).xyz;
  float d = texture(u_depthmap, gs_texcoord).r; 

  lo_fragcolor1 = vec4(c.rgb * n.a, 1);
}

Any Ideas?

Thx and Regards
Saski

When rasterizing your G-buffer, ensure that not only ALPHA_TEST but BLEND and SAMPLE_ALPHA_TO_COVERAGE as well is disabled, as you’re “overloading” what the pipeline would otherwise do with alpha (translucency processing).

Then provide a debug mode where you just read the G-buffer and blast the specified channel to the screen so you can verify what’s in there is correct. …before you try to debug the lighting output.

SubSys_SetState(r_blend, true);

you have blending enabled. and you are writing values to alpha channel. see the problem? can you imagine, what happening? yes, your normal-map texture, generated by geometry pass is blended using that value as a factor.

you don’t really need blending for G-Buffer pass. you will handle semi-transparent objects different way. but you can still use alpha-channel for binary transparency using conditional discard;(it will work for foliage, for example, or rugged cloth.)

oh yeah now I see my mistake! the GL_BLEND totally slipped me.

Thanks very guys much for the quick help!!!