Glow/bloom in OpenGL

Apprentice
Posts: 16
Joined: 2009.01
Post: #1
I'm trying to figure out how to add a glow effect where certain areas of a model (individual polygons in my case). So far I think the general strategy is:
  1. Render the scene normally
  2. Render the regions I want to glow offscreen (to a frame buffer object?)
  3. Blur the image that has the glowing parts
  4. Render the image on top of the real scene (does that mean this image is a texture I drew to?)

Am I on the right track? Also note that I'm using shaders, though I'm fairly new at them.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #2
That's pretty much the trick. What you'll want to do is render the parts you want to glow in white ( or whatever color ) over black into an FBO.

You can blur the FBO pretty easily using two 1D gaussian kernels -- once horizontally, and once vertically. That's pretty important if you want to keep performance acceptable.

Than you can use additive blending ( glBlendFunc( GL_ONE, GL_ONE ) ) to draw the FBO atop the scene.

I'm happy to share my GLSL gaussian blur code if you're interested.
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #3
Or, depending on your content, combine steps 1 and 2 by putting the glow factor into the destination alpha.
Quote this message in a reply
Apprentice
Posts: 16
Joined: 2009.01
Post: #4
O.K.

Does this mean that I need three sets of shaders; one for drawing the scene, one for drawing the glowing parts to the FBO, and one for drawing the FBO contents on top of the scene?
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #5
You've got (at least) three state vectors.
Quote this message in a reply
Apprentice
Posts: 16
Joined: 2009.01
Post: #6
Well, it doesn't help that I've never used frame buffer objects before. Nor have I used textures that much. Blush

To teach myself frame buffer objects I've been trying to render my scene to a texture then render a quad with that texture, but nothing shows up. This is how I'm trying to set things up, where the code is strewn about in an NSOpenGLView subclass:
Code:
GLuint fbo; // Frame buffer object
GLuint fboDepthbuffer; // Render buffer object

GLuint sceneTex; // Texture

...

// Set up the colour texture
glGenTextures(1, &sceneTex);
glBindTexture(GL_TEXTURE_2D, sceneTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 128, 128, 0,
  GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glBindTexture(GL_TEXTURE_2D, 0);

// Set up the render buffer for depth testing
glGenRenderbuffersEXT(1, &fboDepthbuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
  128, 128);

glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

// Set up the frame buffer
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
  GL_TEXTURE_2D, sceneTex, 0);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
  GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fboDepthbuffer);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

...

// Resizing ----------------

int width = [self bounds].size.width;
int height = [self bounds].size.height;

if (height == 0)
{
    height = 1;
}

glViewport(0, 0, width, height);

GLdouble fAspect = (GLdouble)width / (GLdouble)height;

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

gluPerspective(fovy, fAspect, zNear, zFar);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Drawing ----------------

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)
  != GL_FRAMEBUFFER_COMPLETE_EXT)
{
    exit(1);
}

glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,128,128);

glLoadIdentity();

glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Hopefully this will fill the texture red

glPopAttrib();

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glLoadIdentity();

glBindTexture(GL_TEXTURE_2D, sceneTex);

glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(-10.0f, -10.0f);

    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(10.0f, -10.0f);

    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(10.0f, 10.0f);

    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(-10.0f, 10.0f);
glEnd();
glDisable(GL_TEXTURE_2D);

glFlush(); // By now I expect to see a red square on a black background, but I see no red at all
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #7
Your framebuffer setup looks fine. But your quad drawing is at Z = 0, so it's probably clipped by your perspective projection. To debug, ignore the FBO stuff and just draw a white untextured quad.
Quote this message in a reply
Apprentice
Posts: 16
Joined: 2009.01
Post: #8
Yeah, it was just the location of my quad that was off.

Though it does raise the issue of how to effectively size my scene texture and draw it so that I cover the entire window, taking into account window resizing. For one thing, do you suppose it would make sense to call glOrtho2d before rendering the final texture quad?

Also, is it true that multisampling doesn't work when I'm drawing to a texture? I suppose a way to work around that, assuming I want full-screen antialiasing, is to either blur the scene in my fragment shader or to use mipmapping.
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #9
How to draw the quad entirely depends on what you're trying to do. If you want to fill the entire window with a glow texture, a 2D ortho projection makes that easy. Equivalently, the identity projection matrix, with a quad sized to the unit square [-1, 1] will also fill the entire viewport.

Sizing the texture, taking into account aspect ratio etc, is something you'll need to experiment with to see what resolution ratio between window/offscreen is acceptable.

Multisampling does not work with textures, but EXT_framebuffer_multisample will let you create a multisample renderbuffer. You have to explicitly perform the downsample resolve using EXT_framebuffer_blit. These are fairly new extensions, and aren't supported on all hardware. An alternative way to get multisampled render-to-texture is to use NSOpenGLContext's createTexture:fromView:internalFormat: method. That will give you a texture from the view attached to the context, based on whatever pixel format the context is using, including multisampling. It will implicitly do the resolve for you, like regular multisampling. And it works on all renderers that support multisampling (which sadly, doesn't include the Intel GMA renderers in MacBooks/iMacs/Minis.)

If you need some sort of antialiasing even on renderers that don't support multisampling, you can do your own supersampling, by rendering to a texture 2x larger than your target size and then drawing it with linear filtering. You can also mix this technique with regular multisampling, to get more than the 4 or 8 samples the hardware supports.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  How to do a Quick, Dirty bloom effect FAST! Oddity007 12 8,411 May 9, 2009 09:42 AM
Last Post: aardvarc
  So called "Full Screen Glow" TomorrowPlusX 9 5,181 Mar 7, 2005 01:39 PM
Last Post: TomorrowPlusX