如何在 1D 缓冲区中生成 2D 纹理并将其加载到 OpenGL 中?

Posted

技术标签:

【中文标题】如何在 1D 缓冲区中生成 2D 纹理并将其加载到 OpenGL 中?【英文标题】:How to generate a 2D texture into a 1D buffer and load it in OpenGL? 【发布时间】:2021-12-26 18:22:47 【问题描述】:

在某些情况下,我以一维二进制数据和指定格式的形式从相机获取实时图像数据。我想将此格式转换为 RGBA 或 BGRA 并使用它来纹理屏幕对齐的四边形。但是,我似乎误解了一些关于如何在 OpenGL 中生成和加载纹理的核心概念,因为我无法让以下示例正常工作:

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

我想在这里实现的是将整个屏幕变成红色的纹理。相反,我得到了这个:

纹理坐标似乎没问题(我之前能够正确地纹理加载的图像):

对于更多调试信息,我添加了更多颜色:

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;

输出如下所示:

所以看起来纹理在两个方向上都被渲染得太小了。纹理上的环绕设置设置为钳位,因此不应像以前那样重复。我在这里做错了什么?

编辑:只要不影响程序的正确性,请忽略任何明显的低效率或丑陋的代码结构。我正在尝试让最简单的版本暂时可用。

【问题讨论】:

【参考方案1】:

您的 2d 到 1d 索引计算刚刚出错:

int32_t ind = y * BUFF_WIDTH + x * 4;

应该是这样的

int32_t ind = (y * BUFF_WIDTH + x) * 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;

x 现在是 x * 4 之前的样子(但你的循环应该转到 x &lt;= 4*BUFF_WIDTH),ind = y * 4 * BUFF_WIDTH + x 是正确的。

【讨论】:

当然!那是一个愚蠢的错误。感谢您的清晰解释!

以上是关于如何在 1D 缓冲区中生成 2D 纹理并将其加载到 OpenGL 中?的主要内容,如果未能解决你的问题,请参考以下文章

渲染到纹理 OpenGL ES 2.0

在OpenGL中计算按1d、2d、3d分组创建的纹理

在 GLSL 中使用多个纹理+过滤器

如何在 zip 文件中生成 xls 文件并将其发送到电子邮件以在那里下载 - groovy [关闭]

OpenGL ES之如何传输一个超大数组给着色器程序

如何按照最初在 C++ 中生成的顺序从有界缓冲区中检索项目?