Transparent pixels in image are rendered as black when using glBlitFramebuffer()

If I use this code to draw a image with transparent pixels to the screen, the transparent pixels are rendered as black, as said here link, glBlitFramebuffer doesn’t blend, but still why (0,0,0,0), “transparent pixels” are being drawn as (0,0,0,1), “black pixels”? I can’t understand that, as you can see here:


Code:

GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
glBlitFramebuffer(0, 0, textureW, textureH-50, 0, 0, textureW, textureH-50, GL_COLOR_BUFFER_BIT, GL_LINEAR);

Setting the red color as the clear color:

glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

How I load the texture:

int channels = 0;
        
unsigned char* image = SOIL_load_image(fileName.c_str(), &mWidth, &mHeight, &channels, SOIL_LOAD_AUTO);
if (image == nullptr)
{
   SDL_Log("SOIL failed to load image %s: %s", fileName.c_str(), SOIL_last_result());
   return false;
}
int format = GL_RGB;
if (channels == 4)
{
   format = GL_RGBA;
}
glGenTextures(1, &mTextureID);
glBindTexture(GL_TEXTURE_2D, mTextureID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
SOIL_free_image_data(image);

How I make the window:

mScreenW = screenW;
mScreenH = screenH;

SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);

SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);

mWindow = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, mScreenW, mScreenH, SDL_WINDOW_OPENGL);
if (!mWindow)
{
	SDL_Log("Failed to create window: %s", SDL_GetError());
	return false;
}

mContext = SDL_GL_CreateContext(mWindow);

SDL_SetWindowFullscreen(mWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);

Original image, you can’t see here I guess, but it transparent, was made using GIMP.

The framebuffer displayed on the screen contains one color value per pixel. After the blit in the black regions of your image that color values is 0,0,0,0. Since the red value that was written there by the clear has now been replaced, how would the system know that it should still show red for those pixels?
If blending was happening you could arrange for the blending operation to instead leave the red color intact when the incoming color has an alpha of 0.

BTW: If you goal is to have a window where you can see the desktop background (or other windows below yours) through parts of your window you need to tell your operating system when creating the window (assuming the OS supports this) or rather I think there is some flag for SDL that handles this for you.

I understand that, but that is counter intuitive as hell, why would I want to replace the clear color color value of (1,0,0,1) to (0,0,0,1) which should be (0,0,0,0)? It doesn’t make any sense to me…

So, some kind of blending is happening by default?

Why not? This should be the default behavior in my opinion, if the color value was (1,0,0,1), red, and you “try to replace it” with (0,0,0,0), “nothing”, leave it with the original color value, transforming this into (0,0,0,1), black, is just weird…

If this isn’t blending, what is this?

I don’t think that this has nothing to do with SDL since clearcolor is a OpenGL thing. And my goal isn’t to see the desktop background or other windows, is just to figure out a way of rendering images with transparency using glBlitFramebuffer.

No. The blit just writes the values from your image as is to the framebuffer. In the parts that you consider transparent your image contains (0,0,0,0) values (black with alpha == 0), that is what gets written to the framebuffer.

It does not get transformed to (0,0,0,1). What gets written into the framebuffer is (0,0,0,0) because those are the values that your texture stores in the regions you think of as transparent. So the blit writes that directly to the framebuffer.

I think your misunderstanding is that the alpha channel inherently has anything to do with stuff being transparent. That is not the case, it is simply the (by a vast majority) most common use of the alpha channel.
The effect that something with an alpha channel value < 1 appears to be partially transparent is achieved through blending (i.e. combining the color already in the framebuffer with the value produced by a drawing operation). No blending, no transparency and the alpha channel value of colors is basically not used.

After the clear your framebuffer contains (1,0,0,1) and if you do no further drawing you see a red image. You would also see a red image if you cleared the framebuffer to (1,0,0,X) for any X in [0,1]. Why? Because clear also applies no blending so the color values are written as is.

Ok. The part about seeing the desktop background was a guess on my part and I guessed wrong.

Yeah, that makes no sense. Think about it, (1,1,1,1) is white, alpha represents solid/full color, so the opposite of it should be no color, because otherwise you end up with a lot of ways to represent black. But anyway…

I don’t think of the as transparent, they are transparent, I made them in GIMP, and they are shown there as transparent, I EXPECT them to be rendered transparent.

That is confuse, it hasn’t anything to do with transparency, but in image editing programs we use it as transparency. “By a vast majority” is something common, that is reduntant.

So image editing programs use this by default and the OpenGL doesn’t, confuse.

Weird.

I don’t even know why you guessed that, I specifically was talking about clearcolor and glblitframebuffer, what is funny is that in those forums of asking questions, people “trying to help” don’t want to guess anything to try to help people, always asking to be more specific, but when you are specific people try to guess for some reason.

Anyway, you mentioned that I should make the SDL window do this by default, I don’t know any flag that do this, so if it isn’t SDL, it’s OpenGL and I swear to god, I have tried glEnable(GL_BLEND) and all kinds different parameters in the glBlendFuncSeparate() or glBlendFunc() functions and it didn’t work, even putting them before and after anything… Also glEnable(GL_ALPHA_TEST) and glAlphaFunc(GL_NOTEQUAL, 0.0)… And I didn’t find anything at all about using blending with glblitframebuffer…

You can’t. glBlitFramebuffer just copies the source to the destination. The only “processing” that it’s capable of is scaling. If you want anything else (e.g. blending, or alpha testing), you’re going to need to draw a quad (with glDrawArrays, glDrawElements or similar).

So I guess the only way to do this with glBlitFramebuffer is to read the pixels in the screen, change the RGBA values, make a texture of them and use glBlitFramebuffe again. I thought about this and tried in another post for something else, but I didn’t write a proper code, so I don’t know if this will work, but here it is for anyone that need it.
Setting the clearcolor value:

glClearColor(RValue, GValue, BValue, AValue);

Reading the pixels in the screen:

unsigned char pixels[ mScreenW * mScreenH * 4 ] = { 0 };
glReadPixels(0, 0, mScreenW, mScreenH, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

Changing the pixels:

for (int i = 0; i < sizeof(pixels)/sizeof(pixels[0]); i += 4)
{

if (pixels[i+3] == 0x00)
{
    pixels[i+0] = RValue; // red
    pixels[i+1] = GValue; // green
    pixels[i+2] = BValue; // blue
    pixels[i+3] = AValue; // alpha
}

}

Making a texture with the modified pixels:

GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mScreenW, mScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

Drawing the modified pixels using glBlitFramebuffer:

GLuint fboId2 = 0;
glGenFramebuffers(1, &fboId2);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId2);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, mScreenW, mScreenH, 0, 0, mScreenW, mScreenH, GL_COLOR_BUFFER_BIT, GL_LINEAR);

I don’t know if this is a good way, so I will not put it as a solution (if anyone has a better one, post it here).
Edit: This code will mess up the border of things in the images if I remember correctly.