当我们使用OpenGL进行有关图片处理的时候,例如做纹理映射相关程序的时候,一定调用过以下这些函数:
1. glTexImage{1,2,3}D
2. glCopyTexImage{1,2,3}D
这些函数中经常会遇到一个参数 internalFormat,同时也会遇到另外两个参数 format和type 这些参数从表意上就没有width、height这样的参数那么直观。本文就是详细介绍一下参数internalFormat的方方面面,另一篇文章《OpenGL像素格式》介绍另外两个参数format和type。
在OpenGL中什么地方可以作为图像可以存储图像(Image)呢?首先我们想到的是:
1. 纹理对象
2. 帧缓存对象
3. 像素缓冲区对象
这三类对象是在显卡的显存中,那么还有一个地方是内存中
4. 内存中的一块区域
我们需要对这些对象进行分类,其中1和2是有特定用途的,1是作为给几何体贴图使用,2可以理解为我们在屏幕上看到的场景。3和4就没有那么特定的用处了,可以把它们理解为存储像素数据的仓库。
基于这样一种认识,那么接下来我们需要解释的是,1和2里面存储的数据是什么格式的,是RGB、RGBA还是其他方式,这就是本文讨论的主题图像格式(Image Format)[个人翻译,中文准确翻译有待考究]
图像格式(ImageFormat)描述了图像在纹理和帧缓存中的存储方式,定义了图像数据的含义。
图像格式有三种类型:颜色、深度、深度/模板 ,所有的格式都可以被应用到纹理存储单元或者帧缓存存储单元(除非特别的说明)
颜色格式可以以3种方式存储,归一化的整型数(normalized integers),浮点数和整数。其中单位化的整型和浮点型在shader中会被解析为浮点数组,整型会被解析为整型数据。
归一化的整型数可以分为两类,包括有符号类型和无符号类型,有符号类型的取值范围是[-1,1],无符号类型的取值范围是[0,1]
OpenGL在定义颜色格式的时候,语法如下:
GL_[components][size][type]
components:指的是颜色分量,OpenGL只允许图像格式使用下面的4种:"R" "RG" "RGB" "RGBA"
,size定义了每种分量占用的位数(bit),type定义了颜色格式使用的存储方式,可以的取值包括:
type取值 | 参数解释 |
---|---|
“”(空) | 无后缀表示使用的是无符号的的归一化整型数 |
“_SNORM” | 归一化的有符号整型 |
“F” | 浮点类型,如:GL_RGBA32F指每个分量(R\G\B\A)是32位的浮点数 |
“I” | 有符号整型,如GL_RGBA8I指每个分量都是位于[-128,127]的整型数 |
“UI” | 无符号整形数 |
根据上面的介绍,如果你需要一个3分量的无符号整型格式,并且每个分量占用8位(1个字节),那么使用的格式应该是:GL_RGB8UI
对于每一种类型的颜色格式,颜色的每一个分量在位数上有一个限制:
格式type | 每一个分量的位数 |
---|---|
无符号归一化整型 | 2、4、5、8、10、12、16 |
有符号归一化整型 | 8、16 |
无符号整型 | 8、16、32 |
有符号整型 | 8、16、32 |
浮点数 | 16、32 |
需要注意的是:
1. 对于分量位数是2位的情况,只能使用RGBA,使用其他方式都是不允许的(如你不能使用GL_RG2这种格式)
2. 对于分量位数是4,5,12的情况,只能使用RGB和RGBA,使用其他方式都是不允许的
3. 对于分量位数是10的情况,只能使用RGB,其他方式都是不允许的(例如你不能使用GL_RGBA10这种格式
在使用过程中也可以省略分量占用的位数,这种情况仅仅适用于无符号归一化的整型格式,如果省略了,那么OpenGL会帮我们选择一个位数。一般来说,最好还是指定一个位数。
以上所讲的是常规的颜色格式的情况,OpenGL除此之外还支持部分其他的颜色格式,列举如下:
颜色格式 | 格式说明 |
---|---|
GL_R3_G3_B2 | 归一化的整型,RG分量占用3位,B分量占用2位(正好1个字节) |
GL_RGB5_A1 | RGB分量分别占用5位,A分量占用1位,这种格式通常用于压缩格式 |
GL_RGB10_A2 | RGB分量分别占用10位,A分量占用2位(正好4个字节) |
GL_RGB10_A2UI | 无符号整型数,RGB占用10位,A占用2位 |
GL_R11F_G11F_B10F | 浮点数类型的格式,最大程度节约存储空间 |
GL_RGB9_E5 | 浮点类型的格式,它的计算方式非常复杂,一般不用于帧缓冲格式,E是科学记数法的一个标识 |
纹理压缩是非常重要的减少内存占用的一种方式,当可以使用纹理压缩方式时,最好都能运用这一方式。在OpenGL中有两种类型的压缩格式:通用的压缩方式和特定的压缩方式。
通用的纹理压缩方式的实现依赖于OpenGL的硬件驱动,一般来说实现比较自由,最好还是尽量避免使用。
通用的纹理压缩格式使用下面的语法:
GL_COMPRESSED_components
components可以设置为RED RG RGB RGBA SRGB SRGB_ALPHA
压缩格式并不能被设置为帧缓冲区的格式
深度格式有两种类型,归一化整型和浮点数类型,深度图像格式提供一下几种:
GL_DEPTH_COMPONENT16
GL_DEPTH_COMPONENT24
GL_DEPTH_COMPONENT32
GL_DEPTH_COMPONENT32F
深度模板格式融合深度和纹理,可以让你设置深度缓冲区的同时设置模板缓冲区。
如果OpenGL提供了ARB_stencil_texturing扩展(OpenGL4.3)那么纹理对象可以设置一个参数来通过取样器获取模板的部分。
这种类型的图像格式只有两种:
GL_DEPTH24_STENCIL8
GL_DEPTH32F_STENCIL8
模板格式用来存储一个模板值,模板值一般是无符号的整型,所有的模板格式类型使用GL_STENCIL_INDEX#的方式,包括
GL_STENCIL_INDEX1
GL_STENCIL_INDEX4
GL_STENCIL_INDEX8
GL_STENCIL_INDEX16
OpenGL规范对于图像格式的实现相对比较宽松,不过规范中也指明了某些格式必须要支持,包括以下的一些格式:
基本格式 | 数据类型 | 每一个分量占据的位数 |
---|---|---|
RGBA,RG,RED | 归一化整型 | 8,16 |
RGBA,RG,RED | 浮点型 | 16,32 |
RGBA,RG,RED | 无符号整型 | 8,16,32 |
RGBA,RG,RED | 有符号整型 | 8,16,32 |
另外还包括
GL_RGB10_A2
GL_RGB10_A2UI
GL_R11F_G11F_B10F
GL_SRGB8_ALPHA8
GL_DEPTH_COMPONENT16
GL_DEPTH_COMPONENT24
GL_DEPTH_COMPONENT32F
GL_DEPTH24_STENCIL8
GL_DEPTH32F_STENCIL8
GL_STENCIL_INDEX8(4.3只支持帧缓冲区,4.4支持帧缓冲区和纹理)
以下的这些格式必须支持纹理,但是能否支持帧缓冲区OpenGL规范并没有强制要求:
基本类型 | 数据格式 | 分量占据的位数 |
---|---|---|
RGB | 无符号归一化整型 | 8,16 |
RGBA,RGB,RG,RED | 有符号归一化整型 | 8,16 |
RGB | 浮点 | 16,32 |
RGB | 有符号整型 | 8,16,32 |
RGB | 无符号整型 | 8,16,32 |
RG,RED | 无符号整型 | Compressed with RGTC(TODO?) |
此外还包括:
GL_SRGB8
GL_RGB9_E5
OpenGL的图像格式的许多属性根据OpenGL的实现不同而略有差异(这主要是OpenGL对于图像格式的要求比较宽松所致),OpenGL在4.3以及以上版本提供了一种查询图像格式的机制,可以使用下面的函数来获取图像格式:
void glGetInternalFormativ(
GLenum target,
GLenum internalformat,
GLenum pname,
GLsizei bufSize,
GLint *params);
void glGetInternalFormati64v(
GLenum target,
GLenum internalformat,
GLenum pname,
GLsizei bufSize,
GLint64 *params);
在OpenGL Legecy中有两种类型的颜色格式Luminance和intensity,它们表示一般只有两个分量的格式(有点类似于RED或者是RG),但是它们的表现和RED与RG不一样。体现着:
当GL_RED在shader中使用时, vec4是(Red,0,0,1)
GL_INTENSITY是(I,I,I,I),GL_LUMINANCE是(L,L,L,1),GL_LUMINANCE_ALPHA是(L,L,L,A)
这两种格式有8位和16位两种:
GL_INTENSITY8,
GL_INTENSITY16,
GL_LUMINANCE8,
GL_LUMINANCE_ALPHA16
在设置OpenGL的InternalFormat的时候相对比较自由,但是一般来说调用需要设置internalformat的函数时,一般会有pixel format和type这两个参数与之在一起出现,例如:
void glTexImage2D( GLenum target,
GLint level,
GLint internalFormat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid * data);
简单来说internalFormat定义了GPU中像素的格式和存储方式,format和type以及data这三个参数共同定义了在内存中像素的格式和存储方式,如果我们设置这两者格式差异很大,OpenGL在内部会帮助我们转换,这样往往带来性能上的损失,一般来说最好把二者设置的比较兼容,方便OpenGL做数据的传输,避免转换。
参考:
Image Format
stackoverflow关于internalformat讨论