OpenGL / Qt:纹理透明度不起作用

时间:2011-11-06 18:57:13

标签: qt opengl textures

我在OpenGL中的纹理映射存在很大问题,主要是透明度。我试图通过首先显示一个简单的纹理,然后再显示一个更详细的纹理来复制多层背景(例如,第一个将是蓝色,第二个将包含山和东西)。我有这个几乎工作,但我有一个奇怪的结果,我不知道如何解决它。

我基本上希望第二个纹理中的黑色不会出现。我有这个工作,但我的山的棕色似乎与第一个背景纹理混合(或添加到它)。我的棕色山脉显示为淡红色。我的glBlendFunc目前在GL_ONE,但我已尝试使用GL_ONE_MINUS_SRC_ALPHA进行任何更改;黑色仍然在那里,山仍然是棕色。

我尝试过三种不同格式的图像,没有区别(BMP,JPG和PNG)。

以下是我对这两种纹理的代码:

纹理1:

if (buf.load("images/background-layer1.png"))
{
    tex1 = QGLWidget::convertToGLFormat( buf );
    glPushAttrib(GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT);

    glGenTextures(1, &texture[0]);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, 4, tex1.width(), tex1.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex1.bits());

    glEnable(GL_TEXTURE_2D);

    glBegin(GL_QUADS);
        glTexCoord2f(0, 0);
        glVertex2d(m_pBottomLeft.x, m_pBottomLeft.y); //Bottom Left

        glTexCoord2f(1, 0);
        glVertex2d(m_pBottomRight.x, m_pBottomRight.y); //Bottom Right

        glTexCoord2f(1, 1);
        glVertex2d(m_pTopRight.x, m_pTopRight.y); //Top Right

        glTexCoord2f(0, 1);
        glVertex2d(m_pTopLeft.x, m_pTopLeft.y); //Top Left
    glEnd();

    glDisable(GL_TEXTURE_2D);
    glPopAttrib();
}

纹理2:

if (buf2.load("images/background-layer2.png"))
{
    tex2 = QGLWidget::convertToGLFormat( buf2 );

    glPushAttrib(GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glEnable(GL_DEPTH_TEST);

    glEnable(GL_TEXTURE_2D);

    glGenTextures(2, &texture[1]);
    glBindTexture(GL_TEXTURE_2D, texture[1]);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex2.width(), tex2.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex2.bits());

    glBegin(GL_QUADS);
        glColor4f(1, 1, 1, 1);
        glTexCoord2f(0, 0);
        glVertex2d(m_pBottomLeft.x, m_pBottomLeft.y); //Bottom Left

        glTexCoord2f(1, 0);
        glVertex2d(m_pBottomRight.x, m_pBottomRight.y); //Bottom Right

        glTexCoord2f(1, 1);
        glVertex2d(m_pTopRight.x, m_pTopRight.y); //Top Right

        glTexCoord2f(0, 1);
        glVertex2d(m_pTopLeft.x, m_pTopLeft.y); //Top Left
    glEnd();

    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    glPopAttrib();
}

截图: Screenshot

1 个答案:

答案 0 :(得分:1)

你已经有了解决方案,但是,嘿,有可能没有alpha通道,并使某种颜色透明。在参加Global Game Jam(48小时限量游戏比赛)时,我们需要快速为不同的对象制作很多精灵,而不使用任何复杂的工具。

我们实际上最终使用数码相机和窗户画(mspaint)。我们设置了一条规则,即图像的左上角必须始终包含透明色(因此透明色可以是艺术家选择的任何颜色)。加载图像时,根据透明色的出现设置alpha通道。虽然效果很好,但它仍然会在图像中留下一些透明的颜色泄漏(由于纹理过滤)。

/**
 *  @brief a simple raster image with fixed RGBA8 storage
 *
 *  The image data are always RGBA8. Alpha is stored in the most significant byte,
 *  followed by red, green and blue with decreasing significance.
 *
 *  The storage is very simple, each 32 bits in the buffer contains a single pixel,
 *  the first pixel is in top left corner, there is no scanline padding.
 */
