Closing alpha for blending sprites
In modern OpenGL, I paint a series of adjacent sprites using a textured square for each sprite (as in the case of a brush). What technique can I use to paint sprites without accumulating alpha data?
To be clear, let me show you an example.
Let's assume my sprite is a semi-transparent circle, as such:
If I draw two adjacent sprites I would get this:
This is very fine, although I want to get this:
If the alpha value of the result is the maximum of the alpha of the source and the dest, as if they were part of the same visual layer.
I'm guessing it has to do with the blend function . Currently these are:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
What mixing function or other technique can be used to achieve the desired result?
source to share
(Ab) use a stencil buffer:
- Clear stencil buffer to 0xff
- Circle forach, just write (& blend) to color and stencil buffers where the stencil buffer is 0xff. If the stencil and depth tests have undergone a successful assertion of the stencil value for fragment numbering.
This way you can only & blend the color pixel once, until you clear the stencil buffer again.
Example:
#include <GL/glut.h>
void quad()
{
glBegin( GL_QUADS );
glVertex2i( -1, -1 );
glVertex2i( 1, -1 );
glVertex2i( 1, 1 );
glVertex2i( -1, 1 );
glEnd();
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( -2, 2, -2, 2, -1, 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// clear stencil buffer to all 1s
glClearStencil( 0xff );
glClear( GL_STENCIL_BUFFER_BIT );
// turn on stencil testing & make sure we don't mask out any bits
glEnable( GL_STENCIL_TEST );
glStencilMask( 0xff );
// only allow fragments through to the color/depth buffers if
// the stencil buffer at that point is 0xff
glStencilFunc( GL_EQUAL, 0xff, 0xff );
// if the stencil/depth tests succeed for a fragment,
// zero out the stencil buffer at that point;
// that way you can only write to a color buffer pixel once
glStencilOp( GL_KEEP, GL_KEEP, GL_ZERO );
glEnable( GL_BLEND );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPushMatrix();
glTranslatef( -0.5, -0.5, 0 );
glColor4ub( 255, 0, 0, 128 );
quad();
glPopMatrix();
glPushMatrix();
glTranslatef( 0.5, 0.5, 0 );
glColor4ub( 255, 0, 0, 128 );
quad();
glPopMatrix();
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_STENCIL );
glutInitWindowSize( 600, 600 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
source to share
This is what I ended up doing. It's hard to call this an answer since I changed the parameters of the question a bit, but I thought I mentioned it for posterity.
Instead of using transparent sprites, I use fully opaque sprites.
Then I create them with a texture and then paint this texture with the desired opacity.
source to share