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

3
我正在将一些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":

这里输入图片描述

#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个回答

4
看到你使用索引绘制一个矩形的三角形扇形,让我感到困惑,实际上这是你的问题。你的索引数组看起来像是要绘制两个索引三角形而不是单个三角形扇形。因此你用6个顶点来绘制三角形扇形,从而得到4个三角形,这意味着会出现一些额外的三角形在你的其他两个三角形之后被重复绘制,从而导致较暗的区域。
所以最简单的解决方案就是将 GL_TRIANGLE_STRIP 改为 GL_TRIANGLES,但也许需要稍微重新排列一下你的顶点/索引,否则你将按顺时针顺序绘制你的三角形,而你1.3版本的矩形示例使用的是逆时针顺序(也许在你的情况下这是无关紧要的,但这本质上是错误的方法,永远不要忽略你的排序)。
但你知道吗,对于两个三角形的三角形扇形,只需要4个顶点就可以了。所以根本没有必要使用任何索引数组,只需要使用老式的 glDrawArrays 按顺序绘制顶点即可。但需要稍微重新排列一下它们的顺序(三角形扇形使用Z字形图案,所以按从左到右、从上到下的顺序排列)。
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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接