来自libpng的加载的png纹理在OpenGL中显示为黑色

时间:2014-01-09 20:46:09

标签: c++ opengl textures shader libpng

我目前正在尝试使用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);

2 个答案:

答案 0 :(得分:0)

如果你喜欢,可以尝试一下吗?我很久以前写过它..

#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();
}

答案 1 :(得分:0)

首先,将glTexImage2D和glTexSubImage2D的params转储到调试输出,这样就可以验证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

另外,一般来说,除非你有充分的理由不这样做,你应该为纹理格式指定GL_RGBA8(glTexImage2D中的第三个参数),但几乎肯定不是图像数据格式(第3到最后一个参数)在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
}