七喝检、OpenGL紋理

OpenGL紋理

1. 認識函數(shù)

  • 像素存儲方式
//改變像素存儲方式
void glPixelStorei(GLenum pname, GLint param);
//恢復像素存儲方式
void glPixelStoref(GLenum pname, GLfloat param);
//舉例例:
//參數(shù)1:GL_UNPACK_ALIGNMENT 指定OpenGL 如何從數(shù)據(jù)緩存區(qū)中解包圖像 數(shù)據(jù)
//參數(shù)2:表示參數(shù)GL_UNPACK_ALIGNMENT 設置的值
//GL_UNPACK_ALIGNMENT 指內(nèi)存中每個像素?起點的排列請求,允許設置為1 (byte排列)、2(排列為偶數(shù)byte的?)矩动、4(字word排列)等限、8(?從雙字節(jié) 邊界開始)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  • 從顏色緩存區(qū)內(nèi)容作為像素圖直接讀取
//參數(shù)1:x,矩形左下?角的窗?坐標
//參數(shù)2:y,矩形左下?角的窗?坐標
//參數(shù)3:width,矩形的寬爸吮,以像素為單位 //參數(shù)4:height,矩形的?,以像素為單位
//參數(shù)5:format,OpenGL 的像素格式望门,參考 表6-1 //參數(shù)6:type,解釋參數(shù)pixels指向的數(shù)據(jù)形娇,告訴OpenGL 使?緩存區(qū)中的什么 數(shù)據(jù)類型來存儲顏?分量,像素數(shù)據(jù)的數(shù)據(jù)類型筹误,參考 表6-2 //參數(shù)7:pixels,指向圖形數(shù)據(jù)的指針
void glReadPixels(GLint x, GLint y, GLSizei width, GLSizei height, GLenum format, GLenum type, const void * pixels);
glReadBuffer(mode); // 指定讀取的緩存 
glWriteBuffer(mode); // 指定寫?入的緩存
  • 載入紋理
void glTexImage1D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, void *data);
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, void * data);
void glTexImage3D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, void *data);
/*
* target:`GL_TEXTURE_1D`桐早、`GL_TEXTURE_2D`、`GL_TEXTURE_3D`厨剪。
* level:指定所加載的mip貼圖層次哄酝。?般我們都把這個參數(shù)設置為0。
* internalformat:每個紋理單元中存儲多少顏?成分祷膳。
* width陶衅、height、depth參數(shù):指加載紋理的寬度直晨、?度搀军、深度。==注意!==這些值必須是 2的整數(shù)次方勇皇。(這是因為OpenGL 舊版本上的遺留留下的?個要求奕巍。當然現(xiàn)在已經(jīng)可以?支持不不是 2的整數(shù)次方。但是開發(fā)者們還是習慣使?以2的整數(shù)次方去設置這些參數(shù)儒士。)
* border參數(shù):允許為紋理貼圖指定?個邊界寬度的止。
* format、type着撩、data參數(shù):與我們在講glDrawPixels 函數(shù)對于的參數(shù)相同
*/
  • 更新紋理
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);
  • 插入替換紋理
void glCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsize width);
void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yOffset, GLint x, GLint y, GLsizei width, GLsizei height);
void glCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width, GLsizei height);
  • 使用顏?緩存區(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);

// x,y 在顏?緩存區(qū)中指定了開始讀取紋理數(shù)據(jù)的位置; 
// 緩存區(qū)?的數(shù)據(jù)匾委,是源緩存區(qū)通過glReadBuffer設置的。

2. 紋理對象

2.1 生成紋理對象

//使?函數(shù)分配紋理對象
//指定紋理對象的數(shù)量 和 指針(指針指向?個?符號整形數(shù)組氓润,由紋理對象標識符填充)赂乐。 
void glGenTextures(GLsizei n, GLuint * textTures);
//綁定紋理狀態(tài) //參數(shù)target:GL_TEXTURE_1D、GL_TEXTURE_2D咖气、GL_TEXTURE_3D
//參數(shù)texture:需要綁定的紋理對象
void glBindTexture(GLenum target, GLunit texture);
//刪除綁定紋理對象
//紋理對象 以及 紋理對象指針(指針指向?個?符號整形數(shù)組挨措,由紋理對象標識符填充)。
void glDeleteTextures(GLsizei n, GLuint *textures); //測試紋理對象是否有效
//如果texture是?個已經(jīng)分配空間的紋理對象崩溪,那么這個函數(shù)會返回GL_TRUE,否則會返回GL_FALSE浅役。 
GLboolean glIsTexture(GLuint texture);

