2

I am currently attempting to load a png image using libpng, and then convert it into an OpenGL texture that is then drawn.

Now, first of all, to do a quick test to see if any textures would get drawn a simple test quad, I made this texture:

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);

Just a white texture that is then drawn on my quad. This works perfectly. Therefore it seems to indicate that the fragment shader and vertex shader is working correctly, as well as the rest of my drawing logic.

For anyone interested, this is the main loop that does the drawing using a texture:

    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);

Now, the function I use to load an OpenGL texture from a png using libpng is this one: OpenGL Texture Load Function

The reason I've provided a link is so that this post does not become impossible to read :)

Basically, the part of my code where I load the texture and bind it is this:

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);

This happens right before the main loop.

It's also worth noting that I've done extensive debugging using the glGetError function, and it returns no errors at all. Additionally, I've stepped through the png_texture_load function and it returns the OpenGL texture successfully.

Also, the image I'm loading is a power of 2. It has dimensions of 64x64.

I just seem to be at a complete loss as to why, then, it's just a black texture when my own text texture shows up just fine?

EDIT

This is the part of the libpng image function that creates a texture from the loaded data:

    // 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);
CodingBeagle
  • 1,888
  • 2
  • 24
  • 52
  • I actually don't have a glTexSubImage for the image! I just tried adding it, but it didn't seem to change anything. I have edited the original question with the addition of glTexSubImage. – CodingBeagle Jan 09 '14 at 21:01
  • Maybe change a few bytes in your 4x4 test texture just to be sure. – Brett Hale Jan 09 '14 at 21:44
  • I already did this :) changed a few bytes to see if any color would show, which it did. I should have mentioned that, but thanks for the suggestion – CodingBeagle Jan 09 '14 at 22:13
  • White is a terrible test image for a texture, because an improperly loaded OpenGL texture object will default to produce white pixels. – datenwolf Jan 09 '14 at 22:37

2 Answers2

0

Can try this if you like? I wrote it a very long time ago..

#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();
}
Brandon
  • 22,723
  • 11
  • 93
  • 186
0

First, dump your params for glTexImage2D and glTexSubImage2D to debug output so you can verify that temp_width, temp_height, image_data and format all contain sensible values. The problem could lie in your OpenGL usage, but it could just as easily lie in your libpng usage, so you need to check both. How are you setting format, temp_width and temp_height and what are their values when you enter this code?

Next, why are you calling two texture loading functions?

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);

Both of these should have the same effect, so I don't understand why you're calling them both. If you only intend the first call to be allocating storage, you should be passing 0 as the final parameter, or calling glTexStorage2d.

Also, generally speaking, unless you have a good reason for doing otherwise, you should be specifying GL_RGBA8 for the texture format (the third param in glTexImage2D), but almost certainly not in the image data format (the 3rd to last param in both glTexImage2D and glTexSubImage2D), so I'm fairly suspicious of that glTexImage2D call that uses format in both places.

Finally, have you tried calling glGetError after the various calls to see if any of them are generating errors? glGetError can be an expensive call so it's usually a good idea to wrap it in a pre-processor macro that you can easily disable for release or performance testing builds

I have a macro GL_CHECK_ERROR which calls an inline function defined like this

static inline void check() {
#ifdef GL_DEBUG
  GLenum errorCode = glGetError();
  if (errorCode != 0) {
    throw error(errorCode);
  }
#endif
}
Jherico
  • 28,584
  • 8
  • 61
  • 87