0
\$\begingroup\$

I am creating a game using OpenGL that has many textured cubes. I am trying to use an Array Texture to load multiple textures from one file. I tried to setup my texture atlas; however, all that renders is a black texture. I'm not sure what exactly I am doing wrong.

Here is the texture loading code:

    // Load image
    stbi_set_flip_vertically_on_load(true);
    unsigned char *data = stbi_load(path, &width, &height, &nrChannels, 0);
    // Generate texture object
    glActiveTexture(GL_TEXTURE0);
    glGenTextures(1, &textureId);

    glBindTexture(GL_TEXTURE_2D_ARRAY, textureId);

    // Actually load texture
    if (data) {
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, width, height, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        //glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
    } else {
        std::cout << "Failed to load " << path << " texture" << std::endl;
    }

    // set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // Free memory
    stbi_image_free(data);

Here is my fragment shader

#version 330 core
out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2DArray my_sampler;

void main()
{
    FragColor = texture(my_sampler, vec3(0, 0, 1)) * vec4(1.0f);
}

Here is my texture file:

enter image description here

Does anyone know how I can solve my issue?

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

That's just not how array textures work. You can't load a texture atlas as an array texture; how would OpenGL know where one array layer ends and the next begins?

2D array textures have 3 dimension: width, height, and number of layers

Also, you're getting a blank texture because your glTexImage3d call passes zero for the number of layers. Which produced an OpenGL error that you didn't notice.

But more than that, 2D array textures are sequences of 2D images, not a single 2D image with subrectangles in it. So you are expected to upload pixel data with "number of layers" worth of separate 2D images.

So you need to readjust your texture data. You have to break it up into distinct images, and then transfer them into the array texture (either all at once in a big 3D buffer, or each one individually with multiple glTexSubImage3d calls).

\$\endgroup\$
1
  • \$\begingroup\$ Thank you, I understand array textures much better now and got it working! \$\endgroup\$
    – Flibio
    Jun 10, 2018 at 14:15
0
\$\begingroup\$

First thing you wanna do when things don't work in OpenGL (or any API really): check whether some operation raised an error. In OpenGL, this is done by calling glGetError().

Starting with OpenGL 4.3, there's also a mechanism to get debug messages from the graphics driver, which will give you more detailed information about what's going on and, particularly, what's going wrong. You need to have a debug context to get debug messages. If you do not handle context creation yourself but use a library such as, e.g., GLUT or GLFW, consult the documentation of that library to find out how to have it create a debug context for you (may not be supported by all such libraries). Assuming you have a debug context, you can register callbacks to receive debug messages. For example (APIENTRY is needed on Windows):

void APIENTRY OpenGLDebugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
    std::cout << message << std::endl;
}

// ...

glDebugMessageCallback(OpenGLDebugOutputCallback, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);

Now to your actual problem: My first guess would be that glTexImage3D() simply doesn't do anything because you specify a depth of 0, which means your array texture image has no layers. Apart from that, it should probably be pointed out that your fragment shader ignores the texture coordinates and will simply sample the lower left corner of layer index 1 (also: layer indices start at 0). Finally, I'm not sure what exactly you are planning to use an array texture for. Your image looks like a traditional texture atlas, which you would usually store in a normal 2D texture and select subregions from via texture coordinates. If you want to put subregions of this larger image into separate layers of an array texture, you will generally have to make sure to provide the data to OpenGL accordingly, i.e., one image per layer.

Some other notes concerning your code: I would suggest to be explicit and use a sized internal format such as, e.g., GL_RGBA8 rather than a generic base format like GL_RGBA. Assuming that your image data is sRGB encoded (seems most likely as it comes from a file), I would suggest using GL_SRGB8_ALPHA8 as internal format. In this case, you will most likely also want to glEnable(GL_FRAMEBUFFER_SRGB); to get the colors right. Furthermore, you probably want to at least make sure that the number of channels in the data loaded from the file using stbi_load() actually matches the input data format you specify in your call to glTexImage3D().

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .