Search code examples
c++openglsdlphotoshopalpha-transparency

openGL Transparent pixels unexpectedly White


I noticed a big problem in my openGL texture rendering:

Assumedly transparent pixels are rendered as solid white. According to most solutions to similar issues discussed on StackOverflow, I need to set glBlend / the proper functions, but I have already set the necessary gl state and am positive that textures are loaded correctly as far as I can tell. My texture load function is below:

GLboolean GL_texture_load(Texture* texture_id, const char* const path, const GLboolean alpha, const GLint param_edge_x, const GLint param_edge_y)
{
    // load image
    SDL_Surface* img = nullptr; 
    if (!(img = IMG_Load(path))) {
        fprintf(stderr, "SDL_image could not be loaded %s, SDL_image Error: %s\n", 
               path, IMG_GetError());
        return GL_FALSE;
    }


    glBindTexture(GL_TEXTURE_2D, *texture_id);
    // image assignment
    GLuint format = (alpha) ? GL_RGBA : GL_RGB;
    glTexImage2D(GL_TEXTURE_2D, 0, format, img->w, img->h, 0, format, GL_UNSIGNED_BYTE, img->pixels);

    // wrapping behavior
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, param_edge_x);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, param_edge_y);
    // texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glBindTexture(GL_TEXTURE_2D, 0);
    // free the surface
    SDL_FreeSurface(img);

    return GL_TRUE;
}

I use Adobe Photoshop to export "for the web" 24-bit + transparency .png files -- 72 pixels/inch, 6400 x 720. I am not sure how to set the color mode (8, 16, 32), but this might have something to do with the issue. I also use the default sRGB color profile, but I thought to remove the color profile at one point. This didn't do anything. No matter what, a png exported from Photoshop displays as solid white over transparent pixels.

If I create an image in e.g. Gimp, I have correct transparency. Importing the Adobe .psd or .png does not seem to work, and in any case I prefer to use Photoshop for editing purposes.

Has anyone experienced this issue? I imagine that Photoshop must add some strange metadata or I am not using the correct color modes--or both. (I am concerned that this goes beyond the scope of Stack Overflow, but my issue intersects image editing and programming. Regardless, please let me know if this is not the right place.)

EDIT:

In both Photoshop and Gimp I created a test case-- 8 pixels (red, green, transparent, blue) clockwise.

tiny square

In Photoshop, the transparent square is read as 1, 1, 1, 0 and displays as white. In Gimp, the transparent square is 0, 0, 0, 0.

I also checked my fragment shader to see whether transparency works at all. Varying the alpha over time does increase transparency, so the alpha isn't outright ignored. For some reason 1, 1, 1, 0 counts as solid. In addition, setting the background color to black with glClearColor seems to prevent the alpha from increasing transparency.

I don't know how to explain some of these behaviors, but something seems off. 0 alpha should be the same regardless of color, shouldn't it?

(Note that I render a few shapes on top of each other, but I've tried just rendering one for testing purposes.)

The best I can do is post more of my setup code (with bits omitted):

// vertex array and buffers setup

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
// I think that the blend function may be wrong (GL_ONE that is).
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

glDepthRange(0, 1);
glDepthFunc(GL_LEQUAL);

Texture tex0;
// same function as above, but generates one texture id for me
if (GL_texture_gen_and_load_1(&tex0, "./textures/sq2.png", GL_TRUE,  GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE) == GL_FALSE) {
    return EXIT_FAILURE;
}

glUseProgram(shader_2d);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex0);
glUniform1i(glGetUniformLocation(shader_2d, "tex0"), 0);

bool active = true;
while (active) {
       glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

       // uniforms, game logic, etc.

       glDrawElements(GL_TRIANGLES, tri_data.i_count, GL_UNSIGNED_INT,   (void*)0);
}

Solution

  • I don't know how to explain some of these behaviors, but something seems off. 0 alpha should be the same regardless of color, shouldn't it?

    If you want to get an identical result for an alpha channel of 0.0, independent on the red, green and blue channels, the you have to change the blend function. See glBlendFunc.

    Use:

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    

    This cause tha the the red, green and blue channel are multiplied by the alpha channel. If the alpha channel is 0.0, the resulting RGB color is (0, 0, 0). If the alpha channel is 1.0, the RGB color channels keep unchanged.


    See further Alpha Compositing, OpenGL Blending and Premultiplied Alpha