OpenGL / Qt: прозрачность текстур не работает - PullRequest
1 голос
/ 06 ноября 2011

У меня огромная проблема с отображением текстур в 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 Ответ

1 голос
/ 07 февраля 2012

У вас уже есть решение, но, эй, можно обойтись без альфа-канала и сделать определенный цвет прозрачным.Принимая участие в Global Game Jam (48-часовой ограниченный игровой конкурс), нам нужно было быстро создать множество спрайтов для различных объектов без использования каких-либо сложных инструментов.

В действительности мы использовали цифровую камеру и окна.краска (мспейнт).Мы установили правило, согласно которому верхний левый угол изображения должен всегда содержать прозрачный цвет (поэтому прозрачный цвет может быть практически любым цветом, выбранным художником).Когда изображение было загружено, альфа-канал был установлен в соответствии с появлением прозрачного цвета.Несмотря на то, что это работало хорошо, все же оставались некоторые утечки прозрачного цвета в изображение (благодаря фильтрации текстур).

/**
 *  @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"
}

Чтобы удалить прозрачный цвет с изображения, мы написали дополнительную функцию, которая быдублирующий цвет граничных пикселей, эффективно стирающий прозрачный цвет с изображения (это можно сделать, потому что прозрачность теперь находится в альфа-канале).

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;
}

На самом деле мы написали еще одну функцию, которая могла бы разрушитьнепрозрачные части изображения, эффективно уменьшая спрайт на определенное количество пикселей и удаляя проблемные области в случае, если их нельзя было удалить с помощью медианной функции.Вот и все, хотя он был написан примерно через час, но это в значительной степени идеальный инструмент для создания быстрых и грязных спрайтов.

Получение полного исходного кода .

...