将 GLubyte* 转换为 GLuint* 用于 alpha 纹理 Opengl 3.0 / c++ / Win32

Posted

技术标签:

【中文标题】将 GLubyte* 转换为 GLuint* 用于 alpha 纹理 Opengl 3.0 / c++ / Win32【英文标题】:convert GLubyte* to GLuint* for alpha texturing Opengl 3.0 / c++ / Win32 【发布时间】:2015-05-09 12:39:26 【问题描述】:

我的目标是使纹理的像素值 100% 透明,如果它与设置的 RGB 颜色完全匹配。我的程序使用 24 位 .bmp 图像,我知道它不包含第四个通道(除了填充),所以我一直在尝试想出一个好方法来促进添加 alpha 通道以执行基本透明度。

我了解如何计算实际 alpha 值并正确设置混合函数,但是当我尝试将第四个值添加到 GLubyte RGB 三元组时遇到了障碍,让我相信该格式根本不支持α。所以我有一个想法,将纹理数据从字节手动转换为 32 位无符号整数,但我在执行时遇到了问题。在我看来(从看书)内部像素格式 GL_UNSIGNED_INT 每个像素需要四个 32 位整数,但是当我通过将 0.0-1.0 字节值钳制到最大范围来转换字节时,我的程序不会启动无符号整数。

我不想给我答案,但我真的很感激朝着正确的方向前进。我是否在正确的轨道上转换为每像素 4 个整数,或者有没有办法让 alpha 与我忽略的字节数据一起工作?我考虑过使用 GL_UNSIGNED_INT_8888 格式,但我想尽可能避免移位。

【问题讨论】:

您在尝试添加GLubyte 格式的alpha 时遇到了什么问题?真的不应该有什么。您只需每个 texel 有 4 个字节,之前每个 texel 有 3 个字节,并将格式指定为 RGBA 而不是 RGB。 【参考方案1】:

GL_UNSIGNED_INT 不是“内部像素格式”。那实际上是像素传输数据类型。

多年来,GL 中的内部格式一直是个谜,您必须请求组件的数量,但除此之外别无其他。现在(GL3+)你必须使用明确大小的内部格式,比如GL_RGB8,这样就可以排除大部分猜测。但是,您仍然可以假设 GL_RGB8 将被驱动程序用额外的 8 位填充,以便每种颜色都以 2 的幂边界开始。因此GL_RGB8GL_RGBA8 具有相同的存储要求,两者之间的唯一区别是GL_RGB8 具有所有不透明像素。

当您使用GL_UNSIGNED_INT 作为像素传输的数据类型时,这意味着 GL 会将您的输入图像数据解释为具有 32 位(无符号)每颜色分量(每像素 12 字节)。您不希望这样,您目前每个像素有 3 个无符号 8 位分量,因此 GL_UNSIGNED_BYTE 是合适的(每像素 3 个字节)。 添加第 4 个组件不会改变各个组件的大小。

关于GL_UNSIGNED_INT_8_8_8_8,如果您尝试上传RGBA 图像,这实际上与GL_UNSIGNED_BYTE 略有不同。这就是所谓的打包数据类型; 4 个组件封装在与GL_UNSIGNED_INT 相同的封装中,每个组件都是 8 位的。还有GL_UNSIGNED_INT_8_8_8_8_REV,它更有用——它告诉GL在像素传输期间反转颜色分量,如果你只指定GL_UNSIGNED_BYTE作为数据类型,这是你无法做到的。 GL_UNSIGNED_INT_8_8_8_8[_REV] 解决了 CPU 字节顺序问题,在 little-endian CPU 上,GL_UNSIGNED_INT_8_8_8_8_REV 等效于 GL_UNSIGNED_BYTE


请记住,GL 要求图像的每一行都以 4 字节边界开始。

如果您有一个宽度不能被 4 整除的每像素 24 位图像,那么您的数据将会错位,并且 GL 会错误地解压它。要解决这个问题,您可以将解包对齐设置为 1-byte(默认为 4),如下所示:

glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

.bmp 文件格式已经填充了每一行的末尾以满足 4 字节对齐,这只是我想指出的完整性。

【讨论】:

至于您的问题的其余部分,您正在寻找颜色键透明度。您可以预处理数据以插入 alpha 组件,也可以使用片段着色器来完成。我会亲自对数据进行预处理,因为RGB 图像在 GPU 上无论如何都会填充到RGBA,所以你不妨使用否则浪费的内存。 至少在如今流行的小端架构上,我相信8_8_8_8_REV 相当于BYTE8_8_8_8 是字节交换的那个。 如果您查看规范(例如 3.3 规范中的第 131 页),第一个组件位于 _REV 格式的位 0-7 中。如果主机格式是小端,那也是UNSIGNED_BYTE 的第一个字节。在任何 Intel 机器上试一试,你会发现 GL_UNSIGNED_BYTEGL_UNSIGNED_INT_8_8_8_8_REV 产生相同的结果。 @RetoKoradi:啊,是的。你说得对,我在想别的。受GL_UNPACK_SWAP_BYTES影响的是多字节组件格式(例如GL_UNSIGNED_SHORT)。【参考方案2】:

它应该是直截了当的,不需要任何技巧。有两种方法可以实现您所需要的。第一个解决方案是简单地将 bmp 加载为正常的 RGB 数据,然后为 alpha 通道分配一个额外 30% 大小的新缓冲区,并且对于您复制的每 3 个字节,添加一个表示您想要的 alpha 值的字节。然后在上传到 OpenGL 设备时,声明它是 GL_RGBA 而不是 GL_RGB,同时保持格式为 GL_UNSIGNED_BYTE。这样,您可以在加载时将数据从 3 个通道转换为 4 个通道。第二种解决方案是在片段着色器中执行片段丢弃,因为丢弃与 100% 透明相同。由于您知道要丢弃哪种颜色,您可以使用统一或任何其他方式将这些 rgb 值传递给着色器,然后片段着色器可以检查并相应地丢弃。您甚至不必担心此解决方案中的 Alpha 通道。

【讨论】:

以上是关于将 GLubyte* 转换为 GLuint* 用于 alpha 纹理 Opengl 3.0 / c++ / Win32的主要内容,如果未能解决你的问题,请参考以下文章

如何将位图读取为 GLubyte 数组? [关闭]

OpenGL chapter2

将向量<GLubyte> 缓冲区复制到帧缓冲区

在 iOS 中使用 glsl 将 yv12 转换为 rgb,附上结果图像

如何在 C++ 中将 RGB 颜色值转换为十六进制值?

使用 GLuint 而不是 unsigned int 有啥好处?