3

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

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

在此处输入图像描述

这是我现在渲染纹理四边形的方式,这导致:

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

1 回答 1

4

Seeing you use indexed drawing with a simple triangle strip for a quad made me wonder, and in fact this is your problem. Your index array looks like you want to draw two indexed triangles and not a single triangle strip. So you draw a triangle strip with 6 vertices and thus 4 triangles, which means additional triangles that wrap somehow behind your other two and cause double drawing, which then results in the darker parts.

So the easiest solution would be to change GL_TRIANGLE_STRIP to GL_TRIANGLES, but maybe reorder your vertices/indices a bit, as otherwise you are drawing your triangles in clockwise order, whereas your 1.3 quad example uses counter-clockwise order (it may be that this is irrelevant in your case, but that would be the wrong approach in the first place, never ignore your ordering).

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

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);
于 2013-02-28T09:33:02.837 回答