FBO renders differently than draw (Semi-transparent / Anti-aliasing)

Hello everybody!

First off, this is my first time on the forums and I really need to publicly thank you all for this great framework. It got me started with C++ and opened a lot of creative possibilities for me :slight_smile:

But now I’ve run into a problem where I don’t know what to do.
I’m drawing lots of circles on top of circles with no background in between the draw calls.
So I use this configuration in my setup:

ofSetBackgroundAuto(false);
ofEnableAntiAliasing();
ofEnableAlphaBlending();

The result when drawing is this:

I want to save an image sequence out of it with a larger size than my screen.
So I figured I would need to use an FBO.

To get the layered effect working, I’m saving the current image as an FBO and drawing it before drawing the current iteration of circles.

It looks like this:

[...]
ofClear(255, 255, 255, 0);
fbo2.draw(0, 0);
ofClearAlpha();
[...]
fbo.end();
fbo2 = fbo;
fbo.draw(0, 0);

This works perfectly fine, I had some troubles with the alpha (before ofClearAlpha(), but I found a solution. The problem was, that the colors looked different.
They now looked like this:

IMGUR, first picture titled “raw FBO / glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);”

A little more washed out.

I stumbled upon a couple of forum posts like this one: " [Weird problem rendering semi-transparent image to FBO"
And started to implement a GLBlendMode. Using

ofClear(255, 255, 255, 0);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
fbo2.draw(0, 0);
ofClearAlpha();

I get the right colors, but not the sexy anti-alias blur anymore. It looks like this:

IMGUR, second picture titled “glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);”

Tinkering around with different GLBlendFunc settings, I found out that
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); instead of the glBlendFuncSeparateto the washed-out result, so this is what OF seems to use in some way?

But I can’t figure out what the correct glBlendFunc settings would be.
Or how I can just use an FBO without getting altered colors?

Does anybody have any idea?

I can of course share more code, just didn’t want to spam you with a wall.

Thanks a lot!

Also sorry about the IMGUR links, can’t have more than one picture as a new user in a post.

Hi and welcome!

How are you allocating your FBO? OF main buffer uses 4 samples, but by default the FBO allocate function uses none.

void allocate(int width, int height, int internalformat = GL_RGBA, int numSamples = 0);

I prefer to use the ofFboSettings struct.

Hey, thanks for the tip. I didn’t know there was more to allocation than what I had before. It was: fbo.allocate(1080, 1080, GL_RGBA);

I noticed that I’m also not allocating my second FBO drawing the background, but when I did that the picture was completely white and blown out, so that wasn’t it.

Using a higher sample count without glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) resulted in the same picture, everything brighter than expected, but smooth / blurry edges.

When using a sample count of 16 with glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) the background was slightly more blurred, but still no smoothed edges on the newly drawn circles (the newest “layer” drawn on top):

I’m guessing you must be right tough as the sample count was really evidently different from the main draw function. So I’m going to dissect more. Wish me luck and thanks a lot!

Edit: BTW, I’m a little confused by the documentation of the ofFbo::Settings object, if you don’t mind, would you share a little example of how to use it?

Edit 02: After tinkering around some more, I think I found the problem:
I’m clearing the FBO using ofClear(255, 255, 255, 0.0); which of course sets everything on a white “background” (even though the alpha was set to sorry by accident, but it still worked?). So everything looks brighter.
Now I’m clearing with ofClear(0, 0, 0, 1.0); and getting the right colors. Because there is no added brightness.

But the problem still persists, as the normal draw() call drew the semi-transparent circles on top of each other, resulting in these mixed, blurred, and “stronger” colors. With an FBO I need to clear the screen first, so I can’t draw on top of the previous transparency:

ofClear(0, 0, 0, 0.0);
fbo2.draw(0, 0);
ofClearAlpha();

Changing this order of operations leads to chaos. So I can’t clear the alpha before drawing the second FBO sadly.

Looking into the repository, ofClearAlpha(); does this:

glColorMask(0, 0, 0, 1);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(1, 1, 1, 1);

So it seems like a layer of black is drawn, prohibiting the alpha blending of the circles. But I don’t know much about GL.