struct TBmp {
    char n_former_bpp; /**< @brief former bpp, before conversion to RGBA8 */
    bool b_grayscale; /**< @brief grayscale flag (if set, the bitmap is assumed
        to contain grayscale image, stored as RGBA8) */
    bool b_alpha; /**< @brief alpha channel flag (if set, the alpha channel is significant;
        otherwise it's expected to be 0xff in all image pixels) */
    int n_width; /**< @brief image width, in pixels */
    int n_height; /**< @brief image height, in pixels */
    uint32_t *p_buffer; /**< @brief pointer to image data */
};

void TransparentColor_to_Alpha(TBmp *p_sprite, bool b_force_alpha_recalc = false)
{
    if(b_force_alpha_recalc || !p_sprite->b_alpha) {
        uint32_t n_transparent_color = p_sprite->p_buffer[0] & 0xffffff;
        // get transparent color from lower left corner

        for(int i = 0, n = p_sprite->n_width * p_sprite->n_height; i < n; ++ i) {
            uint32_t n_color = p_sprite->p_buffer[i];
            if(n_color == n_transparent_color)
                ;//p_sprite->p_buffer[i] = n_color; // do nothing, color is transparent and alpha is zero
            else if((n_color & 0xffffff) == n_transparent_color)
                p_sprite->p_buffer[i] = n_color & 0xffffff; // clear alpha
            else
                p_sprite->p_buffer[i] = n_color | 0xff000000U; // set alpha
        }
        // calculate alpha based on transparent color (binary only)

        p_sprite->b_alpha = true;
    }
    // build alpha channel using "transparent color"
}

为了从图像中去除透明色,我们编写了附加功能,可以复制边界像素的颜色,有效地从图像中删除透明色(可以这样做,因为透明度现在是alpha通道)。

bool Sprite_FloodEdgeColor(TBmp *p_sprite, int n_max_grow_step_num = 0)
{
    {
        uint32_t n_transparent_color = p_sprite->p_buffer[0] & 0xffffff;
        // get transparent color from lower left corner

        TBmp *p_clone;
        if(!(p_clone = p_sprite->p_Clone()))
            return false;
        // clone the bitmap

        uint32_t *p_buffer = p_sprite->p_buffer;
        uint32_t *p_buffer_pong = p_clone->p_buffer;
        for(int i = 0; !n_max_grow_step_num || i < n_max_grow_step_num; ++ i) {
            bool b_change = false;
            for(int y = 0, w = p_sprite->n_width, h = p_sprite->n_height; y < h; ++ y) {
                for(int x = 0; x < w; ++ x) {
                    if(p_buffer[x + w * y] == n_transparent_color) {
                        int n_neigh_rb = 0, n_neigh_g = 0;
                        int n_neigh_num = 0;

                        for(int sy = max(1, y) - 1, ey = min(y + 1, h - 1); sy <= ey; ++ sy) {
                            for(int sx = max(1, x) - 1, ex = min(x + 1, w - 1); sx <= ex; ++ sx) {
                                if(sx == x && sy == y)
                                    continue; // skip self (it's transparent anyway)
                                uint32_t n_neigh = p_buffer[sx + w * sy];
                                if(n_neigh != n_transparent_color) {
                                    n_neigh_rb += n_neigh & 0xff00ff;
                                    n_neigh_g += n_neigh & 0xff00;
                                    ++ n_neigh_num;
                                }
                            }
                        }
                        // gather neighbour colors

                        if(n_neigh_num > 2) {
                            int r = (n_neigh_rb & 0xffff0000) / n_neigh_num;
                            int g = n_neigh_g / n_neigh_num;
                            int b = (n_neigh_rb & 0xffff) / n_neigh_num;
                            uint32_t n_color = (0xff0000 & min(0xff0000, r)) |
                                (0xff00 & min(0xff00, g)) | (0xff & min(0xff, b));
                            // calculate average neighbor color

                            p_buffer_pong[x + w * y] = n_color;
                            b_change = true;
                        }
                    } else
                        p_buffer_pong[x + w * y] = p_buffer[x + w * y]; // just copy
                }
            }
            // grow 1px into transparent color

            if(b_change || p_buffer != p_sprite->p_buffer)
                std::swap(p_buffer, p_buffer_pong);
            // swap the buffers ...

            if(!b_change)
                break;
        }

        if(p_buffer != p_sprite->p_buffer) {
            memcpy(p_sprite->p_buffer, p_buffer,
                p_sprite->n_width * p_sprite->n_height * sizeof(uint32_t));
        }
        // in case the last result is not in

        p_clone->Delete();
        // cleanup
    }
    // bleed colors on edge into the transparent space (to enable hifi blending)

    return true;
}

