[SOLVED] FBO and transparency


#1

I can’t figure out how I correctly manage transparency when using FBO.

I want to draw some rects and circles blended on an FBO and to make the FBO blend with other graphic elements drawn directly on the screen.

The code attached below is an example:
There are three elements : an FBO where two circles are blended, two other circles drawn on the screen the same way, and a rectangle drawn in the bottom area of the screen.

With this code, I get the right result except the colors of two circles on the FBO are not correct.

Using ofDisableAlphaBlending(); I got the right colors but the rectangle is not blended correctly. (Changing the order of drawing is not the solution I want here.)

Using glBlendFuncSeparate(); I got various results but I have no idea what combination of the parameters I have to use.

Please help me.
Thanks a lot!

void ofApp::draw() {
	// in setup()
	// set to background to (200, 200, 0)
	
	// create an fbo
	ofFbo fbo;
	fbo.allocate(ofGetWidth() / 2, ofGetHeight());

	// draw a gray box at the bottom
	ofSetRectMode(OF_RECTMODE_CORNER);
	ofSetColor(100);
	ofRect(50, ofGetHeight() - 150, ofGetWidth() - 100, 100);
	
	////////////////////////////////////////
	// draw on FBO and copy to the screen //
	////////////////////////////////////////

	// setup
	fbo.begin();
	ofClear(0, 0, 0, 0);

	// draw two circles
	ofSetColor(205, 50, 50, 80);
	ofCircle(200, 200, 100);
	ofCircle(250, 250, 100);
	
	// end
	fbo.end();
	ofSetColor(255);
	fbo.draw(0, 0);
	
	/////////////////////////////////
	// draw directly on the screen //
	/////////////////////////////////
	
	// setup
	ofPushMatrix();
	ofTranslate(ofGetWidth() / 2, 0);
	
	// draw two circles
	ofSetColor(205, 50, 50, 50);
	ofCircle(200, 200, 100);
	ofCircle(250, 250, 100);

	// end
	ofPopMatrix();
}

#2

THis is super odd.
and it has to do completely with the alpha.
if you control the alpha for both renders with the mouse

float alpha = ofMap(ofGetMouseX(), 0, ofGetWidth(), 0, 255);
    
    ofSetColor(205, 50, 50, alpha);//, 80);

you can clearly see how both behave in a different way. It’s like each alpha react according to a different “curve”.
I’ll dig a bit and see what comes out. I’ll let you know.
best!


#3

one thing that’s helpful to understand is that when you draw with alpha into the FBO, those pixels get the resulting alpha. When you then go to draw the FBO, that alpha effects how the FBO is rendered.

ofClearAlpha() is helpful – you can basically reset the alpha to 255 (full opaque) on the FBO – ie:

 fbo.begin();
 draw some transparent stuff
 ofClearAlpha();
 fbo.end();

Problem using ofxFluid on El Capitan (macOSX 10.11)
#4

Yes, roymacdonald.
This seems very odd and is very annoying when using transparency a lot with FBOs.
Thanks and looking forward to seeing your further research. :wink:


#5

Thanks zach. I understand what you mean.
But the reason I do not use ofClearAlpha() here is that I want to have a transparent background in the FBO.

I think there might be some tricky ways to make the alpha values to 255 selectively (not whole pixels in the FBO), but I’m looking for a simple or official way to do this. Any idea?


#6

is your goal for these elements:

ofSetColor(205, 50, 50, 80);
ofCircle(200, 200, 100);
ofCircle(250, 250, 100);

to appear completely opaque when you draw the FBO?

in that case you can try a few things:

a) using a shader to adjust the alpha values as you draw the fbo (scaling the pixels alpha by 255/80 would help here, for example).

b) looking at glColorMask which can prevent you from writing to different channels (ie, you can prevent writing to the alpha channel or color channels). you could draw objects twice, once for color and once for alpha…

just a couple thoughts off the top of my head, hope it helps?


#7

I run into this issue when rendering antialiased text to fbo as well. The trick is as you said, to find the right parameters with glBlendFuncSeparate(). Here’s what works for me…

// after fbo.begin() + ofClear()
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

Then before your fbo.draw(0,0)…

// before fbo.draw()...
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

After all that you prob want to reset your alpha blending back to whatever it was- ofEnableAlphaBlending().

P.S. you have a different alpha value for both your ofSetColor()'s (50 + 80).


#8

hey @trentbrooks
that actually works.
Thanks.
Anyways, besides this annoying case, this should get fixed, as what anyone might be expecting is that the fbo behaves in the same way as the normal drawing surface. Probably there’s something in the FBO that’s set differently thus producing this results. I’ll try to go deeper and see what it might be.
best


#9

@roymacdonald I don’t think anything is “broken” here – the fbo is a surface with various number of channels (RGBA, for example) , when you draw something with alpha, that channel gets affected the same way the red green and blue channels get affected as you draw. when you go to draw the FBO, there’s alpha values in the channel. @trentbrooks’s approach is to use glBlendFuncSeparate which has a different blending function for the alpha then the color, which helps isolate some of the side effects you are seeing. ofClearAlpha is also very handy (but not if you want to use the alpha in the fbo already).

here’s a few threads from a while ago that’s a useful reference:



#10

Hey Zach,
sure I understand that that’s the way it works, but what I mean is that it might be easier if the FBO’s default behavior would be that drawing something to it, with or without transparency, and then drawing it to the main drawing surface should render the same as drawing directly to the drawing surface.
Well, maybe there’s a reason for this to behave this way, I don’t know.
All the best!


#11

@zach I think what you said is the nature of FBO in general. The suggestion roymacdonald mentioned might be something more intuitive and easier to use. We could be happier if oF offers a higher level class of FBO.

@trentbrooks I really appreciate your code. It made my code work! (The different alpha values 50 and 80 were just my mistake. They should be same.)

Thanks all.

My final code is here:

void ofApp::draw() {
	// in setup()
	// set to background to (200, 200, 0)
	
	// create an fbo
	ofFbo fbo;
	fbo.allocate(ofGetWidth() / 2, ofGetHeight());

	// draw a gray box at the bottom
	ofSetRectMode(OF_RECTMODE_CORNER);
	ofSetColor(100);
	ofRect(50, ofGetHeight() - 150, ofGetWidth() - 100, 100);
	
	////////////////////////////////////////
	// draw on FBO and copy to the screen //
	////////////////////////////////////////

	// setup
	fbo.begin();
	ofClear(0, 0, 0, 0);
	glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

	// draw two circles
	ofSetColor(205, 50, 50, 80);
	ofCircle(200, 200, 100);
	ofCircle(250, 250, 100);
	
	// end
	fbo.end();
	ofSetColor(255);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
	fbo.draw(0, 0);
	
	// reset blending mode
	ofEnableAlphaBlending();
	
	/////////////////////////////////
	// draw directly on the screen //
	/////////////////////////////////
	
	// setup
	ofPushMatrix();
	ofTranslate(ofGetWidth() / 2, 0);
	
	// draw two circles
	ofSetColor(205, 50, 50, 80);
	ofCircle(200, 200, 100);
	ofCircle(250, 250, 100);

	// end
	ofPopMatrix();
}

#12

2 years later… still ran into this ‘problem’


#13

If you (like myself) came here through google, are trying to understand how blending, the depth map, and glBlendFuncSeparate(...) used in @trentbrooks answer all work together, I found this resource essential reading.

https://learnopengl.com/Advanced-OpenGL/Blending

It’s not a quick answer, but I found it essential for understanding what’s happening in my scene.