欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Opengl?ES之紋理貼圖使用示例

 更新時間:2022年09月29日 11:51:02   作者:思想覺悟  
這篇文章主要為大家介紹了Opengl?ES之紋理貼圖使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

紋理可以理解為一個二維數(shù)組,它可以存儲大量的數(shù)據(jù),這些數(shù)據(jù)可以發(fā)送到著色器上。一般情況下我們所說的紋理是表示一副2D圖,此時紋理存儲的數(shù)據(jù)就是這個圖的像素數(shù)據(jù)。

所謂的紋理貼圖,就是使用Opengl將這個紋理數(shù)據(jù)渲染出來,這個過程有點像裝修工人給墻體貼瓷磚,而瓷磚好比作紋理。

紋理坐標

如果為了將一副紋理圖貼到Opengl繪制的一個矩形上,那么就需要解決一個問題,如何知道矩形的具體某個點對應(yīng)紋理圖的某個點呢?為了解決這個問題就引出了紋理坐標, 通過矩形的頂點坐標與紋理坐標關(guān)聯(lián),這樣就明確了每個頂點應(yīng)該顯示紋理圖的那部分像素數(shù)據(jù)。

紋理坐標在x和y軸上,范圍為0到1之間。使用紋理坐標獲取紋理顏色叫做采樣(Sampling)。紋理坐標起始于(0, 0),也就是紋理圖片的左下角,終始于(1, 1),即紋理圖片的右上角,如下圖所示:

紋理環(huán)繞

紋理坐標的值介于0到1之間,如果我們把紋理坐標設(shè)置成大于1那么會發(fā)生什么呢?OpenGL默認的行為是重復(fù)這個紋理圖像,那么利用這個默認的特性我們能做些什么呢?那么比較火的抖音四分屏、九分屏濾鏡不就是可以用這個特性巧妙地實現(xiàn)嗎。

以下是通過改變紋理坐標實現(xiàn)四分屏和九分屏的一個小技巧:

//?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)紋理坐標超過1這個范圍時,Opengl也提供了其他的選擇,例如:

GL_REPEAT?//?對紋理的默認行為。重復(fù)紋理圖像。
GL_MIRRORED_REPEAT?//和GL_REPEAT一樣,但每次重復(fù)圖片是鏡像放置的。
GL_CLAMP_TO_EDGE?//紋理坐標會被約束在0到1之間,超出的部分會重復(fù)紋理坐標的邊緣,產(chǎn)生一種邊緣被拉伸的效果。
GL_CLAMP_TO_BORDER?//超出的坐標為用戶指定的邊緣顏色。

紋理環(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);

紋理過濾

紋理過濾實際就是紋理在放大縮小的過程中像素的處理方式。其中在Opengl ES常用的兩種紋理過濾方式是GL_NEAREST(鄰近過濾)和GL_LINEAR(也叫線性過濾)。

  • GL_NEAREST是OpenGL默認的紋理過濾方式。當(dāng)設(shè)置為GL_NEAREST的時候,OpenGL會選擇中心點最接近紋理坐標的那個像素。
  • GL_LINEAR(也叫線性過濾,(Bi)linear Filtering)它會基于紋理坐標附近的紋理像素,計算出一個插值,近似出這些紋理像素之間的顏色。一個紋理像素的中心距離紋理坐標越近,那么這個紋理像素的顏色對最終的樣本顏色的貢獻越大。

GL_NEAREST產(chǎn)生了顆粒狀的圖案,我們能夠清晰看到組成紋理的像素,而GL_LINEAR能夠產(chǎn)生更平滑的圖案,很難看出單個的紋理像素。

同理,紋理過濾特性也是通過函數(shù)glTexParameteri設(shè)置:

glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MIN_FILTER,?GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MAG_FILTER,?GL_LINEAR);

紋理單元

紋理單元的主要目的是讓我們在著色器中可以使用多于一個的紋理。通過把紋理單元賦值給采樣器,我們可以一次綁定多個紋理,只要我們首先激活對應(yīng)的紋理單元。 例如使用Opengl ES對視頻解碼后的YUV進行渲染就需要用到紋理單元的相關(guān)知識點。

Opengl中紋理的使用

在Opengl中使用紋理主要有以下幾個步驟:

  • 創(chuàng)建紋理glGenTextures
  • 激活紋理glActiveTexture
  • 綁定紋理glBindTexture,傳遞特定的紋理id進行綁定
  • 上傳紋理數(shù)據(jù)glTexImage2D
  • 解除紋理綁定,glBindTexture,傳遞0進行解除綁定

紋理坐標映射關(guān)系

在了解紋理貼圖之前我們先回顧一下三個坐標系統(tǒng),分別是紋理坐標系統(tǒng)、手機屏幕坐標系統(tǒng)、Opengl坐標系統(tǒng)。這三個坐標系統(tǒng)的的原點各不相同,紋理坐標系統(tǒng)我們上面已經(jīng)介紹過了,這里不再重復(fù)。而手機屏幕坐標系統(tǒng)則是原點位于左上角,X軸向右為正,Y軸向下為正的坐標系統(tǒng)。 而Opengl坐標系統(tǒng)則是原點位于中心,X軸向右為正,Y軸向下為正,其值介于-1到1之間的一套坐標系統(tǒng)。

