【问题标题】:openGL SubTexturingopenGL 子纹理
【发布时间】:2010-09-17 08:36:10
【问题描述】:

我有图像数据,我想获取它的子图像以用作 opengl 纹理。

glGenTextures(1, &m_name);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldName);
glBindTexture(GL_TEXTURE_2D, m_name);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_data);

如何获取加载为纹理的该图像的子图像。我认为它与使用 glTexSubImage2D 有关,但我不知道如何使用它来创建我可以加载的新纹理。调用:

glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, xWidth, yHeight, GL_RGBA, GL_UNSIGNED_BYTE, m_data);

我看不到任何东西,调用 glCopyTexSubImage2D 只是占用我的帧缓冲区的一部分。 谢谢

【问题讨论】:

    标签: c++ opengl


    【解决方案1】:

    编辑:使用 glPixelStorei。您可以使用它将GL_UNPACK_ROW_LENGTH 设置为整个图像的宽度(以像素为单位)。然后调用 glTexImage2D(或其他),将指针传递给子图像的第一个像素以及子图像的宽度和高度。

    完成后不要忘记将GL_UNPACK_ROW_LENGTH 恢复为 0。

    即:

    glPixelStorei( GL_UNPACK_ROW_LENGTH, img_width );
    char *subimg = (char*)m_data + (sub_x + sub_y*img_width)*4;
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, sub_width, sub_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, subimg );
    glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
    

    或者,如果您对指针数学过敏:

    glPixelStorei( GL_UNPACK_ROW_LENGTH, img_width );
    glPixelStorei( GL_UNPACK_SKIP_PIXELS, sub_x );
    glPixelStorei( GL_UNPACK_SKIP_ROWS, sub_y );
    
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, sub_width, sub_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_data );
    
    glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
    glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
    glPixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
    

    Edit2:为了完整起见,我应该指出,如果您使用的是 OpenGL-ES,那么您不会得到 GL_UNPACK_ROW_LENGTH。在这种情况下,您可以 (a) 自己将子图像提取到新的缓冲区中,或者 (b)...

    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, sub_width, sub_height, 0, GL_RGBA, GL_UNSIGNED_BYTES, NULL );
    
    for( int y = 0; y < sub_height; y++ )
    {
        char *row = m_data + ((y + sub_y)*img_width + sub_x) * 4;
        glTexSubImage2D( GL_TEXTURE_2D, 0, 0, y, sub_width, 1, GL_RGBA, GL_UNSIGNED_BYTE, row );
    }
    

    【讨论】:

    • 基本上我有一个图像(作为原始数据),我想将该图像的一部分用作纹理。我知道如何将整个图像加载为纹理,但不知道如何使用它。
    • 海报回答了这个问题。如果您在内存中有图像,它会以某种方式存储。如果您使用 glTexImage2D(...GL_RGBA, GL_UNSIGNED_BYTE) 加载,那么 R、G、B 和 alpha 值都有一个字节。图像存储的像素 (0, 0) 从位置 0 开始。y 行的像素 x 从 image[width*y+x][0] 开始
    • 因为我使用的是 openGL ES,所以我最终选择了你给出的第一个选项,即将子图像提取到新的缓冲区中。遗憾的是,在设备上它不再工作了,所以我要重新制作纹理,这样我就不需要解决问题了。
    【解决方案2】:

    对于那些在 2018 年及以后坚持使用 OpenGL ES 1.1/2.0 的人,我用不同的方法进行了一些测试,如何从图像数据中更新部分纹理(图像与纹理大小相同)。

    方法一:glTexImage2D复制整张图片:

    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_Pixels );
    

    方法二:glTexSubImage2D复制整张图片:

    glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, m_Pixels );
    

    方法三:循环复制图片部分,逐行复制:

    auto *ptr = m_Pixels + (x + y * mWidth) * 4;
    for( int i = 0; i < h; i++, ptr += mWidth * 4 ) {
        glTexSubImage2D( GL_TEXTURE_2D, 0, x, y+i, w, 1, GL_RGBA, GL_UNSIGNED_BYTE, ptr );
    }
    

    方法四:复制整幅图像,但只垂直复制改变的部分:

    auto *ptr = m_Pixels + (y * mWidth) * 4;
    glTexSubImage2D( GL_TEXTURE_2D, 0, 0, y, mWidth, h, GL_RGBA, GL_UNSIGNED_BYTE, ptr );
    

    这是在 PC 上进行的测试结果,通过 100000 次更新纹理的不同部分,大约是整个纹理大小的 1/5。

    • 方法 1 - 38.17 秒
    • 方法 2 - 26.09 秒
    • 方法 3 - 54.83 秒 - 最慢
    • 方法 4 - 5.93 秒 - 获胜者

    毫不奇怪,方法 4 最快,因为它只复制图像的一部分,并且只需调用 glTex...() 函数即可。

    【讨论】:

      猜你喜欢
      • 2011-05-19
      • 1970-01-01
      • 2023-04-01
      • 2012-12-17
      • 2018-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多