OpenGL ES 入門之旅--OpenGL下的紋理常用API

通常來講,計(jì)算機(jī)圖形學(xué)的目標(biāo)是計(jì)算一張圖片上的每個(gè)組成部分的顏色,雖然我們可以通過著色器中的算法來計(jì)算像素的顏色,不過很多時(shí)候這種著色器的實(shí)現(xiàn)過程太過復(fù)雜,不適合實(shí)際應(yīng)用。這種時(shí)候我們可以選擇使用紋理----它是由大塊的圖像數(shù)據(jù)組成的,可以用來繪制到物體的表面以增強(qiáng)其真實(shí)感。OpenGL也沒有對(duì)“紋理”這個(gè)概念做更多的介紹:作為個(gè)人來講,一個(gè)紋理其實(shí)就是一幅圖像,我們可以把這幅圖像的整體或部分貼到我們先前用頂點(diǎn)勾畫出的物體上去——比如對(duì)一個(gè)立方體、圓等貼上紋理圖,使這個(gè)物體看起來更加的真實(shí)。

紋理映射

在物理世界中,視域內(nèi)的顏色會(huì)發(fā)生快速的變化,例如你坐在你家里的沙發(fā)上, 你可以觀察到房間里的墻壁,天花板,地板以及房間里的其他物品,除非這個(gè)房間沒有任何物體,否則你將會(huì)看到很多物體的表面上都會(huì)呈現(xiàn)出豐富的顏色,并且在狹小的面積上產(chǎn)生多彩的變化。要捕捉細(xì)節(jié)如此豐富的色彩變化是非常辛苦和縝密的工作(你需要有效地辨別每個(gè)線性色彩變化區(qū)域中的每個(gè)三角形)。如果能找到一張圖片,然后把它“粘”到物體表面上,就像貼墻紙一樣,那就簡單多了,這就是紋理映射(texture mapping)。通常來說,紋理貼圖(或者簡稱為紋理)是由攝像機(jī)拍攝或者藝術(shù)家繪制的一張圖片。在自然界中的紋理是二維的,但是OpenGL也支持其他類型的紋理格式:一維紋理,二維紋理,三維紋理,立方體映射紋理,以及緩存紋理。同時(shí)還支持紋理數(shù)組。紋理是由紋素(texel)組成的,其中通常包含顏色數(shù)據(jù)信息。


9.jpg

以左下角為原點(diǎn),向右伸展到1.0的位置,向上伸展到1.0的位置,表示一整張的紋理圖像

圖片的存儲(chǔ)空間

圖像存儲(chǔ)空間 = 圖像的寬度width * 圖像高度height * 每個(gè)像素的字節(jié)數(shù)

紋理相關(guān)函數(shù)

1.改變像素的存儲(chǔ)方式

改變像素的存儲(chǔ)方式:
void glPixelStorei(GLenum pname,GLint param);
恢復(fù)像素存儲(chǔ)方式:
void glPixelStoref(GLenum pname,GLfloat param);
正常來說,我們是沒必要來改變像素的存儲(chǔ)方式的,以上兩個(gè)函數(shù)僅僅作為了解。
這兩個(gè)函數(shù)的功能是一樣的,只不過函數(shù)名一個(gè)是 i 結(jié)尾,一個(gè)是 f 結(jié)尾,區(qū)別只是第二個(gè)參數(shù)GLint param的類型,i 的是 GLint,f 的是 GLfloat。

glPixelStorei(GL_UNPACK_ALIGNMENT,1);

參數(shù)1:GL_UNPACK_ALIGNMENT,指定 OpenGL 如何從數(shù)據(jù)緩沖區(qū)中解包圖像數(shù)據(jù)。
參數(shù)2:針對(duì) GL_UNPACK_ALIGNMENT 設(shè)置的值。
這里pname參數(shù)的符號(hào)有兩種:一種是GL_PACK_ALIGNMENT,它影響將像素?cái)?shù)據(jù)寫回到主存的打包形式,對(duì)glReadPixels的調(diào)用產(chǎn)生影響。
還有一種是GL_UNPACK_ALIGNMENT,它影響從主存讀到的像素?cái)?shù)據(jù)的解包形式,對(duì)glTexImage2D以及glTexSubImage2D產(chǎn)生影響。
GL_UNPACK_ALIGNMENT 指內(nèi)存中每個(gè)像素?起點(diǎn)的排列列請(qǐng)求,允許設(shè)置為:1 (byte排列)、2(排列為偶數(shù)byte的?)、4(字word排列)、8(?從雙字節(jié)邊界開始),對(duì)齊的字節(jié)數(shù)越高,系統(tǒng)就越能優(yōu)化。