既然紋理貼圖就像裝修工人貼瓷磚一樣,那么直接將紋理坐標和Opengl的頂點坐標一一對應(yīng)起來即可,也就是如下圖:

我們按照這個映射關(guān)系建立貼圖:

//?頂點坐標,使用繪制兩個三角形組成一個矩形的形式(三角形帶)
//?第一第二第三個點組成一個三角形,第二第三第四個點組成一個三角形
const?static?GLfloat?VERTICES[]?=?{
????????0.5f,-0.5f,?//?右下
????????0.5f,0.5f,?//?右上
????????-0.5f,-0.5f,?//?左下
????????-0.5f,0.5f?//?左上
};
//?紋理坐標(原點在左下角,這樣貼圖看到的會是倒置的
const?static?GLfloat?TEXTURE_COORD[]?=?{
????????1.0f,0.0f,?//?右下
????????1.0f,1.0f,?//?右上
????????0.0f,0.0f,?//?左下
????????0.0f,1.0f?//?左上
};

運行發(fā)現(xiàn)圖是貼上去了,但是看到的貼圖卻是倒置的,如下:

這是為什么呢?

因為紋理的生成是由圖片像素來生成的,而圖像的存儲是從左上角開始的,但是紋理坐標原點卻是在左下角的(筆者也不知道為什么要這么奇葩),所以就產(chǎn)生了倒置現(xiàn)象,因此正確的映射關(guān)系應(yīng)該是以圖片的左上角為原點做映射才對,而這也剛好與手機屏幕坐標系統(tǒng)匹配。

也就說正確的映射關(guān)系是需要先將以左下角為原點的紋理坐標進行倒置,然后再建立映射關(guān)系,這也是為什么有些博客說紋理坐標的原點是在左上角的原因(其實這是不對的,紋理坐標就是在圖片的左下角,說在左上角的就是一個技巧),那么紋理坐標倒置后再映射如圖: !

廢話少說,放碼過來...

#include?"TextureMapOpengl.h"
#include?"../utils/Log.h"
//?頂點著色器
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"
??????????????????????????????"}";
//?使用繪制兩個三角形組成一個矩形的形式(三角形帶)
//?第一第二第三個點組成一個三角形,第二第三第四個點組成一個三角形
const?static?GLfloat?VERTICES[]?=?{
????????0.5f,-0.5f,?//?右下
????????0.5f,0.5f,?//?右上
????????-0.5f,-0.5f,?//?左下
????????-0.5f,0.5f?//?左上
};
//?紋理坐標(原點在左下角,這樣貼圖看到的會是倒置的
//const?static?GLfloat?TEXTURE_COORD[]?=?{
//????????1.0f,0.0f,?//?右下
//????????1.0f,1.0f,?//?右上
//????????0.0f,0.0f,?//?左下
//????????0.0f,1.0f?//?左上
//};
//?貼圖紋理坐標(參考手機屏幕坐標系統(tǒng),原點在左上角)
//由于對一個OpenGL紋理來說,它沒有內(nèi)在的方向性,因此我們可以使用不同的坐標把它定向到任何我們喜歡的方向上,然而大多數(shù)計算機圖像都有一個默認的方向,它們通常被規(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);
????//?激活紋理,注意以下這個兩句是搭配的,glActiveTexture激活的是那個紋理,就設(shè)置的sampler2D是那個
????//?默認是0,如果不是0的話,需要在onDraw的時候重新激活一下?
//????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?幾個數(shù)字表示一個點,顯示是兩個數(shù)字表示一個點
?????*?normalized?是否需要歸一化,不用,這里已經(jīng)歸一化了
?????*?stride?步長,連續(xù)頂點之間的間隔,如果頂點直接是連續(xù)的,也可填0
?????*/
????//?啟用頂點數(shù)據(jù)
????glEnableVertexAttribArray(positionHandle);
????glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
????//?紋理坐標
????glEnableVertexAttribArray(textureHandle);
????glVertexAttribPointer(textureHandle,2,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);
????//?4個頂點繪制兩個三角形組成矩形
?????glDrawArrays(GL_TRIANGLE_STRIP,0,4);
????glUseProgram(0);
????//?禁用頂點
????glDisableVertexAttribArray(positionHandle);
????if(nullptr?!=?eglHelper){
????????eglHelper->swapBuffers();
????}
????glBindTexture(GL_TEXTURE_2D,?0);
}
TextureMapOpengl::~TextureMapOpengl()?{
????LOGD("TextureMapOpengl析構(gòu)函數(shù)");
}

仔細看注釋多理解...

紋理貼圖運行結(jié)果

以上就是Opengl ES之紋理貼圖使用示例的詳細內(nèi)容,更多關(guān)于Opengl ES 紋理貼圖的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論