Opengl?ES之紋理貼圖使用示例
正文
紋理可以理解為一個(gè)二維數(shù)組,它可以存儲大量的數(shù)據(jù),這些數(shù)據(jù)可以發(fā)送到著色器上。一般情況下我們所說的紋理是表示一副2D圖,此時(shí)紋理存儲的數(shù)據(jù)就是這個(gè)圖的像素?cái)?shù)據(jù)。
所謂的紋理貼圖,就是使用Opengl將這個(gè)紋理數(shù)據(jù)渲染出來,這個(gè)過程有點(diǎn)像裝修工人給墻體貼瓷磚,而瓷磚好比作紋理。
紋理坐標(biāo)
如果為了將一副紋理圖貼到Opengl繪制的一個(gè)矩形上,那么就需要解決一個(gè)問題,如何知道矩形的具體某個(gè)點(diǎn)對應(yīng)紋理圖的某個(gè)點(diǎn)呢?為了解決這個(gè)問題就引出了紋理坐標(biāo), 通過矩形的頂點(diǎn)坐標(biāo)與紋理坐標(biāo)關(guān)聯(lián),這樣就明確了每個(gè)頂點(diǎn)應(yīng)該顯示紋理圖的那部分像素?cái)?shù)據(jù)。
紋理坐標(biāo)在x和y軸上,范圍為0到1之間。使用紋理坐標(biāo)獲取紋理顏色叫做采樣(Sampling)。紋理坐標(biāo)起始于(0, 0),也就是紋理圖片的左下角,終始于(1, 1),即紋理圖片的右上角,如下圖所示:

紋理環(huán)繞
紋理坐標(biāo)的值介于0到1之間,如果我們把紋理坐標(biāo)設(shè)置成大于1那么會發(fā)生什么呢?OpenGL默認(rèn)的行為是重復(fù)這個(gè)紋理圖像,那么利用這個(gè)默認(rèn)的特性我們能做些什么呢?那么比較火的抖音四分屏、九分屏濾鏡不就是可以用這個(gè)特性巧妙地實(shí)現(xiàn)嗎。
以下是通過改變紋理坐標(biāo)實(shí)現(xiàn)四分屏和九分屏的一個(gè)小技巧:
//?4分屏
const?static?GLfloat?TEXTURE_COORD[]?=?{
????????2.0f,2.0f,?//?右下
????????2.0f,0.0f,?//?右上
????????0.0f,2.0f,?//?左下
????????0.0f,0.0f?//?左上
};
//?九分屏
const?static?GLfloat?TEXTURE_COORD[]?=?{
????????3.0f,3.0f,?//?右下
????????3.0f,0.0f,?//?右上
????????0.0f,3.0f,?//?左下
????????0.0f,0.0f?//?左上
};
當(dāng)然,當(dāng)紋理坐標(biāo)超過1這個(gè)范圍時(shí),Opengl也提供了其他的選擇,例如:
GL_REPEAT?//?對紋理的默認(rèn)行為。重復(fù)紋理圖像。 GL_MIRRORED_REPEAT?//和GL_REPEAT一樣,但每次重復(fù)圖片是鏡像放置的。 GL_CLAMP_TO_EDGE?//紋理坐標(biāo)會被約束在0到1之間,超出的部分會重復(fù)紋理坐標(biāo)的邊緣,產(chǎn)生一種邊緣被拉伸的效果。 GL_CLAMP_TO_BORDER?//超出的坐標(biāo)為用戶指定的邊緣顏色。