2.2 設置紋理參數(shù)

glTexParameterf(GLenum target, GLenum pname, GLFloat param);
glTexParameteri(GLenum target, GLenum pname, GLint param);
glTexParameterfv(GLenum target, GLenum pname, GLFloat *param);
glTexParameteriv(GLenum target, GLenum pname, GLint *param);
//參數(shù)1:target,指定這些參數(shù)將要應?在那個紋理模式上,?如GL_TEXTURE_1D伶唯、GL_TEXTURE_2D觉既、GL_TEXTURE_3D。 
//參數(shù)2:pname,指定需要設置那個紋理參數(shù)
//參數(shù)3:param,設定特定的紋理參數(shù)的值

2.3 設置過濾方式

  • 鄰近過濾(GL_NEAREST)
GL_NEAREST
  • 線性過濾(GL_LINEAR)
GL_LINEAR
GL_NEAREST vs GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) //紋理縮?時,使用鄰近過濾
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) //紋理放大時,使?線性過濾

2.4 設置環(huán)繞方式

環(huán)繞方式
環(huán)繞方式效果對比
//參數(shù)1:GL_TEXTURE_1D乳幸、GL_TEXTURE_2D瞪讼、GL_TEXTURE_3D 
//參數(shù)2:GL_TEXTURE_WRAP_S、GL_TEXTURE_T粹断、GL_TEXTURE_R,針對s,t,r坐標 
//參數(shù)3:GL_REPEAT符欠、GL_CLAMP、GL_CLAMP_TO_EDGE瓶埋、GL_CLAMP_TO_BORDER
//GL_REPEAT:OpenGL 在紋理坐標超過1.0的?方向上對紋理進?重復希柿。
//GL_CLAMP:所需的紋理單元取?紋理邊界或TEXTURE_BORDER_COLOR。
//GL_CLAMP_TO_EDGE環(huán)繞模式強制對范圍之外的紋理坐標沿著合法的紋理單元的最后??或者最后?列來進?采樣悬赏。
//GL_CLAMP_TO_BORDER:在紋理坐標在0.0到1.0范圍之外的只使?用邊界紋理單元。邊界紋理單元是作為圍繞基本圖像的額外的?和列娄徊,并與基本紋理圖像?起加載的闽颇。
glTextParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_S, GL_CLAMP_TO_EDGE);
glTextParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_T, GL_CLAMP_TO_EDGE);

2.5 部分枚舉

像素格式
像素數(shù)據(jù)的數(shù)據(jù)類型

UNSIGNED_BYTE_3_3_2,表示8位中3個分量占的個數(shù)寄锐,第一個分量3兵多、第二個分量3、第三個分量2橄仆。

UNSIGNED_BYTE_2_3_3_REV剩膘,表示8位中3個分量占的個數(shù),但分量是反過來的盆顾,第一個分量2怠褐、第二個分量3、第三個分量3您宪。

2.6 紋理坐標

2D紋理坐標左下角為(0,0)奈懒,右上角為(1.0, 1.0)奠涌。

2D紋理坐標

3D紋理坐標(不常用):

3D紋理坐標

補充:

頂點坐標: (x, y, z, w)

紋理坐標: (s, t, r, q)

3. 常見紋理使用流程

bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
      //1.分配紋理對象 參數(shù)1:紋理對象個數(shù),參數(shù)2:紋理對象指針
    glGenTextures(1, &textureID);
    //2.綁定紋理狀態(tài) 參數(shù)1:紋理狀態(tài)2D 參數(shù)2:紋理對象
    glBindTexture(GL_TEXTURE_2D, textureID);
      
      //3磷杏、讀紋理位溜畅,讀取像素
    GLbyte *pBits;
    int nWidth, nHeight, nComponents;
    GLenum eFormat;
    //參數(shù)1:紋理文件名稱
    //參數(shù)2:文件寬度地址
    //參數(shù)3:文件高度地址
    //參數(shù)4:文件組件地址
    //參數(shù)5:文件格式地址
    //返回值:pBits,指向圖像數(shù)據(jù)的指針
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if(pBits == NULL)
        return false;
    
    //4、設置紋理參數(shù)
    //參數(shù)1:紋理維度
    //參數(shù)2:為S/T坐標設置模式
    //參數(shù)3:wrapMode,環(huán)繞模式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
    
    //參數(shù)1:紋理維度
    //參數(shù)2:線性過濾
    //參數(shù)3:wrapMode,環(huán)繞模式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);

    //5.載入紋理
    //參數(shù)1:紋理維度
    //參數(shù)2:mip貼圖層次
    //參數(shù)3:紋理單元存儲的顏色成分(從讀取像素圖是獲得)
    //參數(shù)4:加載紋理寬
    //參數(shù)5:加載紋理高
    //參數(shù)6:加載紋理的深度
    //參數(shù)7:像素數(shù)據(jù)的數(shù)據(jù)類型(GL_UNSIGNED_BYTE极祸,每個顏色分量都是一個8位無符號整數(shù))
    //參數(shù)8:指向紋理圖像數(shù)據(jù)的指針
    glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBits);
    
    //使用完畢釋放pBits
    free(pBits);
    
    //只有minFilter 等于以下四種模式慈格,才可以生成Mip貼圖
    //GL_NEAREST_MIPMAP_NEAREST具有非常好的性能,并且閃爍現(xiàn)象非常弱
    //GL_LINEAR_MIPMAP_NEAREST常常用于對游戲進行加速遥金,它使用了高質量的線性過濾器
    //GL_LINEAR_MIPMAP_LINEAR 和GL_NEAREST_MIPMAP_LINEAR 過濾器在Mip層之間執(zhí)行了一些額外的插值浴捆,以消除他們之間的過濾痕跡。
    //GL_LINEAR_MIPMAP_LINEAR 三線性Mip貼圖汰规。紋理過濾的黃金準則汤功,具有最高的精度。
    if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
       minFilter == GL_LINEAR_MIPMAP_NEAREST ||
       minFilter == GL_NEAREST_MIPMAP_LINEAR ||
       minFilter == GL_NEAREST_MIPMAP_NEAREST)
        //6.紋理生成所有的Mip層
        //參數(shù):GL_TEXTURE_1D溜哮、GL_TEXTURE_2D滔金、GL_TEXTURE_3D
        glGenerateMipmap(GL_TEXTURE_2D);
 
    return true;
}

對應demo代碼。

4. Mipmapping

4.1 簡介

Mipmapping是一個功能強大的紋理技術茂嗓,它可以提高渲染的性能以及提升場景的視覺質量餐茵,它可以用來解決一般紋理貼圖出現(xiàn)的兩個常見問題:

  • 閃爍,當屏幕上被渲染物體的表面與它所應用的紋理圖像相比顯得非常小時述吸,就會出現(xiàn)閃爍忿族,尤其當相機和物體在移動的時候,這種負面效果更容易出現(xiàn)蝌矛。
  • 性能問題道批,加載了大量紋理數(shù)據(jù)之后,還要對其縮小入撒,在屏幕上顯示的只是一小部分隆豹,紋理越大,所造成的影響越大茅逮。

當加載紋理的時候璃赡,不單單加載一個紋理,而是加載一系列從大到小的紋理mipmapped紋理献雅,然后OpenGL會根據(jù)給定的幾何圖像的大小選擇最合適的紋理碉考。

mipmap是把紋理按照2的倍數(shù)進行縮放,直到圖像1x1的大小挺身,然后把這些圖都存儲起來侯谁,當要使用就選擇一個合適的圖像。但是這會增加一些額外的內(nèi)存,在正方形的紋理貼圖中使用mipmap技術良蒸,大概要比原來多出三分之一的內(nèi)存空間技扼。

mipmap有多少個層級是有glTexImage1D、glTexImage2D載入紋理的第二個參數(shù)level決定的嫩痰。 層級從0開始剿吻,0,1串纺,2丽旅,3這樣遞增,如果沒有使用mipmap技術纺棺,只有第0層的紋理會被加載榄笙。在默認情況下, 為了使用mipmap祷蝌,所有層級都會被加載茅撞,但是我們可以用紋理參數(shù)來控制要加載的層級范圍。

//GL_TEXTURE_BASE_LEVEL指定最低層級的level
//GL_TEXTURE_MAX_LEVEL指定最高層級的level
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); 

經(jīng)過Mipmap的紋理過濾會有更多的過濾選項:

Mipmap紋理過濾

3.2 各向異性過濾

各向異性過濾并非OpenGL核心API的一部分巨朦,但其作為擴展被廣泛用于提升紋理過濾操作的質量米丘。

OpenGL使用紋理坐標計算得到紋理將映射到幾何圖形的哪一個片段上。然后通過對該位置周圍的紋理元素以GL_NEAREST過濾或GL_LINEAR過濾方式進行采樣糊啡。