2.將顏色緩存區(qū)的內(nèi)容作為像素圖直接讀取

void glReadPixels(GLint x,GLint y,GLSizei width,GLSizei height, GLenum format, GLenum type,const void * pixels);

參數(shù)1:x,矩形左下角的窗?坐標(biāo)
參數(shù)2:y,矩形左下角的窗?坐標(biāo)
參數(shù)3:width,矩形的寬,以像素為單位
參數(shù)4:height,矩形的高,以像素為單位
參數(shù)5:format,OpenGL 的像素格式
參數(shù)6:type,解釋參數(shù) pixels 指向的數(shù)據(jù),告訴OpenGL 使?緩存區(qū)中的什么數(shù)據(jù)類型來存儲(chǔ)顏?分量,像素?cái)?shù)據(jù)的數(shù)據(jù)類型
參數(shù)7:pixels,指向圖形數(shù)據(jù)的指針

void glReadBuffer(GLenum mode);—> 指定讀取的緩存
void glWriteBuffer(GLenum mode);—> 指定寫?的緩存
下面來看一下OpenGL中的像素格式有哪些:如下表所示

常量 描述
GL_RED 每個(gè)像素只包含了一個(gè)紅?分量
GL_GREEN 每個(gè)像素只包含了一個(gè)綠色分量
GL_BLUE 每個(gè)像素只包含了一個(gè)藍(lán)?分量
GL_RG 每個(gè)像素依次包含了一個(gè)紅?和綠?的分量
GL_RGB 描述紅、綠、藍(lán)順序排列的顏?
GL_RGBA 按照紅、綠、藍(lán)、Alpha順序排列的顏色
GL_BGR 按照藍(lán)、綠、紅順序排列顏色
GL_BGRA 按照藍(lán)、綠、紅、Alpha順序排列顏色
GL_RED_INTEGER 每個(gè)像素包含了一個(gè)整數(shù)形式的紅色分量
GL_GREEN_INTEGER 每個(gè)像素包含了一個(gè)整數(shù)形式的綠色分量
GL_BLUE_INTEGER 每個(gè)像素包含了一個(gè)整數(shù)形式的藍(lán)色分量
GL_RG_INTEGER 每個(gè)像素依次包含了一個(gè)整數(shù)形式的紅色、綠色分量
GL_RGB_INTEGER 每個(gè)像素包含了一個(gè)整數(shù)形式的紅色、藍(lán)色、綠色分量
GL_RGBA_INTEGER 每個(gè)像素包含了一個(gè)整數(shù)形式的紅色、藍(lán)色、綠色、Alpah分量
GL_BGR_INTEGER 每個(gè)像素包含了一個(gè)整數(shù)形式的藍(lán)色、綠色、紅?分量
GL_BGRA_INTEGER 每個(gè)像素包含了一個(gè)整數(shù)形式的藍(lán)色、綠色、紅色、Alpah分量
GL_STENCIL_INDEX 每個(gè)像素只包含了?個(gè)模板值
GL_DEPTH_COMPONENT 每個(gè)像素值包含?個(gè)深度值
GL_DEPTH_STENCIL 每個(gè)像素包含一個(gè)深度值和?個(gè)模板值

在上述表格中的最后3個(gè)格式 GL_STENCIL_INDEX、GL_DEPTH_COMPONENT 和 GL_DEPTH_STENCIL 用于對(duì)模板緩沖區(qū)和深度緩沖區(qū)直接進(jìn)行讀寫。

下面再來看一下像素?cái)?shù)據(jù)的數(shù)據(jù)類型有哪些?如下表:

常量 描述
GL_UNSIGNED_BYTE 每種顏色分量都是一個(gè)8 位無符號(hào)整數(shù)
GL_BYTE 每種顏色分量都是一個(gè)8 位有符號(hào)整數(shù)
GL_UNSIGNED_SHORT 每種顏色分量都是一個(gè)16 位無符號(hào)整數(shù)
GL_SHORT 每種顏色分量都是一個(gè)16 位有符號(hào)整數(shù)
GL_UNSIGNED_INT 每種顏色分量都是一個(gè)32 位無符號(hào)整數(shù)
GL_INT 每種顏色分量都是一個(gè)32 位有符號(hào)整數(shù)
GL_FLOAT 每種顏色分量都是一個(gè)單精度浮點(diǎn)數(shù)
GL_HALF_FLOAT 每種顏色分量都是一個(gè)半精度浮點(diǎn)數(shù)
GL_UNSIGNED_BYTE_3_2_2 包裝的 RGB 值
GL_UNSIGNED_BYTE_2_3_3_REV 包裝的 RGB 值
GL_UNSIGNED_SHORT_5_6_5 包裝的 RGB 值
GL_UNSIGNED_SHORT_5_6_5_REV 包裝的 RGB 值
GL_UNSIGNED_SHORT_4_4_4_4 包裝的 RGB 值
GL_UNSIGNED_SHORT_4_4_4_4_REV 包裝的 RGB 值
GL_UNSIGNED_SHORT_5_5_5_1 包裝的 RGB 值
GL_UNSIGNED_SHORT_5_5_5_1_REV 包裝的 RGB 值
GL_UNSIGNED_INT_8_8_8_8 包裝的 RGB 值
GL_UNSIGNED_INT_8_8_8_8_REV 包裝的 RGB 值
GL_UNSIGNED_INT_10_10_10_2 包裝的 RGB 值
GL_UNSIGNED_INT_2_10_10_10_REV 包裝的 RGB 值
GL_UNSIGNED_INT_24_8 包裝的 RGB 值
GL_UNSIGNED_INT_10F_11F_11F_REV 包裝的 RGB 值
GL_FLOAT_32_UNSIGNED_INT_24_8_REV 包裝的 RGB 值

3. 載入紋理函數(shù)

在讀取完一張紋理貼圖之后,需要把它載入到紋理中去,一旦被載入,這些紋理就會(huì)成為當(dāng)前紋理狀態(tài)的一部分。

void glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels);

void glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);

void glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);

實(shí)際上,這三個(gè)函數(shù)是由函數(shù) glTexImage 派生出來的,在開發(fā)過程中,我們?cè)谑褂幂d入紋理時(shí),使用的glTexImage2D這個(gè)函數(shù)。

參數(shù)target:紋理維度GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數(shù)level:指定所加載的mip貼圖層次,一般我們都把這個(gè)參數(shù)設(shè)置為0
參數(shù)internalformat:每個(gè)紋理理單元中存儲(chǔ)多少顏?色成分。(從讀取像素圖時(shí)獲得)
參數(shù)width:指加載紋理的寬度
參數(shù)height:指加載紋理的高度
參數(shù)depth:指加載紋理的深度
參數(shù)border :允許為紋理理貼圖指定?一個(gè)邊界寬度。
參數(shù)format :像素?cái)?shù)據(jù)的數(shù)據(jù)類型(GL_UNSIGNED_BYTE,每個(gè)顏色分量都是一個(gè)8位無符號(hào)整數(shù))
參數(shù)type :解釋參數(shù) pixels 指向的數(shù)據(jù),告訴OpenGL 使?緩存區(qū)中的什么數(shù)據(jù)類型來存儲(chǔ)顏?分量,像素?cái)?shù)據(jù)的數(shù)據(jù)類型
參數(shù)*pixels :指向紋理圖像數(shù)據(jù)的指針

除此之外,還可以通過顏色緩沖區(qū)載入紋理數(shù)據(jù),

void glCopyTexImage1D(GLenum target,GLint level,GLenum
  internalformt,GLint x,GLint y,GLsizei width,GLint border);
void glCopyTexImage2D(GLenum target,GLint level,GLenum
  internalformt,GLint x,GLint y,GLsizei width,GLsizei
  height,GLint border);

這倆函數(shù)的類似于 glTexImage,但在這里參數(shù) x 和 參數(shù)y 在顏色緩沖區(qū)中指定了開始讀取紋理數(shù)據(jù)的位置。源緩沖區(qū)是通過 glReadBuffer 函數(shù)設(shè)置的。并不存在 glCopyTexImage3D,因?yàn)槲覀儫o法從 2D 顏色緩沖區(qū)獲取體積數(shù)據(jù)。

4.更新紋理

void glTexSubImage1D(GLenum target,GLint level,GLint xOffset,GLsizei width,GLenum
  format,GLenum type,const GLvoid *data);

void glTexSubImage2D(GLenum target,GLint level,GLint xOffset,GLint yOffset,GLsizei
  width,GLsizei height,GLenum format,GLenum type,const GLvoid *data);

void glTexSubImage3D(GLenum target,GLint level,GLint xOffset,GLint yOffset,GLint
  zOffset,GLsizei width,GLsizei height,GLsizei depth,Glenum type,const GLvoid * data);

