我正在学习如何在OpenGL中进行纹理处理,并且对我得到的一些结果感到有些困惑。
我正在使用stb_image加载以下棋盘图片:
当我保存png图像时,我明确选择将其保存为32位。这将使我相信每个组件(RGBA)将存储为8位,总共32位 - 无符号整数的大小。但是,使用以下代码:
unsigned char * texture_data =
stbi_load("resources/graphics-scene/tut/textures/checker.png", &w, &h, nullptr, 4);
// ...
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0,
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture_data);
的产率:
如果我改为使用GL_UNSIGNED_BYTE作为类型参数,我会得到正确的结果。
另外,只是帮助它,我也尝试了下面的图像:
产生
GL_UNSIGNED_BYTE也会在这种情况下给出正确的结果。
我不确定这是否是我误解glTexImage2D或stb_image的情况(它是否将加载的数据转换为8位?这对我来说似乎不太可能)。
编辑:我终于找到了一个相关的帖子(已经搜索了一些但没有运气)。然而答案(https://stackoverflow.com/a/4191875/2507444)让我感到困惑。如果是这种情况 - type参数指定每个组件的字节数 - 那么GL_UNSIGNED_BYTE_3_3_2和GL_UNSIGNED_INT_8_8_8_8之类的内容究竟是什么意思???答案 0 :(得分:3)
如果是这种情况 - type参数指定每个组件的字节数 - 那么GL_UNSIGNED_BYTE_3_3_2和GL_UNSIGNED_INT_8_8_8_8之类的内容究竟是什么意思???
两者都有,具体取决于实际类型。
如果pixel transfer type只是一种数据类型,则它指定数据大小每个组件。如果它中有数字,那么类型指定数据的大小每像素;这些数字指定在数据类型中的各个组件大小。
GL_UNSIGNED_INT_8_8_8_8
表示OpenGL会将每个像素解释为无符号整数。第一个组件是高8位,下一个组件是接下来的8位,依此类推。
然而 ,问题来自于STB-image 无符号整数的事实。每个像素按RGBA顺序写为4个单独的字节。基本上,它是这样做的:
GLubyte arr[4] = {red, green, blue, alpha};
现在,这可能声音就像是同一件事。但事实并非如此。原因与endian issues有关。
在C / C ++中执行此操作时:
GLuint foo = 0;
foo |= (red << 24);
foo |= (green << 16);
foo |= (blue << 8);
foo |= (alpha << 0);
OpenGL的数据类型要求GLuint
为无符号整数,大小为32位。假设red
,green
,blue
和alpha
都是GLubyte
s(8位无符号整数),C / C ++表示这将是将red
位打包到高8位字节,将green
打包到下一位,依此类推。 C和C ++标准要求这样做。
然而,C和C ++标准不要求它起作用:
GLubyte *ptr = (GLubyte*)&foo;
ptr[0] == ((foo >> 24) & 0xFF);
也就是说,foo
指向的内存的第一个字节不会将作为red
组件。
在小端字节排序中,32位整数的低字节先存储 ,而不是最后存储。
当OpenGL看到GL_UNSIGNED_INT
时,这意味着它将完全按照您的CPU 的方式解释这四个字节。因此,GL_UNSIGNED_INT_8_8_8_8
将完全相同于foo
。它看到的内存的第一个字节将在小端机器上解释为低字节,而不是高字节。
STB-image不输出GL_UNSIGNED_INT_8_8_8_8
。它将每个像素视为一个4字节数组,如上面的arr
。因此,您必须告诉OpenGL这是您的数据存储方式。所以你说每个组件都是一个字节。这是GL_UNSIGNED_BYTE
的作用。