PDA

View Full Version : glow effect and the depth buffer



abiogenesis
12-07-2009, 03:18 PM
Hi. I want to implement a glow effect for the particular objects on the scene. This is how I do it using 2 framebuffers:
1) Render the scene normally.
2) Bind framebuffer 1 and render the objects which must be "glowed" with the simple shader on the black background with the color of the glow light.
3) Bind framebuffer 2 and apply horizontal Gaussian filter using framebuffer 1 as the input texture.
4) Bind framebuffer 1 and apply vertical Gaussian filter using framebuffer 2 as the input texture.
5) Use additive blending to draw texture from framebuffer 1 over the main buffer with the rendered scene.

Also I must use the depth buffer from the main buffer to allow step 2 to be executed correctly (to draw only that parts of the glowing objects that are not overlapped by non-glowind objects)

This is how my FBO are initialized:


glGenFramebuffers (2, fbo);
glGenTextures (2, textures);
glGenRenderbuffers (1, &depthBuffer);

// Renderbuffer stores the depth buffer
glBindRenderbuffer (GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);

// Attach textures to the framebuffers
for (int i = 0; i < 2; i++)
{
glBindFramebuffer (GL_FRAMEBUFFER, fbo[i]);
glBindTexture (GL_TEXTURE_2D, textures[i]);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_FLOAT, NULL);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[i], 0);
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
}

I attach renderbuffer with the depth values to both FBO but it used only once at step 2.

This is how I do the step 2:


fboHandler->activate ();

void FboHandler::activate ()
{
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, textures[0]);
glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, textures[1]);
glActiveTexture (GL_TEXTURE0);

glBindFramebuffer (GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, fbo[0]);
glBlitFramebuffer (0, 0, width, height, 0, 0, width, height,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);

glBindFramebuffer (GL_FRAMEBUFFER, fbo[0]);
glClear (GL_COLOR_BUFFER_BIT);
}

// Paint the objects with the glow color
// ...

fboHandler->deactivate ();
glDisable (GL_DEPTH_TEST);

void FboHandler::deactivate ()
{
glBindFramebuffer (GL_FRAMEBUFFER, 0);
}


So to use the main depth buffer in my FBO I copy it using glBlitFramebuffer.
I create my FBO with the same size as the size of the main buffer because glBlitFramebuffer allows to copy unequal sized buffers only for color component but not depth.
Also glGetError returns 0 for glBlitFramebuffers.

As there is no need for depth buffer in the blur shader (Gaussian filter) in steps 3, 4, 5, I disable it.
As for now, the framebuffer's 1 texture must contain the one-colored image of all object that must be glowed. And if that objects are behind other objects, they shouldn't be in that texture because the I copied the main depth buffer to my FBO.

Then I do steps 3, 4 and 5.


// Apply the filters
// ...
// Draw the final glow texture
glEnable (GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDrawArrays (GL_QUADS, 0, 12);
glDisable (GL_BLEND);


And the glow is painted incorrect, sometimes in front of objects which are in front of the glowed objects, sometimes it disappears, sometimes it flickers.
And it is also low quality (with thin stripes) comparing to the glow effect rendered while using low-resolution FBO. But I can't use low-resolution FBO because than I won't be able to use glBlitFramebuffer.

So where is my mistake? How can I use depth information from the main buffer to draw the necessary objects correctly to the framebuffer?

Great thanks in advance.

DmitryM
12-08-2009, 06:22 AM
I guess you can't use your main buffer's depth information for other FBO's.

Instead of copying the depth from main->fbo, you can create in FBO (as render buffer or texture) together with color buffer, then use it for GLOW and then copy the color to the main buffer. The operations number & memory consumption is almost the same comparing to what you are trying to do.

BTW, for ping-ponging between textures (Gaussian filter), I'd just attach both to one FBO and switch using DrawBuffers() command.

abiogenesis
12-08-2009, 03:07 PM
OK, I changed the strategy and got into another trouble.

Now I have 3 FBO:


glGenRenderbuffers (1, &amp;colorBuffer);
glBindRenderbuffer (GL_RENDERBUFFER, colorBuffer);
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_RGBA8, width, height);

glGenRenderbuffers (1, &amp;depthBuffer);
glBindRenderbuffer (GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, width, height);

glBindFramebuffer (GL_FRAMEBUFFER, fbo[2]);
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);

glGenTextures (2, textures);
for (int i = 0; i < 2; i++)
{
glBindFramebuffer (GL_FRAMEBUFFER, fbo[i]);
glBindTexture (GL_TEXTURE_2D, textures[i]);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_FLOAT, NULL);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[i], 0);
}


One multisampled FBO with attached renderbuffers to render my multisampled main buffer to, and others non-multisampled and without depth buffers for Gaussian filters.
So I set glDepthFunc (GL_LESS), draw all my scene to fbo[2] (I also must copy fbo[2] to fbo[0] to draw the whole scene befure using fbo[0] futher for blur, but it doesn't matter now)
Then I change glDepthFunc to GL_LEQUAL and draw my model with the very simple one-color shader.


simple.vs:
in vec3 vertex;

uniform mat4 projection;
uniform mat4 modelview;

void main ()
{
gl_Position = projection * modelview * vec4 (vertex, 1.0);
}

simple.fs:
out vec4 fragcolor;

void main ()
{
fragcolor = vec4 (0.35, 0.08, 0.8, 1.0);
}

Then I copy fbo[2] to fbo[0] and draw it.
And if the original scene is OK, the one-color image is corrupted.
http://img9.imageshack.us/img9/2583/corrupted.png

And if I always use glDepthFunc (GL_LESS), the resulting corrupted image is even more sparse.

So why does it get corrupted if I change the shader program? I don't even clear the color or depth buffer.
Thanks.

DmitryM
12-08-2009, 03:33 PM
Looks like a z-fighting issue to me. Try using PolygonOffset in this case.
However, I don't understand clearly what you are trying to do (describe in more detail?).

abiogenesis
12-08-2009, 03:58 PM
Thanks DmitryM!
Sorry for the absence of knowledge about z-fighting.
Now the problem is solved by using glPolygonOffset and turning on/off GL_POLYGON_OFFSET_FILL when drawing one-colored models.