Hi,
I’m interested in alpha compositing and blend modes, so I made some investigations.
I start with your code and try to obtain a multiply blendmode which give results similar to photoshop:
recordFbo.begin();
ofClear(255, 255, 255, 0);
aImg.draw(0, 0);
ofEnableBlendMode(OF_BLENDMODE_MULTIPLY);
ofSetColor(255, 255, 255, parOpacity);
maskFbo.draw(0,0);
ofSetColor(255, 255, 255);
ofDisableBlendMode();
recordFbo.end();
From the ofGLRenderer::setBlendMode(ofBlendMode blendMode) definition we know:
case OF_BLENDMODE_MULTIPLY:{
glEnable(GL_BLEND);
#ifndef TARGET_OPENGLES
glBlendEquation(GL_FUNC_ADD);
#endif
glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
break;
}
The blend factors are GL_DST_COLOR and GL_ONE_MINUS_SRC_ALPHA. We can compute the blending result to understand what happens:
- RGBA values for the recordFbo pixel: (Rs, Gs, Bs, As)
- RGBA values for the aImg pixel: (Rd, Gd, Bd, Ad)
For the following calculations I assume the RGBA values are expressed in the 0…1 range. This is simplier than the 0…255 range.
According to this doc, the RGBA values for the result are
R = Rs * Rd + Rd * ( 1 - As )
G = Gs * Gd + Gd * ( 1 - As )
B = Bs * Bd + Bd * ( 1 - As )
A = As * Ad + Ad * ( 1 - As ) = Ad * ( As + 1 - As ) = Ad
When As = 1, (1 - As ) = ( 1 - 1 ) = 0, and we have this result
R = Rs * Rd
G = Gs * Gd
B = Bs * Bd
This is the expected calculation for the blend mode multiply.
But what happens when As is not 1 ?
To compute for exemple the red component we start the same, multiplying the Rs and Rd component. But this time we add something else:
R = Rs * Rd // same as previously
+ Rd * ( 1 - As ) // but with somthing else this time !
This is why the result become brighter when parOpacity is less than 255 in your code.
I did some tries to fix it using glBlendFunc, but I can’t figure how to do. So I dig further and found this. The main answer is pretty explicit. Following these advices and links, I wrote a shader for the multiply blending mode. I’m not sure this is a good practise to pass to the shader the texture in which it draw, but it works 
The fragment shader:
#version 150
uniform sampler2DRect tex0;
uniform sampler2DRect dstTex;
uniform float alpha;
in vec2 texCoordVarying;
out vec4 outputColor;
void main (void)
{
// Get the pixels
vec4 src = texture( tex0, texCoordVarying );
vec4 dst = texture( dstTex, gl_FragCoord.xy );
// Modify foreground with opacity
src.a *= alpha;
// Compute alpha result
float a = src.a + dst.a * ( 1 - src.a );
// Premultiply colors
vec3 s = src.rgb * src.a;
vec3 d = dst.rgb * dst.a;
// Blendmode formula
vec3 rgb = s * d + s * ( 1 - dst.a ) + d * ( 1 - src.a );
// Unpremultiply
outputColor = vec4( rgb / a, a );
}
Use case:
void ofApp::draw()
{
// Draw img1 in fbo
fbo.begin();
ofClear( 255 );
ofSetColor( 255, 255, 255, 200 );
img1.draw( 0, 0 );
fbo.end();
// Draw img2 over img1 with multiply blend mode
fbo.begin();
shader.begin();
shader.setUniform1f( "alpha", 150.f );
shader.setUniformTexture( "dstTex", fbo.getTexture(), 1 );
img2.draw( 100, 100, 300, 300 );
shader.end();
fbo.end();
// Draw result
ofSetColor( 255 );
fbo.draw( 0, 0 );
}
Complete exemple:
multiply.zip (92.7 KB)