紋理環(huán)繞效果圖
以上特性可以通過函數(shù)glTexParameteri設(shè)置:
glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_WRAP_S,?GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_WRAP_T,?GL_MIRRORED_REPEAT);
紋理過濾
紋理過濾實(shí)際就是紋理在放大縮小的過程中像素的處理方式。其中在Opengl ES常用的兩種紋理過濾方式是GL_NEAREST(鄰近過濾)和GL_LINEAR(也叫線性過濾)。
- GL_NEAREST是OpenGL默認(rèn)的紋理過濾方式。當(dāng)設(shè)置為GL_NEAREST的時(shí)候,OpenGL會選擇中心點(diǎn)最接近紋理坐標(biāo)的那個(gè)像素。
- GL_LINEAR(也叫線性過濾,(Bi)linear Filtering)它會基于紋理坐標(biāo)附近的紋理像素,計(jì)算出一個(gè)插值,近似出這些紋理像素之間的顏色。一個(gè)紋理像素的中心距離紋理坐標(biāo)越近,那么這個(gè)紋理像素的顏色對最終的樣本顏色的貢獻(xiàn)越大。
GL_NEAREST產(chǎn)生了顆粒狀的圖案,我們能夠清晰看到組成紋理的像素,而GL_LINEAR能夠產(chǎn)生更平滑的圖案,很難看出單個(gè)的紋理像素。
同理,紋理過濾特性也是通過函數(shù)glTexParameteri設(shè)置:
glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MIN_FILTER,?GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MAG_FILTER,?GL_LINEAR);
紋理單元
紋理單元的主要目的是讓我們在著色器中可以使用多于一個(gè)的紋理。通過把紋理單元賦值給采樣器,我們可以一次綁定多個(gè)紋理,只要我們首先激活對應(yīng)的紋理單元。 例如使用Opengl ES對視頻解碼后的YUV進(jìn)行渲染就需要用到紋理單元的相關(guān)知識點(diǎn)。
Opengl中紋理的使用
在Opengl中使用紋理主要有以下幾個(gè)步驟:
- 創(chuàng)建紋理
glGenTextures - 激活紋理
glActiveTexture - 綁定紋理
glBindTexture,傳遞特定的紋理id進(jìn)行綁定 - 上傳紋理數(shù)據(jù)
glTexImage2D - 解除紋理綁定,
glBindTexture,傳遞0進(jìn)行解除綁定
紋理坐標(biāo)映射關(guān)系
在了解紋理貼圖之前我們先回顧一下三個(gè)坐標(biāo)系統(tǒng),分別是紋理坐標(biāo)系統(tǒng)、手機(jī)屏幕坐標(biāo)系統(tǒng)、Opengl坐標(biāo)系統(tǒng)。這三個(gè)坐標(biāo)系統(tǒng)的的原點(diǎn)各不相同,紋理坐標(biāo)系統(tǒng)我們上面已經(jīng)介紹過了,這里不再重復(fù)。而手機(jī)屏幕坐標(biāo)系統(tǒng)則是原點(diǎn)位于左上角,X軸向右為正,Y軸向下為正的坐標(biāo)系統(tǒng)。 而Opengl坐標(biāo)系統(tǒng)則是原點(diǎn)位于中心,X軸向右為正,Y軸向下為正,其值介于-1到1之間的一套坐標(biāo)系統(tǒng)。
既然紋理貼圖就像裝修工人貼瓷磚一樣,那么直接將紋理坐標(biāo)和Opengl的頂點(diǎn)坐標(biāo)一一對應(yīng)起來即可,也就是如下圖:

我們按照這個(gè)映射關(guān)系建立貼圖:
//?頂點(diǎn)坐標(biāo),使用繪制兩個(gè)三角形組成一個(gè)矩形的形式(三角形帶)
//?第一第二第三個(gè)點(diǎn)組成一個(gè)三角形,第二第三第四個(gè)點(diǎn)組成一個(gè)三角形
const?static?GLfloat?VERTICES[]?=?{
????????0.5f,-0.5f,?//?右下
????????0.5f,0.5f,?//?右上
????????-0.5f,-0.5f,?//?左下
????????-0.5f,0.5f?//?左上
};
//?紋理坐標(biāo)(原點(diǎn)在左下角,這樣貼圖看到的會是倒置的
const?static?GLfloat?TEXTURE_COORD[]?=?{
????????1.0f,0.0f,?//?右下
????????1.0f,1.0f,?//?右上
????????0.0f,0.0f,?//?左下
????????0.0f,1.0f?//?左上
};
運(yùn)行發(fā)現(xiàn)圖是貼上去了,但是看到的貼圖卻是倒置的,如下:

這是為什么呢?
因?yàn)榧y理的生成是由圖片像素來生成的,而圖像的存儲是從左上角開始的,但是紋理坐標(biāo)原點(diǎn)卻是在左下角的(筆者也不知道為什么要這么奇葩),所以就產(chǎn)生了倒置現(xiàn)象,因此正確的映射關(guān)系應(yīng)該是以圖片的左上角為原點(diǎn)做映射才對,而這也剛好與手機(jī)屏幕坐標(biāo)系統(tǒng)匹配。
也就說正確的映射關(guān)系是需要先將以左下角為原點(diǎn)的紋理坐標(biāo)進(jìn)行倒置,然后再建立映射關(guān)系,這也是為什么有些博客說紋理坐標(biāo)的原點(diǎn)是在左上角的原因(其實(shí)這是不對的,紋理坐標(biāo)就是在圖片的左下角,說在左上角的就是一個(gè)技巧),那么紋理坐標(biāo)倒置后再映射如圖: !