當我們的視角是垂直于該幾何圖形的時候拄查,這樣的方式?jīng)]有問題。然而當我們的視角與幾何圖形形成一個斜角的時候棚蓄,以常規(guī)的方式對周邊紋理進行采樣會丟失一些紋理的信息堕扶,它看起來變模糊了。更真實和精確的采樣是梭依,沿著平面傾斜的方向稍算,拉長紋理的采樣。如下的第二個圖:

我們可以把各向異性過濾應用去基本的和mipmap方式的紋理過濾模式上役拴。

我們可以對比一下:

  • 使用GL_NEAREST或GL_LINEAR時糊探,貼圖都非常清晰,近處和遠處的銳度很高扎狱,顆粒度很強侧到,比較假勃教。
GL_NEAREST或GL_LINEAR
  • 使用mipmap技術后淤击,遠處的貼圖開始變得比較模糊,開始有點符合真實場景了故源,遠處的看不清比較模糊污抬,但有點太模糊了,特別是沒多遠的貼圖也比較模糊。
mipmap技術
  • 使用mipmap技術印机,同時使用各向異性過濾矢腻,效果就比較真實、自然了射赛。
mipmap+各向異性過濾

對應demo代碼多柑。

5. 壓縮紋理

  • 通?壓縮紋理格式
壓縮格式
  • 判斷壓縮 與 選擇壓縮?方式
GLint comFlag;
//判斷紋理理是否被成功壓縮
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &comFlag);
//根據(jù)選擇的壓縮紋理理格式,選擇最快楣责、最優(yōu)竣灌、?自?行行選擇的算法?方式選擇壓縮格式。
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST);
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_DONT_CARE);
  • glGetTexLevelParameter函數(shù)提取的壓縮紋理格式
壓縮格式
  • 加載壓縮紋理
void glCompressedTexImage1D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imageSize, void *data);
void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint heigth, GLint border, GLsizei imageSize, void *data);
void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei heigth, GLsizei depth, GLint border, GLsizei imageSize, void *data);
//target:GL_TEXTURE_1D秆麸、GL_TEXTURE_2D初嘹、GL_TEXTURE_3D
//level:指定所加載的mip貼圖層次。?般我們都把這個參數(shù)設置為0沮趣。
//internalformat:每個紋理單元中存儲多少顏?成分屯烦。 
//width、height房铭、depth參數(shù):指加載紋理的寬度驻龟、高度、深度育叁。
//==注意!==
//這些值必須是2的整數(shù)次?迅脐。這是因為舊版本上的遺留下的?個要求。當然現(xiàn)在已經(jīng)可以?持不是2的整數(shù)次方豪嗽。但是開發(fā)者們還是習慣使用2的數(shù)次?參數(shù)谴蔑。
//border:允許為紋理理貼圖指定?一個邊界寬度。
//format龟梦、type隐锭、data參數(shù):與我們在講glDrawPixels 函數(shù)對于的參數(shù)相同
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市计贰,隨后出現(xiàn)的幾起案子钦睡,更是在濱河造成了極大的恐慌,老刑警劉巖躁倒,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荞怒,死亡現(xiàn)場離奇詭異,居然都是意外死亡秧秉,警方通過查閱死者的電腦和手機褐桌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來象迎,“玉大人荧嵌,你說我怎么就攤上這事鸿染≈匝剩” “怎么了篱瞎?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵博肋,是天一觀的道長。 經(jīng)常有香客問我赃春,道長愉择,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任织中,我火速辦了婚禮薄辅,結果婚禮上,老公的妹妹穿的比我還像新娘抠璃。我一直安慰自己站楚,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布搏嗡。 她就那樣靜靜地躺著窿春,像睡著了一般。 火紅的嫁衣襯著肌膚如雪采盒。 梳的紋絲不亂的頭發(fā)上旧乞,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音磅氨,去河邊找鬼尺栖。 笑死,一個胖子當著我的面吹牛烦租,可吹牛的內(nèi)容都是我干的延赌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼叉橱,長吁一口氣:“原來是場噩夢啊……” “哼挫以!你這毒婦竟也來了?” 一聲冷哼從身側響起窃祝,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤掐松,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粪小,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體大磺,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年探膊,在試婚紗的時候發(fā)現(xiàn)自己被綠了杠愧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡突想,死狀恐怖殴蹄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猾担,我是刑警寧澤袭灯,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站绑嘹,受9級特大地震影響稽荧,放射性物質發(fā)生泄漏。R本人自食惡果不足惜工腋,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一姨丈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擅腰,春花似錦蟋恬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渗勘,卻和暖如春沐绒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背旺坠。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工乔遮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人取刃。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓蹋肮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親璧疗。 傳聞我的和親對象是個殘疾皇子括尸,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345