【问题标题】:Loaded png textures from libpng shows up black in OpenGL从 libpng 加载的 png 纹理在 OpenGL 中显示为黑色
【发布时间】:2014-01-28 15:00:28
【问题描述】:

我目前正在尝试使用 libpng 加载 png 图像,然后将其转换为 OpenGL 纹理,然后进行绘制。

现在,首先,做一个快速测试,看看是否有任何纹理会被绘制一个简单的测试四边形,我制作了这个纹理:

glGenTextures(1, &textureHandle);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureHandle);

glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, 4, 4);

const GLubyte myTexture[] = {0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF,
                             0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF,
                             0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF,
                             0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF};

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_RGB, GL_UNSIGNED_BYTE, myTexture);

只是一个白色纹理,然后在我的四边形上绘制。这完美地工作。因此,它似乎表明片段着色器和顶点着色器以及我的其他绘图逻辑工作正常。

对于任何感兴趣的人,这是使用纹理进行绘图的主循环:

    glClearColor(1, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(theProgram);

    GLuint samplerLoc = glGetUniformLocation(theProgram, "tex");

    if (samplerLoc == -1)
        printf("Location name could not be found...\n");

    glUniform1i(samplerLoc, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

    glEnableVertexAttribArray(0);

    glEnableVertexAttribArray(1);

    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(16 * sizeof(float)));

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

现在,我使用 libpng 从 png 加载 OpenGL 纹理的函数是:OpenGL Texture Load Function

我提供链接的原因是为了让这篇文章不会变得无法阅读:)

基本上,我加载纹理并绑定它的代码部分是这样的:

GLuint myImage = png_texture_load("niks.png", &width, &height);    

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, myImage);

GLuint mySampler;

glGenSamplers(1, &mySampler);

glBindSampler(0, mySampler);

这发生在主循环之前。

还值得注意的是,我已经使用 glGetError 函数进行了广泛的调试,并且它根本没有返回任何错误。此外,我已经逐步完成了 png_texture_load 函数,它成功返回了 OpenGL 纹理。

另外,我正在加载的图像是 2 的幂。它的尺寸为 64x64。

我似乎完全不知道为什么,当我自己的文本纹理显示得很好时,它只是一个黑色纹理?

编辑

这是从加载的数据创建纹理的 libpng 图像函数的一部分:

    // Generate the OpenGL texture object
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, format, temp_width, temp_height, 0, format, GL_UNSIGNED_BYTE, image_data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, temp_width, temp_height, GL_RGB, GL_UNSIGNED_BYTE, image_data);

【问题讨论】:

  • 我实际上没有图像的 glTexSubImage !我只是尝试添加它,但它似乎没有改变任何东西。我通过添加 glTexSubImage 编辑了原始问题。
  • 可能会更改 4x4 测试纹理中的几个字节以确保确定。
  • 我已经这样做了 :) 更改了几个字节以查看是否会显示任何颜色,确实如此。我应该提到这一点,但感谢您的建议
  • 白色对于纹理来说是一个糟糕的测试图像,因为加载不当的 OpenGL 纹理对象将默认生成白色像素。

标签: c++ opengl textures shader libpng


【解决方案1】:

首先,将 glTexImage2D 和 glTexSubImage2D 的参数转储到调试输出,以便验证 temp_width、temp_height、image_data 和 format 是否都包含合理的值。问题可能在于您的 OpenGL 使用情况,但也很容易在于您的 libpng 使用情况,因此您需要同时检查两者。您如何设置formattemp_widthtemp_height,当您输入此代码时它们的值是多少?

接下来,为什么要调用两个纹理加载函数?

glTexImage2D(GL_TEXTURE_2D, 0, format, temp_width, temp_height, 0, format, GL_UNSIGNED_BYTE, image_data);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, temp_width, temp_height, GL_RGB, GL_UNSIGNED_BYTE, image_data);

这两个应该具有相同的效果,所以我不明白你为什么要同时调用它们。如果您只打算第一次调用分配存储,则应将 0 作为最终参数传递,或调用 glTexStorage2d

此外,一般来说,除非您有充分的理由这样做,否则您应该为纹理格式(glTexImage2D 中的第三个参数)指定 GL_RGBA8,但几乎可以肯定不是图像数据格式(倒数第三个参数)在 glTexImage2D 和 glTexSubImage2D 中),所以我对在两个地方都使用格式的 glTexImage2D 调用相当怀疑。

最后,您是否尝试在各种调用之后调用glGetError 以查看其中是否有任何产生错误? glGetError 可能是一个昂贵的调用,因此将它包装在一个预处理器宏中通常是个好主意,您可以轻松地禁用它以进行发布或性能测试构建

我有一个宏 GL_CHECK_ERROR 调用这样定义的内联函数

static inline void check() {
#ifdef GL_DEBUG
  GLenum errorCode = glGetError();
  if (errorCode != 0) {
    throw error(errorCode);
  }
#endif
}