廢話少說,放碼過來...
#include?"TextureMapOpengl.h"
#include?"../utils/Log.h"
//?頂點(diǎn)著色器
static?const?char?*ver?=?"#version?300?es\n"
?????????????????????????"in?vec4?aPosition;\n"
?????????????????????????"in?vec2?aTexCoord;\n"
?????????????????????????"out?vec2?TexCoord;\n"
?????????????????????????"void?main()?{\n"
?????????????????????????"??TexCoord?=?aTexCoord;\n"
?????????????????????????"??gl_Position?=?aPosition;\n"
?????????????????????????"}";
//?片元著色器
static?const?char?*fragment?=?"#version?300?es\n"
??????????????????????????????"precision?mediump?float;\n"
??????????????????????????????"out?vec4?FragColor;\n"
??????????????????????????????"in?vec2?TexCoord;\n"
??????????????????????????????"uniform?sampler2D?ourTexture;\n"
??????????????????????????????"void?main()\n"
??????????????????????????????"{\n"
??????????????????????????????"????FragColor?=?texture(ourTexture,?TexCoord);\n"
??????????????????????????????"}";
//?使用繪制兩個(gè)三角形組成一個(gè)矩形的形式(三角形帶)
//?第一第二第三個(gè)點(diǎn)組成一個(gè)三角形,第二第三第四個(gè)點(diǎn)組成一個(gè)三角形
const?static?GLfloat?VERTICES[]?=?{
????????0.5f,-0.5f,?//?右下
????????0.5f,0.5f,?//?右上
????????-0.5f,-0.5f,?//?左下
????????-0.5f,0.5f?//?左上
};
//?紋理坐標(biāo)(原點(diǎn)在左下角,這樣貼圖看到的會是倒置的
//const?static?GLfloat?TEXTURE_COORD[]?=?{
//????????1.0f,0.0f,?//?右下
//????????1.0f,1.0f,?//?右上
//????????0.0f,0.0f,?//?左下
//????????0.0f,1.0f?//?左上
//};
//?貼圖紋理坐標(biāo)(參考手機(jī)屏幕坐標(biāo)系統(tǒng),原點(diǎn)在左上角)
//由于對一個(gè)OpenGL紋理來說,它沒有內(nèi)在的方向性,因此我們可以使用不同的坐標(biāo)把它定向到任何我們喜歡的方向上,然而大多數(shù)計(jì)算機(jī)圖像都有一個(gè)默認(rèn)的方向,它們通常被規(guī)定為y軸向下,X軸向右
const?static?GLfloat?TEXTURE_COORD[]?=?{
????????1.0f,1.0f,?//?右下
????????1.0f,0.0f,?//?右上
????????0.0f,1.0f,?//?左下
????????0.0f,0.0f?//?左上
};
//?四分屏??GL_REPEAT環(huán)繞方式
//const?static?GLfloat?TEXTURE_COORD[]?=?{
//????????2.0f,2.0f,?//?右下
//????????2.0f,0.0f,?//?右上
//????????0.0f,2.0f,?//?左下
//????????0.0f,0.0f?//?左上
//};
//?九分屏?GL_REPEAT環(huán)繞方式
//const?static?GLfloat?TEXTURE_COORD[]?=?{
//????????3.0f,3.0f,?//?右下
//????????3.0f,0.0f,?//?右上
//????????0.0f,3.0f,?//?左下
//????????0.0f,0.0f?//?左上
//};
TextureMapOpengl::TextureMapOpengl():BaseOpengl()?{
????initGlProgram(ver,fragment);
????positionHandle?=?glGetAttribLocation(program,"aPosition");
????textureHandle?=?glGetAttribLocation(program,"aTexCoord");
????textureSampler?=?glGetUniformLocation(program,"ourTexture");
????LOGD("program:%d",program);
????LOGD("positionHandle:%d",positionHandle);
????LOGD("textureHandle:%d",textureHandle);
????LOGD("textureSample:%d",textureSampler);
}
void?TextureMapOpengl::setPixel(void?*data,?int?width,?int?height,?int?length)?{
????LOGD("texture?setPixel");
????glGenTextures(1,?&textureId);
????//?激活紋理,注意以下這個(gè)兩句是搭配的,glActiveTexture激活的是那個(gè)紋理,就設(shè)置的sampler2D是那個(gè)
????//?默認(rèn)是0,如果不是0的話,需要在onDraw的時(shí)候重新激活一下?
//????glActiveTexture(GL_TEXTURE0);
//????glUniform1i(textureSampler,?0);
//?例如,一樣的
????glActiveTexture(GL_TEXTURE2);
????glUniform1i(textureSampler,?2);
????//?綁定紋理
????glBindTexture(GL_TEXTURE_2D,?textureId);
????//?為當(dāng)前綁定的紋理對象設(shè)置環(huán)繞、過濾方式
????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_WRAP_S,?GL_REPEAT);
????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_WRAP_T,?GL_REPEAT);
????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MIN_FILTER,?GL_LINEAR);
????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MAG_FILTER,?GL_LINEAR);
????glTexImage2D(GL_TEXTURE_2D,?0,?GL_RGBA,?width,?height,?0,?GL_RGBA,?GL_UNSIGNED_BYTE,?data);
????//?生成mip貼圖
????glGenerateMipmap(GL_TEXTURE_2D);
????glBindTexture(GL_TEXTURE_2D,?textureId);
????//?解綁定
????glBindTexture(GL_TEXTURE_2D,?0);
}
void?TextureMapOpengl::onDraw()?{
????glClearColor(0.0f,?1.0f,?0.0f,?1.0f);
????glClear(GL_COLOR_BUFFER_BIT);
????glUseProgram(program);
????//?激活紋理
????glActiveTexture(GL_TEXTURE2);
????glUniform1i(textureSampler,?2);
????//?綁定紋理
????glBindTexture(GL_TEXTURE_2D,?textureId);
????/**
?????*?size?幾個(gè)數(shù)字表示一個(gè)點(diǎn),顯示是兩個(gè)數(shù)字表示一個(gè)點(diǎn)
?????*?normalized?是否需要?dú)w一化,不用,這里已經(jīng)歸一化了
?????*?stride?步長,連續(xù)頂點(diǎn)之間的間隔,如果頂點(diǎn)直接是連續(xù)的,也可填0
?????*/
????//?啟用頂點(diǎn)數(shù)據(jù)
????glEnableVertexAttribArray(positionHandle);
????glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
????//?紋理坐標(biāo)
????glEnableVertexAttribArray(textureHandle);
????glVertexAttribPointer(textureHandle,2,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);
????//?4個(gè)頂點(diǎn)繪制兩個(gè)三角形組成矩形
?????glDrawArrays(GL_TRIANGLE_STRIP,0,4);
????glUseProgram(0);
????//?禁用頂點(diǎn)
????glDisableVertexAttribArray(positionHandle);
????if(nullptr?!=?eglHelper){
????????eglHelper->swapBuffers();
????}
????glBindTexture(GL_TEXTURE_2D,?0);
}
TextureMapOpengl::~TextureMapOpengl()?{
????LOGD("TextureMapOpengl析構(gòu)函數(shù)");
}
仔細(xì)看注釋多理解...

