Текстуру на квад, оказываемых используя буфер вершин объектов только половина прозрачный


Я преобразую некоторый код из OpenGL 1.3 в OpenGL ES 1.1. Это 2D-игра, поэтому она в основном сводится к рендерингу текстур на квадроциклах. В OpenGL ES нет немедленного режима, поэтому мне пришлось использовать объекты vertex buffer.

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

Введите описание изображения здесь

Вот как я рендерю текстурированные квадроциклы прямо сейчас, что приводит к этому:

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

Вот прозрачное PNG-изображение 512x256, которое вы можете сохранить как "прозрачный.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 3

1 ответ:

Видя, что вы используете индексированный рисунок с простой треугольной полосой для квадрата, я задался вопросом, и на самом деле это ваша проблема. Ваш массив индексов выглядит так, как будто вы хотите нарисовать два индексированных треугольника, а не одну полосу треугольника. Итак, вы рисуете полосу треугольника с 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);