我已经读过很多地方,应该避免使用3字节长的OpenGL纹理,并且应该始终使用4字节总线对齐表示。记住这一点,我有几个关于glTextImage2D API的问题。
从文档中,API签名是:
void glTexImage2D(GLenum target, GLint level, GLint internalformat,
GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type, const GLvoid * data);
我是否正确假设如果我有RGB图像并且我想要RGBA表示,那么将internalformat参数指定为GL_RGBA并将格式参数指定为GL_RGB就足够了?在生成纹理时,格式之间会有内部转换吗?
我的第二个问题是如果我有灰度数据(所以只有一个通道)。是否可以将其表示为GL_RED,或者为此更好地使用4字节表示?
答案 0 :(得分:3)
我不同意您发现的避免RGB格式的建议。我想不出一个避免RGB纹理的好理由。一些GPU原生支持它们,许多其他GPU不支持它们。您有两种情况:
在方案1中,您最终得到的结果与将RGBA指定为内部格式时完全相同。在方案2中,通过实际使用RGB内部格式节省了25%的内存(以及相应的带宽)。
因此,使用RGB作为内部格式永远不会更糟,在某些系统上可能更好。
您的代码片段中的内容在桌面OpenGL中完全合法。通过使用1.0填充A组件,您提供的RGB数据将扩展为RGBA。在OpenGL ES中不会出现这种情况,您只能为每种内部格式使用非常受控制的格式,这主要避免了TexImage
和TexSubImage
操作期间的格式转换。
匹配内部格式和格式/类型以避免转换通常很有用。但即使这样也不像看起来那么明确。假设您将RGB数据或RGBA数据加载到具有内部RGBA格式的纹理中。通过不使用格式转换,加载RGBA具有明显的优势。另一方面,由于RGB数据较小,因此加载它需要较少的内存带宽,并且可能会减少缓存污染。
现在,现代计算机系统的内存带宽如此之高,以至于无法通过单核的顺序访问来实现它的饱和。因此,避免转换的选项可能会更好。但它将是非常平台和情况依赖。例如,如果需要中间副本,则可以获得较少量的数据。特别是如果实际的RGB到RGBA扩展可以作为GPU执行的复制的一部分来完成。
我绝对会避免的一件事是在您自己的代码中进行转换。假设您从某个地方获取RGB数据,并且您确实需要将其加载到RGBA纹理中。驱动程序为这些转换提供了高度优化的代码,或者它们甚至可能作为GPU blit的一部分发生。与创建数据的另一个副本的代码相比,将转换作为副本的一部分执行总是更好。
听起来很混乱?在考虑性能时,这种权衡是很常见的。通常情况下,要使用OpenGL获得最佳性能,您需要为不同的GPU /平台使用不同的代码路径。
答案 1 :(得分:1)
我是否正确假设如果我有RGB图像并且我想要RGBA表示,那么将internalformat参数指定为GL_RGBA并将格式参数指定为GL_RGB就足够了?在生成纹理时,格式之间会有内部转换吗?
这将起作用,GL将为每个纹素的alpha分量指定一个常量 1.0 。在像素传输过程中,需要OpenGL将兼容的图像数据转换为GPU的原生格式,这包括添加额外的图像通道,从浮点转换为定点,交换字节以实现端点之间的差异。 CPU和GPU。
为了获得最佳的像素传输性能,您需要消除GL所做的所有额外工作。这意味着如果您使用GL_RGBA8
作为内部格式,则数据类型应为GL_UNSIGNED_BYTE
,像素应为RGBA
的某种变体(通常GL_BGRA
是快速路径 - 数据可以直接从CPU复制到GPU。)
在CPU端仅存储3个组件显然会阻止直接复制,并且会使驱动程序做更多的工作。这是否重要取决于您传输像素数据的频率和频率。
我的第二个问题是如果我有灰度数据(所以只有一个通道)。是否可以将其表示为GL_RED,或者为此更好地使用4字节表示?
GL_RED
不代表您的数据。这只会告诉GL像素传输数据包含哪些通道,你必须将它与数据类型(例如GL_UNSIGNED_BYTE
)结合起来才能理解它。
GL_R8
将是灰度图像的常见内部格式,这非常好。您需要关注的经验法则实际上是数据大小需要与二次幂对齐。因此,1字节,2字节,4字节和8字节图像格式都可以。奇怪的是3,当你尝试使用像GL_RGB8
这样的东西时会发生这种情况(驱动程序必须填充该图像格式以进行对齐)。
除非你的灰度实际上需要32位渐变(42.9亿灰度!),否则坚持使用GL_R8
(256种阴影)或GL_R16
(65536色调)用于内部格式。