OpenGL 子纹理化

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只会复制我的帧缓冲的一部分。

2个回答

30

编辑:使用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 );

编辑2:为了完整起见,我应该指出,如果你正在使用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 );
}

基本上,我有一张图像(作为原始数据),我想使用该图像的一部分作为纹理。我知道如何将整个图像加载为纹理,但不知道如何仅使用其中的一部分。 - DavidG
海报回答了这个问题。如果你在内存中有一张图片,它会以某种方式被存储。如果你使用glTexImage2D(...GL_RGBA, GL_UNSIGNED_BYTE)来加载,那么每个像素都有一个R、G、B和alpha值的字节。这些图像是按照像素(0, 0)从位置0开始存储的。第y行的第x个像素从image[width*y+x][0]开始。 - mstrobl
由于我正在使用OpenGL ES,所以我最终选择了您提供的第一种选项,即将子图像提取到新缓冲区中。不幸的是,在设备上它不再起作用,所以我将重新设计纹理,以便我不需要解决这个问题。 - DavidG

4

如果在2018年或之后仍然使用OpenGL ES 1.1/2.0,我进行了一些测试,尝试不同的方法来更新部分纹理图像(图像与纹理大小相同)。

方法1:使用glTexImage2D复制整个图像:

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

方法2: 使用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 );
}
方法4: 复制整个图像的宽度,但垂直复制仅改变的部分:
auto *ptr = m_Pixels + (y * mWidth) * 4;
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, y, mWidth, h, GL_RGBA, GL_UNSIGNED_BYTE, ptr );

以下是测试结果,测试在PC上进行,更新的纹理部分约为整个纹理的1/5:
- 方法1 - 38.17秒 - 方法2 - 26.09秒 - 方法3 - 54.83秒 - 最慢 - 方法4 - 5.93秒 - 最快 毫不奇怪,方法4是最快的,因为它只复制图像的一部分,并使用单个glTex...()函数调用完成。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接