與glTexImage函數(shù)不同的是:xOffset、yOffset 和 zOffset 參數(shù)指定了在原來的紋理貼圖中開始替換紋理數(shù)據(jù)的偏移量。width、height 和 depth 參數(shù)指定了插入到原來那個(gè)紋理中的新紋理的寬度、高度和深度

5.插入替換紋理

void glTexSubImage1D(GLenum target,GLint level,GLint xOffset,GLsizei width,GLenum
  format,GLenum type,const GLvoid *data);

void glTexSubImage2D(GLenum target,GLint level,GLint xOffset,GLint yOffset,GLsizei
  width,GLsizei height,GLenum format,GLenum type,const GLvoid *data);

void glTexSubImage3D(GLenum target,GLint level,GLint xOffset,GLint yOffset,GLint
  zOffset,GLsizei width,GLsizei height,GLsizei depth,Glenum type,const GLvoid * data);

值得注意的是:這里并沒有 glCopyTexImage 函數(shù)。這是因?yàn)轭伾彌_區(qū)是 2D的,不存在一種對(duì)應(yīng)的方法來將一個(gè)2D 彩色圖像作為一個(gè) 3D 紋理的來源。但是,我們可以使用 glCopyTexSubImage3D 函數(shù),在一個(gè)三維紋理中使用顏色緩沖區(qū)的數(shù)據(jù)來設(shè)置它的一個(gè)紋理單元平面。

6.紋理對(duì)象

1. 分配紋理對(duì)象

void glGenTextures (GLsizei n, GLuint *textures);

這個(gè)glGenTextures函數(shù)需要指定紋理對(duì)象的數(shù)量和指針,這個(gè)指針指向一個(gè)無符號(hào)整形數(shù)組,由紋理對(duì)象標(biāo)識(shí)填充

2. 綁定紋理狀態(tài)

void glBindTexture (GLenum target, GLuint texture);

參數(shù)target: GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數(shù)texture:需要綁定的紋理對(duì)象
當(dāng)紋理狀態(tài)被綁定后,在以后的紋理加載和紋理參數(shù)設(shè)置只會(huì)影響當(dāng)前綁定的紋理對(duì)象

3. 刪除綁定的紋理對(duì)象

void glDeleteTextures (GLsizei n, const GLuint *textures);

glDeleteTextures刪除紋理對(duì)象函數(shù)需要傳入對(duì)應(yīng)的紋理對(duì)象以及紋理對(duì)象的指針,指針指向的是一個(gè)無符號(hào)整形數(shù)組,由紋理對(duì)象標(biāo)識(shí)符填充

4. 測試紋理對(duì)象是否有效

GLboolean glIsTexture(GLuint texture)

如果texture是一個(gè)已經(jīng)分配空間的紋理對(duì)象,那么這個(gè)函數(shù)會(huì)返回GL_TRUE,否則返回GL_FALSE

7.設(shè)置紋理參數(shù)

void glTexParameterf (GLenum target, GLenum pname, GLfloat param);
void glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params);
void glTexParameteri (GLenum target, GLenum pname, GLint param);
void glTexParameteriv (GLenum target, GLenum pname, const GLint *params);

參數(shù)target::指定這些參數(shù)將要應(yīng)?在哪個(gè)紋理模式上,?如 GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D。(一般就設(shè)設(shè)置為GL_TEXTURE_2D)
參數(shù)pname:指定了需要設(shè)置哪個(gè)紋理參數(shù)
參數(shù)param 或 params:用于設(shè)置特定的紋理參數(shù)的值

1. 設(shè)置紋理的過濾方式
根據(jù)一個(gè)放大或縮小的紋理貼圖計(jì)算顏色片段的過程稱為紋理過濾,在紋理參數(shù)時(shí),可以同時(shí)設(shè)置放大和縮小過濾器,這兩種過濾器的參數(shù)名分別是 GL_TEXTURE_MAG_FILTER 和 GL_TEXTURE_MIN_FILTER。我們可以為它們從兩種基本的紋理過濾器 GL_NEAREST 和 GL_LINEAR 中進(jìn)行選擇,它們分別對(duì)應(yīng)于鄰近過濾和線性過濾。

鄰近過濾GL_NEAREST
鄰近過濾是把最鄰近的紋理單元應(yīng)用到紋理坐標(biāo)中,指的是紋理坐標(biāo)最靠近哪個(gè)紋素,就用哪個(gè)紋素(這是OpenGL默認(rèn)的過濾方式,速度最快,但是效果最差)

