【问题标题】:How to use NPOT images as textures with libpng and OpenGL ES 1.1?如何使用 NPOT 图像作为带有 libpng 和 OpenGL ES 1.1 的纹理?
【发布时间】:2012-12-18 16:09:12
【问题描述】:

我正在尝试使用 libpng 1.5 将 NPOT 大小的 PNG 图像用作 OpenGL ES 1.1 中的纹理(所以没有 GL_arb_texture_rectangle)。使用 SDL,我可以将 NPOT 图像粘贴到 NPOT 纹理上,但我不知道如何使用 libpng 做类似的事情。

当我使用原始纹理 (1280x720) 时,我得到的只是一个白色表面。当我在文件系统上将其调整为 1024x512 时,它显示正常。

由于某种原因,NPOT 纹理适用于带有-gpu on 的 4.2 AVD。

这是代码。几乎是 this,但有一些更改是使用 libzip 从 APK 中读取,而不是直接使用 fopening。

void png_zip_read(png_structp png, png_bytep data, png_size_t size)
{
    zip_file* file = static_cast<zip_file*>(png_get_io_ptr(png));
    zip_fread(file, data, size);
}

GLuint load_png_texture(const std::string& path, unsigned int& width,
                        unsigned int& height)
{
    if (!apk_path.length())
        throw "APK Path not set";

    zip* apk = zip_open(apk_path.c_str(), 0, NULL);
    if (!apk)
        throw "Error loading APK";

    zip_file* file = zip_fopen(apk, path.c_str(), 0);
    if (file == 0)
        throw path + ": " + strerror(errno);

    png_byte header[8];
    zip_fread(file, header, 8);
    if (png_sig_cmp(header, 0, 8)) {
        zip_fclose(file);
        zip_close(apk);
        throw path + " is not a PNG";
    }

    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                             NULL, NULL, NULL);
    if (!png) {
        zip_fclose(file);
        zip_close(apk);
        throw "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);
        zip_fclose(file);
        zip_close(apk);
        throw "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);
        zip_fclose(file);
        zip_close(apk);
        throw "Failed to create png info struct";
    }

    if (setjmp(png_jmpbuf(png))) {
        png_destroy_read_struct(&png, &info, &info_end);
        zip_fclose(file);
        zip_close(apk);
        throw "Error from libpng";
    }

    png_set_read_fn(png, file, png_zip_read);
    png_set_sig_bytes(png, 8);
    png_read_info(png, info);

    int bit_depth, color_type;
    unsigned int temp_width, temp_height;
    png_get_IHDR(png, info, &temp_width, &temp_height, &bit_depth,
                 &color_type, NULL, NULL, NULL);
    width = temp_width;
    height = temp_height;

    png_read_update_info(png, info);

    int row_bytes = png_get_rowbytes(png, info);
    png_byte* image_data = new png_byte[row_bytes * height];
    if (!image_data) {
        png_destroy_read_struct(&png, &info, &info_end);
        zip_fclose(file);
        zip_close(apk);
        throw "Could not allocate memory for PNG image data";
    }

    png_bytep* row_pointers = new png_bytep[height];
    if (!row_pointers) {
        png_destroy_read_struct(&png, &info, &info_end);
        delete[] image_data;
        zip_fclose(file);
        zip_close(apk);
        throw "Could not allocate memory for PNG row pointers";
    }

    for (int i = 0; i < height; i++)
        row_pointers[height - 1 - i] = image_data + i * row_bytes;

    png_read_image(png, row_pointers);

    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    GLenum format = GL_RGBA;
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
                 format, GL_UNSIGNED_BYTE, image_data);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    png_destroy_read_struct(&png, &info, &info_end);
    delete[] image_data;
    delete[] row_pointers;
    zip_fclose(file);
    zip_close(apk);
    return texture;
}

【问题讨论】:

    标签: android opengl-es textures libpng opengl-es-1.1


    【解决方案1】:

    经过更多的尝试和错误,我回到疯狂的谷歌搜索中,终于找到了a code sample,这正是我想要的。

    秘诀基本上就是这样创建 OpenGL 纹理:

    int texture_width = next_power_of_two(width);
    int texture_height = next_power_of_two(height);
    
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
    if (texture_width != width || texture_height != height) {
        glTexImage2D(GL_TEXTURE_2D, 0, format, texture_width,
                     texture_height, 0, format, GL_UNSIGNED_BYTE, NULL);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format,
                        GL_UNSIGNED_BYTE, image_data);
    } else
        glTexImage2D(GL_TEXTURE_2D, 0, format, texture_width,
                     texture_height, 0, format, GL_UNSIGNED_BYTE,
                     image_data);
    

    要以原始尺寸渲染图像,x/y 纹理坐标必须按如下方式计算(而不是仅使用1):

    float x_coordinate = (width - 0.5f) / texture_width;
    float y_coordinate = (height - 0.5f) / texture_height;
    

    【讨论】:

      【解决方案2】:

      ES 1.1 正式不支持没有扩展的 NPOT 纹理。它可能会意外地在某些实现上起作用,但这只是“运气”。

      通常的方法是将您的 NPOT 图像打包在较大的 POT 纹理中,并相应地调整纹理坐标。您可以在运行时以编程方式执行此操作(“打包”),或者只是在工具链中执行此操作(很可能是您的艺术程序)。

      【讨论】:

      • 你能给我一个关于如何用 libpng 进行打包的提示吗?我真的不知道从哪里开始。我可以将透明像素推入 image_data 吗?还是建议我只创建一个精灵表(或纹理图集,AFAIK 一样)并将其保存为单个 POT 图像?
      • 是的,我建议使用美术工具或现有的纹理打包器(如codeandweb.com/texturepacker)来完成,而不是在代码中完成。你以后总是可以运行自己的东西。是的,“sprite sheet”和“texture atlas”是同一个概念。
      • 我已经阅读了更多关于纹理图集的内容。似乎由于硬件限制,我将无法使用单个图集,所以我认为我宁愿每个纹理都坚持一个图像,直到出现性能问题。你知道我如何在运行时使用 libpng 以编程方式打包图像吗?
      • 您实际上不会为此使用 libpng。 Libpng 为您提供原始的 32 位 RGBA 数据。您只需将其(逐行,使用右偏移)复制到稍大(POTxPOT)数据块的角落。然后您使用 glTexImage2D 上传,并更正您的纹理坐标(1 变为 NPOTsize/POTsize)。
      猜你喜欢
      • 2010-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-16
      • 1970-01-01
      • 1970-01-01
      • 2019-02-17
      • 1970-01-01
      相关资源
      最近更新 更多