紋理貼圖運(yùn)行結(jié)果
以上就是Opengl ES之紋理貼圖使用示例的詳細(xì)內(nèi)容,更多關(guān)于Opengl ES 紋理貼圖的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android自定義控件實(shí)現(xiàn)按鈕滾動選擇效果
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)按鈕滾動選擇效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Android 自定義imageview實(shí)現(xiàn)圖片縮放實(shí)例詳解
這篇文章主要介紹了Android 自定義imageview實(shí)現(xiàn)圖片縮放實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android實(shí)現(xiàn)錄音功能實(shí)現(xiàn)實(shí)例(MediaRecorder)
本篇文章主要介紹了Android實(shí)現(xiàn)錄音的實(shí)例代碼(MediaRecorder),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07
Android中oncreate中獲得控件高度或?qū)挾鹊膶?shí)現(xiàn)方法
這篇文章主要介紹了Android中oncreate中獲得控件高度或?qū)挾鹊膶?shí)現(xiàn)方法的相關(guān)資料,希望通過本文大家能實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09
Android ViewPager實(shí)現(xiàn)滑動指示條功能
這篇文章主要介紹了Android-ViewPager實(shí)現(xiàn)滑動指示條功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
Android實(shí)現(xiàn)仿網(wǎng)易新聞主界面設(shè)計(jì)
這篇文章主要為大家介紹了Android實(shí)現(xiàn)仿網(wǎng)易新聞主界面設(shè)計(jì)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01
Android AutoCompleteTextView自動提示文本框?qū)嵗a
這篇文章主要介紹了Android AutoCompleteTextView自動提示文本框?qū)嵗a的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
Android 點(diǎn)擊editview以外位置實(shí)現(xiàn)隱藏輸入法
這篇文章主要介紹了Android 點(diǎn)擊editview以外位置實(shí)現(xiàn)隱藏輸入法的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android?autojs隨時(shí)翻譯剪貼板單詞實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Android?autojs隨時(shí)翻譯剪貼板單詞,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