Now there doesn’t seem to be a way to clear an FBO by using an FBO texture. So I don’t really know what to do here.

Hey @etlaM21 just a couple of quick thoughts:

An ofFbo will accumulate drawings each cycle if you don’t call ofClear(), so you can just draw a new set of circles on top of the previous rendering. A previous render can also be faded a bit each cycle by drawing a large, translucent rectangle in the ofFbo before drawing new circles.

OF has some different blend modes which can be passed as an argument for ofEnableBlendMode(). There is also a simple but helpful example on blending in /examples/graphics/blendingExample.

Hey Tim!
Thank you very much for your input.

I tried not using any clearing and also drawing a translucent rectangle, but still no luck.

Even with ofEnableBlendMode(OF_BLENDMODE_ALPHA); I end up with a black screen when I’m not using ofClearAlpha();.
Therefore the FBO will always only show the last iteration of the draw call, and I need to use a second FBO to “save” previous iterations.

Hum. I’m maybe a bit confused about effect you’re trying to achieve. I’m thinking that you want the blending of the new circles with the old rendering, but with vibrant colors. Is this correct?

The blend modes are convenient to use, but maybe a shader would provide more control. You could keep an ongoing rendering in an ofFbo, render the new circles into another (cleared) ofFbo, and use a shader to combine them in a 3rd ofFbo (which will then become the ongoing rendering in the next cycle). You can pretty much do anything you want in a shader, to each and every pixel in the rendering.

That’s a smart idea. I might do that, thanks!

It’s just that I didn’t want to use shaders or any other things. I simply wanted the output of my draw() call without drawing to an FBO to be the same as drawing to an FBO. And I thought that would be pretty simple as ofMain obviously is already kind of doing it when not writing to an FBO.
That’s why I wrote here, just to understand more and find a solution without applying more and more fancy techniques lol

Maybe a simple option could be to set the color (to something opaque) and draw a series of png images instead of circles. The image could have a gradient in the alpha channel, so that it gets more opaque in the center. Or perhaps its mostly an opaque circle with fade to transparent around the perimeter. I don’t think you’d need to do anything special with the blending, or perhaps combine this idea with what you’ve already got going.

It’s just a struct to have direct access to a FBO internal settings, and you only need to call one function:

struct ofFboSettings {
    int width;                        ///< width of images attached to fbo
    int height;                       ///< height of images attached to fbo
    int numColorbuffers;              ///< how many color buffers to create
    std::vector<GLint> colorFormats;  ///< format of the color attachments for MRT.
    bool useDepth;                    ///< whether to use depth buffer or not
    bool useStencil;                  ///< whether to use stencil buffer or not
    bool depthStencilAsTexture;       ///< use a texture instead of a renderbuffer for depth (useful to draw it or use it in a shader later)
    GLenum textureTarget;             ///< GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB
    GLint internalformat;             ///< GL_RGBA, GL_RGBA16F_ARB, GL_RGBA32F_ARB, GL_LUMINANCE32F_ARB etc.
    GLint depthStencilInternalFormat; ///< GL_DEPTH_COMPONENT(16/24/32)
    int wrapModeHorizontal;           ///< GL_REPEAT, GL_MIRRORED_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER etc.
    int wrapModeVertical;             ///< GL_REPEAT, GL_MIRRORED_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER etc.
    int minFilter;                    ///< GL_NEAREST, GL_LINEAR etc.
    int maxFilter;                    ///< GL_NEAREST, GL_LINEAR etc.
    int numSamples;                   ///< number of samples for multisampling (set 0 to disable)
    ofFboSettings(std::shared_ptr<ofBaseGLRenderer> renderer=nullptr);
    bool operator!=(const ofFboSettings & other);
private:
    std::weak_ptr<ofBaseGLRenderer> renderer;
    friend class ofFbo;
};

For example, you can do:

ofFboSettings fboSettings;
fboSettings.width = 1080;
fboSettings.height = 1080;
fboSettings.internalformat = GL_RGBA16F;
fboSettings.numSamples = 4;
(...)

yourFbo.allocate(fboSettings);
1 Like