Search code examples
opengltexturestexture-mappingtexture2d

How to generate a 2D texture into a 1D buffer and load it in OpenGL?


For some context, I'm getting real-time image data from a camera in a form of 1D binary data and a specified format. I want to convert this format to RGBA or BGRA and use it to texture a screen-aligned quad. However, I seem to be misunderstanding some core concepts about how generating and loading texture in OpenGL works, since I can't get the following example to work correctly:

void OpenGLRenderer::renderScreenAlignedQuad(const XrCompositionLayerProjectionView& view)
{
    CHECK_GL_ERROR(glBindVertexArray(m_screenAlignedQuad.vao));
    CHECK_GL_ERROR(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_screenAlignedQuad.indexBuffer));

    // Update texture
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glBindTexture(GL_TEXTURE_2D, m_screenAlignedQuad.texture);

#define BUFF_HEIGHT 1152
#define BUFF_WIDTH 1152
    unsigned char *buffer = new unsigned char[BUFF_HEIGHT * BUFF_WIDTH * 4];
    for (int32_t y = 0; y < BUFF_HEIGHT; y++) {
        for (int32_t x = 0; x < BUFF_WIDTH; x++) {
            int32_t ind = y * BUFF_WIDTH + x * 4;
            buffer[ind] = 255;   // R
            buffer[ind + 1] = 0; // G
            buffer[ind + 2] = 0; // B
            buffer[ind + 3] = 255; // A
        }
    }
    {// =! Critical section !=
        // The mutex will be unlocked when this object goes out of scope;
        // Note that it blocks other threads from writing, but allows reading
        std::shared_lock<std::shared_mutex> sl(m_videoStreamContext.g_currentFrameDataMutex);
        CHECK_GL_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, BUFF_HEIGHT, BUFF_WIDTH, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer));
    }// =! Critical section !=

    CHECK_GL_ERROR(glDrawElements(GL_TRIANGLES, m_screenAlignedQuad.indexCount, GL_UNSIGNED_SHORT, 0));
}

What I wanted to achieve here is to texture to whole screen red. Instead, I get this: incorrect-texture

The texture coordinates seem to be alright (I was able to texture a loaded image correctly before): enter image description here

For some more debugging information I add some more colors:

void OpenGLRenderer::renderScreenAlignedQuad(const XrCompositionLayerProjectionView& view)
{
    CHECK_GL_ERROR(glBindVertexArray(m_screenAlignedQuad.vao));
    CHECK_GL_ERROR(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_screenAlignedQuad.indexBuffer));

    // Update texture
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glBindTexture(GL_TEXTURE_2D, m_screenAlignedQuad.texture);

    unsigned char *buffer = new unsigned char[BUFF_HEIGHT * BUFF_WIDTH * 4];
    for (int32_t y = 0; y < BUFF_HEIGHT; y++) {
        for (int32_t x = 0; x < BUFF_WIDTH; x=x+4) {
            int32_t ind = y * BUFF_WIDTH + x;
                if (y < BUFF_HEIGHT / 2) {
                    buffer[ind] = 255;   // R
                    buffer[ind + 1] = 0; // G
                    buffer[ind + 2] = 0; // B
                    buffer[ind + 3] = 255; // A
                } else if (x < BUFF_WIDTH / 2) {
                    buffer[ind] = 0;   // R
                    buffer[ind + 1] = 0; // G
                    buffer[ind + 2] = 255; // B
                    buffer[ind + 3] = 255; // A
                } else {
                    buffer[ind] = 0;   // R
                    buffer[ind + 1] = 255; // G
                    buffer[ind + 2] = 0; // B
                    buffer[ind + 3] = 255; // A
                }
        }
    }

    {// =! Critical section !=
        // The mutex will be unlocked when this object goes out of scope;
        // Note that it blocks other threads from writing, but allows reading
        std::shared_lock<std::shared_mutex> sl(m_videoStreamContext.g_currentFrameDataMutex);
        CHECK_GL_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, BUFF_HEIGHT, BUFF_WIDTH, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer));
    }// =! Critical section !=

    CHECK_GL_ERROR(glDrawElements(GL_TRIANGLES, m_screenAlignedQuad.indexCount, GL_UNSIGNED_SHORT, 0));
    delete buffer;
}

The output looks like this: enter image description here

So it looks like the texture is rendered too small in both directions. The wrap setting on the texture is set to clamp, so it should not repeat as it does. What am I doing wrong here?

Edit: Please, ignore any obvious inefficiencies or ugly code structure as long as it does not affect the correctness of the program. I'm trying to get the simplest possible version working for now.


Solution

  • Your 2d-to-1d index calculation is just broken:

    int32_t ind = y * BUFF_WIDTH + x * 4;
    

    That is supposed to be

    int32_t ind = (y * BUFF_WIDTH + x) * 4;
    

    You're making the same basic mistake also in your second approach, just obfuscated a bit more:

    for (int32_t y = 0; y < BUFF_HEIGHT; y++) {
         for (int32_t x = 0; x < BUFF_WIDTH; x=x+4) {
             int32_t ind = y * BUFF_WIDTH + x;
    

    x here is now what x * 4 was before (but your loop should then go to x <= 4*BUFF_WIDTH), and ind = y * 4 * BUFF_WIDTH + x would be correct.