使用顶点缓冲对象渲染的四边形纹理只有一半透明

时间:2013-02-28 08:35:23

标签: opengl opengl-es textures vbo opengl-es-1.1

我正在将一些代码从OpenGL 1.3转换为OpenGL ES 1.1。这是一款2D游戏,所以它主要归结为将纹理渲染到四边形上。 OpenGL ES中没有立即模式,所以我不得不使用顶点缓冲对象。

但似乎构成每个四边形的两个三角形中只有一个处理透明度。这是一个截图:

enter image description here

以下是我现在渲染纹理四边形的方法,这会导致:

glBindTexture2D(GL_TEXTURE_2D, id);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

const GLfloat texture_coordinates[] = {0, 0,
                                       0, 1,
                                       1, 1,
                                       1, 0};
glTexCoordPointer(2, GL_FLOAT, 0, texture_coordinates);

const GLfloat vertices[] = {0, 0,
                            0, height,
                            width, height,
                            width, 0};
glVertexPointer(2, GL_FLOAT, 0, vertices);

const GLubyte indices[] = {0, 1, 2,
                           0, 2, 3};
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_BYTE, indices);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

以下是我过去使用立即模式渲染纹理四边形的方法,它可以正常工作:

glBindTexture2D(GL_TEXTURE_2D, id);

glBegin(GL_QUADS);

glTexCoord2i(0, 0);
glVertex2i(0, 0);

glTexCoord2i(1, 0);
glVertex2i(width, 0);

glTexCoord2i(1, 1);
glVertex2i(width, height);

glTexCoord2i(0, 1);
glVertex2i(0, height);

glEnd();

以下是重现此问题的示例程序。

你可以在Linux上编译它:

g++ `pkg-config --cflags --libs sdl gl libpng` reproduce.cpp

在Mac OS X上就像这样:

clang++ -framework OpenGL `pkg-config --cflags --libs sdl libpng` reproduce.cpp

这是一个512x256透明PNG图像,您可以另存为“transparent.png”:

enter image description here

#include <cmath>
#include <cstdio>
#include <iostream>
#include <png.h>
#include <SDL.h>
#include <SDL_main.h>
#include <SDL_opengl.h>
#include <stdexcept>
#include <sstream>

#define USE_VBO 1

struct Pixmap {
    int width;
    int height;
    const unsigned char* data;
    GLenum format;
};

Pixmap load_png(const std::string& path)
{
    FILE* const file = fopen(path.c_str(), "rb");
    if (!file)
        throw std::runtime_error("Unable to open " + path);

    png_byte header[8];
    fread(header, 1, 8, file);
    const bool is_png = !png_sig_cmp(header, 0, 8);
    if (!is_png)
        throw std::runtime_error(path + " is not a PNG");

    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                             NULL, NULL, NULL);
    if (!png)
        throw std::runtime_error("Failed to create png struct");

    png_infop info = png_create_info_struct(png);
    if (!info) {
        png_destroy_read_struct(&png, (png_infopp) NULL, (png_infopp) NULL);
        throw std::runtime_error("Failed to create png info struct");
    }

    png_infop info_end = png_create_info_struct(png);
    if (!info_end) {
        png_destroy_read_struct(&png, &info, (png_infopp) NULL);
        throw std::runtime_error("Failed to create png info struct");
    }

    if (setjmp(png_jmpbuf(png))) {
        png_destroy_read_struct(&png, &info, &info_end);
        throw std::runtime_error("Error from libpng");
    }

    png_init_io(png, file);
    png_set_sig_bytes(png, 8);
    png_read_info(png, info);

    int bit_depth;
    int color_type;
    png_uint_32 image_width, image_height;
    png_get_IHDR(png, info, &image_width, &image_height, &bit_depth,
                 &color_type, NULL, NULL, NULL);

    png_read_update_info(png, info);

    GLenum format;
    switch (color_type) {

    case PNG_COLOR_TYPE_RGBA:
        format = GL_RGBA;
        break;
    case PNG_COLOR_TYPE_RGB:
        format = GL_RGB;
        break;
    default:
        png_destroy_read_struct(&png, &info, &info_end);
        std::ostringstream message_stream;
        message_stream << "Unsupported PNG color type: " << color_type;
        throw std::runtime_error(message_stream.str());
    }

    const int row_bytes = png_get_rowbytes(png, info);
    png_byte* image_data = new png_byte[row_bytes * image_height];
    png_bytep* row_pointers = new png_bytep[image_height];
    for (unsigned int i = 0; i < image_height; i++)
        row_pointers[i] = image_data + i * row_bytes;

    png_read_image(png, row_pointers);

    png_destroy_read_struct(&png, &info, &info_end);
    delete[] row_pointers;
    fclose(file);

    Pixmap pixmap;
    pixmap.width = image_width;
    pixmap.height = image_height;
    pixmap.data = image_data;
    pixmap.format = format;
    return pixmap;
}

GLuint create_texture(Pixmap pixmap)
{
    GLuint id;
    glGenTextures(1, &id);
    glBindTexture(GL_TEXTURE_2D, id);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, pixmap.format, pixmap.width,
                 pixmap.height, 0, pixmap.format, GL_UNSIGNED_BYTE,
                 pixmap.data);
    return id;
}

void draw_texture(const GLuint id, const int width, const int height)
{
    glBindTexture(GL_TEXTURE_2D, id);

#if USE_VBO
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    const GLfloat texture_coordinates[] = {0, 0,
                                           0, 1,
                                           1, 1,
                                           1, 0};
    glTexCoordPointer(2, GL_FLOAT, 0, texture_coordinates);

    const GLfloat vertices[] = {0, 0,
                                0, height,
                                width, height,
                                width, 0};
    glVertexPointer(2, GL_FLOAT, 0, vertices);

    const GLubyte indices[] = {0, 1, 2,
                               0, 2, 3};
    glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_BYTE, indices);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
#else
    glBegin(GL_QUADS);

    glTexCoord2i(0, 0);
    glVertex2i(0, 0);

    glTexCoord2i(1, 0);
    glVertex2i(width, 0);

    glTexCoord2i(1, 1);
    glVertex2i(width, height);

    glTexCoord2i(0, 1);
    glVertex2i(0, height);

    glEnd();
#endif
}

int main(int argc, char* argv[])
{
    const int width = 512;
    const int height = 256;

    SDL_Init(SDL_INIT_VIDEO);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_SetVideoMode(width, height, 0, SDL_OPENGL);

    glEnable(GL_TEXTURE_2D);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, height, 0, -1, 1);
    glMatrixMode(GL_MODELVIEW);

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

    try {
        Pixmap pixmap = load_png("transparent.png");
        GLuint texture = create_texture(pixmap);
        draw_texture(texture, pixmap.width, pixmap.height);
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    SDL_GL_SwapBuffers();

    SDL_Event event;
    for (;;) {
        SDL_WaitEvent(&event);
        if (event.type == SDL_QUIT)
            return 0;
    }

    return 0;
}

1 个答案:

答案 0 :(得分:4)

看到你使用带有简单三角形条的索引绘图让我觉得奇怪,事实上这是你的问题。您的索引数组看起来像要绘制两个索引三角形而不是单个三角形条。因此,您绘制一个带有6个顶点的四角形条带,从而绘制出4个三角形,这意味着额外的三角形以某种方式包裹在您的另外两个之后并导致双重绘制,从而导致更暗的部分。

所以最简单的解决方案是将GL_TRIANGLE_STRIP更改为GL_TRIANGLES,但可能会稍微重新排序顶点/索引,否则您按顺时针顺序绘制三角形,而1.3 quad示例使用逆时针顺序(可能这与你的情况无关,但这首先是错误的做法,永远不要忽视你的订购)。

但你知道吗,它是两个三角形的三角形条带,只需要4个顶点。因此根本不需要任何索引数组,只需使用旧的glDrawArrays并按顺序绘制顶点。但重新排序它们(三角形条带使用锯齿形图案,因此从左到右,从上到下排序):

const GLfloat texture_coordinates[] = {0, 1,
                                       0, 0,
                                       1, 1,
                                       1, 0};
glTexCoordPointer(2, GL_FLOAT, 0, texture_coordinates);

const GLfloat vertices[] = {0, height,
                            0, 0,
                            width, height,
                            width, 0};
glVertexPointer(2, GL_FLOAT, 0, vertices);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);