【讨论】:

    【解决方案2】:

    如果你愿意,可以试试这个吗?很久以前写的。。

    #include <fstream>
    #include <stdexcept>
    #include <cstdint>
    
    typedef union RGBA
    {
        std::uint32_t Colour;
        struct
        {
            std::uint8_t R, G, B, A;
        };
    } *PRGB;
    
    void ReadFromStream(png_structp PngPointer, std::uint8_t* Data, std::size_t Length)
    {
        std::ifstream* Stream = reinterpret_cast<std::ifstream*>(png_get_io_ptr(PngPointer));
        Stream->read(reinterpret_cast<char*>(Data), Length);
    }
    
    void LoadPngImage(const char* FilePath, std::vector<RGBA> &Pixels, std::uint32_t &width, std::uint32_t &height, std::uint16_t &BitsPerPixel)
    {
        Pixels.clear();
        std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
    
        if (!hFile.is_open())
        {
            throw std::invalid_argument("File Not Found.");
        }
    
        std::uint8_t Header[18] = {0};
        hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
        hFile.seekg(8, std::ios::beg);
    
        if (png_sig_cmp(Header, 0, 8))
        {
            hFile.close();
            throw std::invalid_argument("Error: Invalid File Format. Required: Png.");
        }
    
        png_structp PngPointer = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
        if (!PngPointer)
        {
            hFile.close();
            throw std::runtime_error("Error: Cannot Create Read Structure.");
        }
    
        png_infop InfoPointer = png_create_info_struct(PngPointer);
        if (!InfoPointer)
        {
            hFile.close();
            png_destroy_read_struct(&PngPointer, nullptr, nullptr);
            throw std::runtime_error("Error: Cannot Create InfoPointer Structure.");
        }
    
        png_infop EndInfo = png_create_info_struct(PngPointer);
        if (!EndInfo)
        {
            hFile.close();
            png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
            throw std::runtime_error("Error: Cannot Create EndInfo Structure.");
        }
    
        if (setjmp(png_jmpbuf(PngPointer)))
        {
            hFile.close();
            png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
            throw std::runtime_error("Error: Cannot Set Jump Pointer.");
        }
    
        png_set_sig_bytes(PngPointer, 8);
        png_set_read_fn(PngPointer, &hFile, ReadFromStream);
        png_read_info(PngPointer, InfoPointer);
    
        channels = png_get_channels(PngPointer, InfoPointer);
        png_get_IHDR(PngPointer, InfoPointer, &width, &height, &bitdepth, &colortype, &interlacetype, nullptr, nullptr);
        png_set_interlace_handling(PngPointer);
        png_set_strip_16(PngPointer);
        png_set_packing(PngPointer);
    
        switch (colortype)
        {
            case PNG_COLOR_TYPE_GRAY:
                {
                    png_set_expand_gray_1_2_4_to_8(PngPointer);
                    png_set_gray_to_rgb(PngPointer);
                    png_set_bgr(PngPointer);
                    break;
                }
    
            case PNG_COLOR_TYPE_PALETTE:
                {
                    png_set_palette_to_rgb(PngPointer);
                    if (png_get_valid(PngPointer, InfoPointer, PNG_INFO_tRNS))
                        png_set_tRNS_to_alpha(PngPointer);
                    else
                        png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER);
                    png_set_bgr(PngPointer);
                    BitsPerPixel = 24;
                    break;
                }
    
            case PNG_COLOR_TYPE_GRAY_ALPHA:
                {
                    png_set_gray_to_rgb(PngPointer);
                    break;
                }
    
            case PNG_COLOR_TYPE_RGB:
                {
                    png_set_bgr(PngPointer);
                    png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER);
                    BitsPerPixel = 24;
                    break;
                }
    
            case PNG_COLOR_TYPE_RGBA:
                {
                    png_set_bgr(PngPointer);
                    BitsPerPixel = 32;
                    break;
                }
    
            default:
                png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
                throw std::runtime_error("Error: Png Type not supported.");
                break;
        }
    
        png_read_update_info(PngPointer, InfoPointer);
        channels = png_get_channels(PngPointer, InfoPointer);
    
        int bitdepth, colortype, interlacetype;
        png_get_IHDR(PngPointer, InfoPointer, &width, &height, &bitdepth, &colortype, &interlacetype, nullptr, nullptr);
    
        Pixels.resize(width * height);
        std::vector<std::uint8_t*> RowPointers(height);
        std::uint8_t* BuffPos = reinterpret_cast<std::uint8_t*>(Pixels.data());
    
        for (size_t I = 0; I < height; ++I)
        {
            RowPointers[I] = BuffPos + (I * width * sizeof(RGBA));
        }
    
        png_read_image(PngPointer, RowPointers.data());
        png_read_end(PngPointer, InfoPointer);
        png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
        hFile.close();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-14
      • 1970-01-01
      • 2014-05-05
      • 1970-01-01
      • 2018-09-17
      • 1970-01-01
      • 2023-02-03
      相关资源
      最近更新 更多