鄰近過濾.png

上圖中的“+”號(hào)代表紋理像素的坐標(biāo),那么這個(gè)像素點(diǎn)再讀取紋理的時(shí)候會(huì)取它鄰近的顏色值。

線性過濾
線性過濾會(huì)把這個(gè)紋理坐標(biāo)周圍的紋理單元的加權(quán)平均值應(yīng)用到這個(gè)紋理坐標(biāo)上(線性插值),一個(gè)紋理像素的中心距離紋理坐標(biāo)越近,那么這個(gè)紋理像素的顏色對(duì)最終的樣本顏色的貢獻(xiàn)越大。(這是OpenGL應(yīng)用最廣泛的一種方式,效果一般,速度較快)

線性過濾.png
當(dāng)你有一張低分辨率的紋理圖,但是需要用到一個(gè)非常大的物體上時(shí),下面我們來看一下這兩種過濾方式的效果:
過濾效果.png

很明顯,鄰近過濾的像素痕跡非常明顯,一塊一塊的。而線性過濾的方式效果就好上很多了,雖然感覺很模糊,但我們完全能理解一張小圖放大之后會(huì)模糊這件事。
可以用下面這個(gè)函數(shù)來設(shè)置過濾方式:

void glTexParameteri (GLenum target, GLenum pname, GLint param);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  //縮小時(shí)的鄰近過濾方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  //放大時(shí)的線性過濾方式

四個(gè)組合方式的過濾:

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_HEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_HEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

2. 設(shè)置紋理的環(huán)繞方式
通常,紋理坐標(biāo)的范圍在(0,0)到(1,1)之間,使它與紋理貼圖中的紋理單元形成映射關(guān)系。但是如果我們制定的坐標(biāo)在這之外呢?默認(rèn)情況下,OpenGL會(huì)重復(fù)繪制紋理圖,不過與此同時(shí)OpenGL也提供了更多的環(huán)繞方式:

環(huán)繞方式 描述
GL_REPEAT 對(duì)紋理的默認(rèn)行為,重復(fù)紋理圖像
GL_MIRRORED_REPEAT 和GL_REPEAT一樣,但每次重復(fù)圖片是鏡像放置的
GL_CLAMP_TO_EDGE 紋理坐標(biāo)會(huì)被約束在0到1之間,超出的部分會(huì)重復(fù)紋理坐標(biāo)的邊緣,產(chǎn)生一種邊緣被拉伸的效果
GL_CLAMP_TO_BORDER 超出的坐標(biāo)為用戶指定的邊緣顏色

下面來看一下這四種環(huán)繞方式的效果:


環(huán)繞方式效果.png

設(shè)置紋理環(huán)繞方式的方法是調(diào)用glTexParameteri函數(shù),具體方式如下:

//橫坐標(biāo)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    
//縱坐標(biāo)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    

參數(shù)1:紋理維度。
GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數(shù)2:為S/T坐標(biāo)設(shè)置模式。
GL_TEXTURE_WRAP_S、GL_TEXTURE_T、GL_TEXTURE_R,針對(duì)s,t,r坐標(biāo)
參數(shù)3:wrapMode,環(huán)繞模式。
GL_REPEAT、
GL_CLAMP、
GL_CLAMP_TO_EDGE、GL_CLAMP_TO_BORDER
(1) GL_REPEAT: OpenGL 在紋理坐標(biāo)超過1.0的?向上對(duì)紋理理進(jìn)?重復(fù);
(2) GL_CLAMP:所需的紋理單元取自紋理邊界或TEXTURE_BORDER_COLOR.
(3) GL_CLAMP_TO_EDGE:環(huán)繞模式強(qiáng)制對(duì)范圍之外的紋理坐標(biāo)沿著合法的紋理單元的最后一?或者最后一列來進(jìn)行采樣。
(4) GL_CLAMP_TO_BORDER:在紋理坐標(biāo)在0.0到1.0范圍之外的只使?邊界紋理單元。邊界紋理單元是作為圍繞基本圖像的額外的行和列,并與基本紋理圖像?起加載的。

至于當(dāng)設(shè)定了GL_CLAMP_TO_BORDER的環(huán)繞方式,想要指定邊界顏色,就需要使用glTexParameterfv函數(shù)了,像這樣:

float borderColor[] = { 0.0f, 1.0f, 0.0f, 1.0f };  //指定成綠色
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); 

設(shè)置顏色需要在設(shè)置了環(huán)繞方式之后使用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容