OpenGL add blend mode on frame buffer

The rendering is different if I render my scene directly on the screen, and if I render on a FBO (frame buffer object), I suspect that the add blend mode is not working in the later case:

Here is the result directly on screen:

Rendering directly on screen

Here is the result with a frame buffer:

enter image description here

As you can see, in the frame buffer the blue color does not sum to become bright (and eventually white).

To draw on screen I just call the draw particles function:

 drawParticles();

And for the FBO (with ofFbo from openframeworks):

 ofClear(0, 0, 0, 0);

 fbo.begin();
 drawParticles();
 fbo.end();
        
 fbo.draw(0,0);
1 Like

@arthur_sw, are you setting the blend mode to OF_BLENDMODE_ADD or OF_BLENDMODE_ALPHA? And then are you disabling that blend mode before drawing your fbo?

I see on stackoverflow that you are using a 32 bit precision fbo, which I assume you are using because it has a high color precision than drawing to the screen directly. One guess might be that the difference between what you are showing might be due to color rounding during blending. Have you tried using GL_RGBA16F_ARB instead of GL_RGBA32F_ARB and comparing the results?

I got an those errors with GL_RGBA16F_ARB

[ error ] ofGLUtils: ofGetGLFormatFromInternal(): unknown internal format 34842, returning GL_RGBA
[ error ] ofGLUtils: ofGetGlTypeFromInternal(): unknown internal format 34842, returning GL_UNSIGNED_BYTE

and it didn’t change the result.

Here is my whole code, to draw on screen I use this version, and to draw on the fbo I uncomment the comments:

//            ofClear(0, 0, 0, 0);
//            bodyFBO.begin();
            
            if(persistence>0)
            {
                ofSetColor(0,0,0,255*(1.0-persistence));
                ofFill();
                ofRect(0, 0, ofGetScreenWidth(), ofGetScreenHeight());
            }
            else
                ofBackground(0, 0, 0);
            
            effects[effect]->draw();
//            bodyFBO.end();
            
            ofEnableAlphaBlending();
//            bodyFBO.draw(0,0);

And here is the actual drawing, in effects[effect]->draw():

ofSetColor(color);

ofEnableBlendMode(OF_BLENDMODE_ADD);

ofEnablePointSprites();

ofTexture &texture = textureIndex>0?texture2:texture1;
texture.bind();

vbo.draw(GL_POINTS, 0, (int)points.size());

texture.unbind();

ofDisablePointSprites();

The blue does not become white even on the screen, but it does get lighter than on the frame buffer (and so it looks much better on the screen).

Right okay, not a precision thing then. Hmm, I’m not sure what is happening. Hopefully someone else jumps in here.

Going off of your code, here’s a simplified version I was playing with that replicates your issue:

void testApp::setup(){
    ofSetFrameRate(60);
    ofSetBackgroundAuto(false);
    ofBackground(0);

    fbo.allocate(ofGetWidth()/2, ofGetHeight(), GL_RGBA32F);
    fbo.begin();
        ofBackground(0);
    fbo.end();
}

void testApp::draw(){
    // Draw directly to screen
    drawCircle();

    // Draw to fbo
    fbo.begin();
        drawCircle();
    fbo.end();
    ofDisableBlendMode();
    fbo.draw(ofGetWidth()/2, 0);
}

void testApp::drawCircle() {
    ofEnableAlphaBlending();
    ofSetColor(0, 0, 0, 2);
    ofFill();
    ofRect(0, 0, ofGetScreenWidth(), ofGetScreenHeight());
    ofSetColor(50, 50, 255, 255);
    ofEnableBlendMode(OF_BLENDMODE_ADD);
    ofCircle(ofRandomWidth()/2, ofRandomHeight(), 50);
}

Directly to canvas on the left and fbo on the right:

1 Like

Yes, this does replicate very well my problem ! Thanks : - )
I’ll also transfer this on stackoverflow, hopefully someone will understand what’s going on.

Hey guys,
I had similar issues lately. And I ended up seeing that my problem was because of blending twice (in fbo and when applying fbo). read this posts carefully and check that out:


Oh I’m sorry if those posts didn’t help… I’m checking it myself now… it seems that you have a different problem here. I guess that ofClear might fix it… I’ll give it a try.

ufff i got it… those blending issues are really a nightmare… I wish we could have tools to fix them…

void ofApp::setup(){
    ofSetFrameRate(60);
    ofSetBackgroundAuto(false);
    ofBackground(0);
    fbo.allocate(ofGetWidth()/2, ofGetHeight(), GL_RGB);
    fbo.begin();
    ofBackground(0);
    fbo.end();
}

void ofApp::draw(){
	
	// Draw directly to screen
	drawCircle();
	
    // Draw to fbo
    fbo.begin();
	drawCircle();
    fbo.end();
	fbo.draw(ofGetWidth()/2, 0);
}

void ofApp::drawCircle() {
	ofPushStyle();
	
	ofEnableAlphaBlending();
    ofSetColor(0, 0, 0, 2);
    ofFill();
    ofRect(0, 0, ofGetScreenWidth()/2, ofGetScreenHeight());
	
	ofEnableBlendMode(OF_BLENDMODE_ADD);
    ofSetColor(50, 50, 255, 255);
    ofCircle(ofRandomWidth()/2, ofRandomHeight(), 30);

	ofPopStyle();
}

Notice that i use GL_RGB and also added ofPushStyle() but i had to try many things before seeing what was going on…

2 Likes

@Jordi Nice solve!

So to clarify for @arthur_sw (and myself) as to what’s going on here, I’m attaching some more code that uses @Jordi’s as a jumping point.

Pushing and popping the style was important because the ofSetColor(...) commands were applying a tint to the fbo. If we remove the push/pop and then add in ofSetColor(255,255,255) before drawing the fbo, everything is peachy. (Well, you need to add back the ofDisableAlphaBlending() because the push/pop was also saving and restoring the blend modes).

The switch to using GL_RGB was a red herring. You can still use GL_RGBA32F and have it work. (Though it is really surprising to me that using GL_RGB would work with drawing colors with transparency…?)

ofFbo fbo;

void testApp::setup(){
    ofSetFrameRate(60);
    ofSetBackgroundAuto(false);
    ofBackground(0);
    fbo.allocate(ofGetWidth()/2, ofGetHeight(), GL_RGBA32F);
    fbo.begin();
        ofBackground(0);
    fbo.end();
}

void testApp::draw(){
	// Draw directly to screen
	drawCircle();

    // Draw to fbo
    fbo.begin();
        drawCircle();
    fbo.end();
    ofSetColor(255,255,255);
    ofDisableAlphaBlending();    
	fbo.draw(ofGetWidth()/2, 0);
}

void testApp::drawCircle() {
	ofEnableAlphaBlending();
    ofSetColor(0, 0, 0, 2);
    ofFill();
    ofRect(0, 0, ofGetScreenWidth()/2, ofGetScreenHeight());
	ofEnableBlendMode(OF_BLENDMODE_ADD);
    ofSetColor(50, 50, 255, 255);
    ofCircle(ofRandomWidth()/2, ofRandomHeight(), 30);
}
2 Likes

Thanks a 1000 times !

It seems that the only thing missing was

ofSetColor(255,255,255);

before drawing the FBO, am I wrong?
I kept GL_RGBA32F and ofEnableAlphaBlending() (before drawing the fbo) and it works fine.

You can answer on stackoverflow and have some reputation.

1 Like

Correct - you just needed the ofSetColor(255, 255, 255) call before the fbo draw.

When everything is being passed to the GPU for rendering, I think that the vertex colors are being set by ofSetColor(...). With the openGL settings used by openFrameworks, the texture (from your fbo) gets blended with the vertex colors, which resulted in you having blue-tinted fbo renderings.

Leaving up to @Jordi to answer on stackoverflow if he wants to do so.

I juste realized that using GL_RGB to allocate the fbo is necessary to have the exact same thing on the screen and on the fbo.

fbo.allocate(ofGetWidth()/2, ofGetHeight(), GL_RGB);