Skip to content

Instantly share code, notes, and snippets.

@roxlu

roxlu/blurfbo.md Secret

Last active February 7, 2019 14:01
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roxlu/549fe841d3f2713d34c5 to your computer and use it in GitHub Desktop.
Save roxlu/549fe841d3f2713d34c5 to your computer and use it in GitHub Desktop.
Render to texture with blur

Update (2016.11.05)

I just realised that I just should use PREMULTIPLIED ALPHA!

How to render to texture and blur

I'm trying to render a scene with some textures into a texture, also called render-to-texture (RTT), then apply a blur to this RTT and after I applied a blur I want to draw this blurred texture. I never really dived into this but apparently this is not trivial and you need to be aware when and what blending features you use.

I'll try to describe a bit what I'm rendering. I'll describe it as "layers" as this kinda easy to understand.

  • At the first layer I render a screen filling background image which fills the complete viewport
  • then I draw several layers with textures that have alpha transparency (pngs), this is what I call the scene.
  • I've also got a "overlay" which I draw on top of everything;

In pseude code I do this:

fbo.begin();
  scene.draw();
fbo.end();

blur.apply(fbo.getRTT());

blur.draw();

overlay.draw()

The problem I have, is that the RTT which is blurred gets blackish edges. I simplified my scene a bit and just started drawing a texture with a circle in its center and a transparent background (not using the matte option from Photoshop when exporting). When I render this scene and apply a blur I get something similar to what you see in the image below:

Just to be sure that the problem wasn't related to premultiplied alpha, I changed my fragment shader so that it generates the pixels for a circle inside the shader itself. When I inspected the results of my blur shader I get the following, which clearly adds the dark edges too:

When I disable blending when I capture the scene in my FBO and then apply a blur the blurred scene texture looks fine when I only draw one image. So when using this code below with only one texture, I get the image below (note the glDisable):

glDisable(GL_BLEND);

fbo.begin();
  texture_with_circle.draw();
fbo.end();

blur.apply(fbo.getRTT());

blur.draw();

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
overlay.draw()

This results in a correctly blurred RTT w/o blackish edges.

Though when I render multiple textures with blending disbabled overlapping textures simply become "hidden" / not drawn making this solution unusable.

Below is another example of what I'm trying to accomplish and what I'm getting. The image in the middle is what I'm trying to achieve and the right is what I get:

The solution

Ok, with the help of Victor Martins we came to a perfect solution. In the image below I'm rendering several textures with transparency into the FBO (the text and arrows). The textures do not contain premultiplied alpha but do contain an alpha channel.

When I draw into the FBO I've blending enabled with glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); After rendering the scene into the FBO, I pass the FBO texture (RTT) to my blur shader which performs two passes (X and Y blur pass). This will be done with blending disabled. Then, when blurred, I've got a texture that contains the blurred pixels with premultiplied alpha (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) from the RTT step, therefore the blurred edges will still look dark.

My blur class has a draw() function which will draw the blurred texture to screen. In the fragment shader of the blur class I unpremultiply the color channels to remove the dark edges. You can unpremultiply by doing: fragcolor.rgb = fragcolor.rgb / fragcolor.a. So in steps:

  • Set you clear color to: glClear(0.0f, 0.0f, 0.0f, 0.0f) (intel HD3000 seems to have a bug, use glClear(0.0f, 0.0f, 0.0f, 0.01f)` instead, not sure why, looks like an overflow / divide by zero maybe.).
  • Render the scene into the FBO with blending enabled and glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  • Disable blending and apply blur
  • Enable blending with glBlend(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  • Draw the blurred texture with a fragment shader that unpremultiplies the blurred texture colors frag.rgb = frag.rgb / frag.a

The result of this approach

Pseudo code

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

fbo.begin();
  scene.draw();
fbo.end();

glDisable(GL_BLEND)
blur.apply(fbo.getRTT());

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
blur.draw();

fragment shader part that unpremultiplies in blur.draw()

/* note: some GPUs will give incorrect results when fragcolor.a is 0. You can either check for this, set your clear color to (0.0, 0.0, 0.0, 0.01); */
fragcolor.rgb = fragcolor.rgb / fragcolor.a;

Author: roxlu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment