關於OpenGL的圖像數據格式

1. 引言

當我們使用OpenGL進行有關圖片處理的時候,例如做紋理映射相關程序的時候,一定調用過以下這些函數:
1. glTexImage{1,2,3}D
2. glCopyTexImage{1,2,3}D
這些函數中經常會遇到一個參數 internalFormat,同時也會遇到另外兩個參數 format和type 這些參數從表意上就沒有width、height這樣的參數那麼直觀。本文就是詳細介紹一下參數internalFormat的方方面面,另一篇文章《OpenGL像素格式》介紹另外兩個參數format和type。

2. 簡介

在OpenGL中什麼地方可以作爲圖像可以存儲圖像(Image)呢?首先我們想到的是:
1. 紋理對象
2. 幀緩存對象
3. 像素緩衝區對象
這三類對象是在顯卡的顯存中,那麼還有一個地方是內存中
4. 內存中的一塊區域
我們需要對這些對象進行分類,其中1和2是有特定用途的,1是作爲給幾何體貼圖使用,2可以理解爲我們在屏幕上看到的場景。3和4就沒有那麼特定的用處了,可以把它們理解爲存儲像素數據的倉庫。
基於這樣一種認識,那麼接下來我們需要解釋的是,1和2裏面存儲的數據是什麼格式的,是RGB、RGBA還是其他方式,這就是本文討論的主題圖像格式(Image Format)[個人翻譯,中文準確翻譯有待考究]

3. 詳細介紹

圖像格式(ImageFormat)描述了圖像在紋理和幀緩存中的存儲方式,定義了圖像數據的含義。
圖像格式有三種類型:顏色、深度、深度/模板 ,所有的格式都可以被應用到紋理存儲單元或者幀緩存存儲單元(除非特別的說明)

3.1 顏色格式

顏色格式可以以3種方式存儲,歸一化的整型數(normalized integers),浮點數和整數。其中單位化的整型和浮點型在shader中會被解析爲浮點數組,整型會被解析爲整型數據。
歸一化的整型數可以分爲兩類,包括有符號類型和無符號類型,有符號類型的取值範圍是[-1,1],無符號類型的取值範圍是[0,1]
OpenGL在定義顏色格式的時候,語法如下:

GL_[components][size][type]
  • 1

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會幫我們選擇一個位數。一般來說,最好還是指定一個位數。

3.2 特殊的顏色格式

以上所講的是常規的顏色格式的情況,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是科學記數法的一個標識

3.3 壓縮的格式

紋理壓縮是非常重要的減少內存佔用的一種方式,當可以使用紋理壓縮方式時,最好都能運用這一方式。在OpenGL中有兩種類型的壓縮格式:通用的壓縮方式和特定的壓縮方式。
通用的紋理壓縮方式的實現依賴於OpenGL的硬件驅動,一般來說實現比較自由,最好還是儘量避免使用。
通用的紋理壓縮格式使用下面的語法:

GL_COMPRESSED_components
  • 1

components可以設置爲RED RG RGB RGBA SRGB SRGB_ALPHA
壓縮格式並不能被設置爲幀緩衝區的格式

3.4 深度格式

深度格式有兩種類型,歸一化整型和浮點數類型,深度圖像格式提供一下幾種:

GL_DEPTH_COMPONENT16
GL_DEPTH_COMPONENT24
GL_DEPTH_COMPONENT32
GL_DEPTH_COMPONENT32F

3.4 深度、模板格式

深度模板格式融合深度和紋理,可以讓你設置深度緩衝區的同時設置模板緩衝區。
如果OpenGL提供了ARB_stencil_texturing擴展(OpenGL4.3)那麼紋理對象可以設置一個參數來通過取樣器獲取模板的部分。
這種類型的圖像格式只有兩種:

GL_DEPTH24_STENCIL8
GL_DEPTH32F_STENCIL8
  • 1
  • 2

3.6 模板格式

模板格式用來存儲一個模板值,模板值一般是無符號的整型,所有的模板格式類型使用GL_STENCIL_INDEX#的方式,包括

GL_STENCIL_INDEX1
GL_STENCIL_INDEX4
GL_STENCIL_INDEX8
GL_STENCIL_INDEX16
  • 1
  • 2
  • 3
  • 4

3.7 常用的圖像格式

OpenGL規範對於圖像格式的實現相對比較寬鬆,不過規範中也指明瞭某些格式必須要支持,包括以下的一些格式:

3.7.1 支持紋理和幀緩衝區

基本格式 數據類型 每一個分量佔據的位數
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支持幀緩衝區和紋理)

3.7.2 只支持紋理

以下的這些格式必須支持紋理,但是能否支持幀緩衝區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​);

3.9 過時的圖像格式

在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

4. 其他

在設置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討論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章