差不多就是这样,但是我们使用数码相机拍摄的物体的照片通常在边缘处有更亮的像素,这对于玩家来说尤其令人不安。所以我们再编写一个函数,使用中值滤波器从边界中去除明亮的像素(而图像的其余部分不受影响)。

bool SpriteEdge_MedianFilter(TBmp *p_sprite,
    bool b_prefer_darker = true, bool b_5x5_median = true)
{
    {
        uint32_t n_transparent_color = p_sprite->p_buffer[0] & 0xffffff;
        // get transparent color from lower left corner

        TBmp *p_clone;
        if(!(p_clone = p_sprite->p_Clone()))
            return false;
        // clone the bitmap

        uint32_t *p_buffer = p_sprite->p_buffer;
        uint32_t *p_buffer_pong = p_clone->p_buffer;
        {
            const int n_off = (b_5x5_median)? 2 : 1;
            const int n_thresh = (b_5x5_median)? 25 : 9;

            bool b_change = false;
            for(int y = 0, w = p_sprite->n_width, h = p_sprite->n_height; y < h; ++ y) {
                for(int x = 0; x < w; ++ x) {
                    if(p_buffer[x + w * y] != n_transparent_color) {
                        uint32_t p_neigh_color[25];
                        int n_neigh_num = 0;

                        for(int sy = max(n_off, y) - n_off,
                           ey = min(y + n_off, h - 1); sy <= ey; ++ sy) {
                            for(int sx = max(n_off, x) - n_off,
                               ex = min(x + n_off, w - 1); sx <= ex; ++ sx) {
                                uint32_t n_neigh = p_buffer[sx + w * sy];
                                if(n_neigh != n_transparent_color) {
                                    p_neigh_color[n_neigh_num] = n_neigh;
                                    ++ n_neigh_num;
                                }
                            }
                        }
                        // gather neighbour colors (including self)

                        if(n_neigh_num < n_thresh) { // if the pixel is on the edge ...
                            uint32_t r[25], g[25], b[25];
                            for(int i = 0; i < n_neigh_num; ++ i) {
                                r[i] = p_neigh_color[i] & 0xff0000;
                                g[i] = p_neigh_color[i] & 0xff00;
                                b[i] = p_neigh_color[i] & 0xff;
                            }
                            std::sort(r, r + n_neigh_num);
                            std::sort(g, g + n_neigh_num);
                            std::sort(b, b + n_neigh_num);
                            // calculate median neighbor color

                            uint32_t n_self = p_buffer[x + w * y];
                            int mr, mg, mb;
                            if(b_prefer_darker) {
                                mr = min(r[n_neigh_num / 2], n_self & 0xff0000);
                                mg = min(g[n_neigh_num / 2], n_self & 0xff00);
                                mb = min(b[n_neigh_num / 2], n_self & 0xff);
                            } else {
                                mr = r[n_neigh_num / 2];
                                mg = g[n_neigh_num / 2];
                                mb = b[n_neigh_num / 2];
                            }
                            int a = n_self & 0xff000000U;

                            p_buffer_pong[x + w * y] = mr | mg | mb | a;
                            b_change = true;
                        }
                    } else
                        p_buffer_pong[x + w * y] = p_buffer[x + w * y]; // just copy
                }
            }
            // grow 1px into transparent color

            if(b_change || p_buffer != p_sprite->p_buffer)
                std::swap(p_buffer, p_buffer_pong);
            // swap the buffers ...
        }

        if(p_buffer != p_sprite->p_buffer) {
            memcpy(p_sprite->p_buffer, p_buffer,
                p_sprite->n_width * p_sprite->n_height * sizeof(uint32_t));
        }
        // in case the last result is not in

        p_clone->Delete();
        // cleanup
    }

    return true;
}

我们实际上写了另外一个会侵蚀图像不透明部分的函数,有效地使精灵选择的像素数量更小,并且在无法使用中值函数移除它们的情况下移除有问题的区域。这就是它,尽管它是在大约一个小时内编写的,但它几乎是创建quick'n'dirty精灵的终极工具。

获取full source code