OpenGL ES 入門之旅 -- GLSL紋理單元和紋理翻轉解決策略

從上一篇文章中我們了解到片元著色器是如何編寫的:
片元著色器
片元著色器shaderf.fsh

//傳遞過來的紋理坐標
varying lowp vec2 varyTextCoord;
// 紋理采樣器 (獲取對應的紋理ID)
uniform sampler2D colorMap;

void main() {
//將紋理顏色添加到對應的像素點上
 gl_FragColor = texture2D(colorMap, varyTextCoord);
//返回值應該是一個vec4 即是RGBA--顏色值挟冠。
}

gl_FragColor GLSL內建變量 (賦值像素點顏色值)GLSL語言已經提前定義好的變量刃泌,有相應的特殊含義寇甸。
內建函數 GLSL提前封裝好的函數
texture2D(紋理采樣器增显,紋理坐標),獲取對應坐標紋素(讀取紋素涨享,讀取每一個像素點的顏色值)筋搏。

我們知道sampler(采樣器)是GLSL提供的可供紋理對象使用的內建數據,而且sampler通常實在片元著色器中內定義厕隧,被uniform修飾符修飾奔脐,表示這個變量是不會被修改的。
通過上面的代碼可以看到聲明sampler的類型還有一個sampler2D吁讨。這個只是代表一個二維的紋理類型髓迎。sampler1D,sampler2D,sampler3D 表示不同維度的紋理類型.

在上面的代碼中我們簡單聲明了一個紋理對象. uniform sampler2D,將一個紋理添加片元著色器中.

uniform sampler2D colorMap;

同時我們使用GLSL內建的texture函數來采樣紋理的顏色值.

 gl_FragColor = texture2D(colorMap, varyTextCoord);

紋理單元

這里聲明的sampler2D變量是個uniform,我們卻沒有用glUniform給它賦值建丧,一般來講我們需要用glUniform1i()函數進行將紋理對象(數據)從CPU中傳入顯存中的著色器排龄。之所以使用glUniform1i()函數,是因為只需要給紋理采樣器傳入一個索引值(位置)即可翎朱,這樣我們就能夠在一個片元著色器中設置多個紋理橄维。

那么這個索引值就是我們接下來要介紹的‘紋理單元’:
一個紋理的位置值通常稱為一個紋理單元(Texture Unit)尺铣。一個紋理的默認紋理單元是0,它是默認的激活紋理單元争舞。紋理單元的主要目的是讓我們在著色器中可以使用多于一個的紋理凛忿。

如果我們只傳入一個紋理對象,那么倒是不用考慮紋理單元的問題竞川。但是當有多個紋理對象要傳入的時候店溢,我們必須指定紋理對象,然后在主函數用glUniform1i()函數將紋理對象一個一個綁定到著色器內部流译。

通過把紋理單元賦值給采樣器逞怨,我們可以一次綁定多個紋理,只要我們首先激活對應的紋理單元福澡。就像glBindTexture一樣,我們可以使用管理ActiveTexture激活紋理單元驹马,傳入我們需要使用的紋理單元革砸。

//在綁定紋理之前先激活紋理單元
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

激活紋理單元之后,接下來的glBindTexture函數調用會綁定這個紋理到當前激活的紋理單元糯累,紋理單元GL_TEXTURE0默認總是被激活.

OpenGL提供有16個紋理單元供我們使用算利,也就是說我們可以激活從GL_TEXTURE0到GL_TEXTRUE15。它們都是按順序定義的泳姐,所以我們也可以通過GL_TEXTURE0 + 8的方式獲得GL_TEXTURE8效拭,這在當我們需要循環(huán)一些紋理單元的時候會很有用。

紋理混合
GLSL內建的mix函數會將兩個紋理進行結合并輸出最終顏色值胖秒。

varying vec2 TexCoord;
uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;
void main()
{
    gl_FragColor = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
}
genType mix (genType x, genType y, float a)
返回線性混合的x和y缎患,如:x?(1?a)+y?a

mix函數需要接受兩個值作為參數,并對它們根據第三個參數進行線性插值(線性插值是一種針對一維數據的插值方法阎肝,它根據一維數據序列中需要插值的點的左右鄰近兩個數據點來進行數值的估計挤渔。當然了它不是求這兩個點數據大小的平均值(當然也有求平均值的情況),而是根據到這兩個點的距離來分配它們的比重的)风题。

例如:如果第三個值是0.0判导,它會返回第一個輸入;如果是1.0沛硅,會返回第二個輸入值眼刃。輸入0.2則會返回80%的第一個輸入顏色和20%的第二個輸入顏色,即返回兩個紋理的混合色摇肌。

設置多個紋理

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);  // 手動設置

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

注意擂红,我們使用glUniform1i設置uniform采樣器的位置值,或者說紋理單元朦蕴。通過glUniform1i的設置篮条,我們保證每個uniform采樣器對應著正確的紋理單元

其實弟头,使用glUniform1i()函數作為著色器內部和程序來進行傳入值,需要知道兩個參數涉茧,一個是在著色器內部接受信息的對象為位置赴恨。一個是外界的數據對象,嚴格來講傳入數據本身也不是這個函數做的伴栓,這個函數只是告訴著色器那個紋理對象對應哪個采樣器對象伦连。
流程.png

重點介紹一下glTexImage2D函數:

glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
參數1:紋理模式(綁定紋理對象的種類),GL_TEXTURE_1D钳垮、GL_TEXTURE_2D惑淳、GL_TEXTURE_3D
參數2:加載的層次,一般設置為0, 0表示沒有進行縮小的原始圖片等級饺窿。
參數3:紋理的顏色值GL_RGBA, 表示了紋理所采用的內部格式歧焦,內部格式是我們的像素數據在顯卡中存儲的格式,這里的GL_RGB顯然就表示紋理中像素的顏色值是以RGB的格式存儲的肚医。
參數4:紋理的寬
參數5:紋理的高
參數6:border绢馍,邊界寬度,通常為0.
參數7:format(描述了像素在內存中的存儲格式)
參數8:type(描述了像素在內存中的數據類型)
參數9:紋理數據

紋理翻轉
在使用OpenGL函數加載紋理到圖形時肠套,經常遇到紋理上下顛倒的問題舰涌。原因是因為OpenGL要求紋理坐標原點(0,0)在左下角你稚。

紋理坐標.png
而圖片中像素的存儲順序是從左上到右下的瓷耙,因此我們需要對我們的坐標系進行一次Y軸的“翻轉”,保持原點坐標一致刁赖。

  1. 利用旋轉矩陣翻轉圖形搁痛,不翻轉紋理
    即讓圖形的頂點坐標旋轉180度,而紋理坐標保持不變乾闰。
//rotate等于shaderv.vsh中的uniform屬性落追,rotateMatrix
GLuint rotate = glGetUniformLocation(self.myPrograme, "rotateMatrix");
//獲取渲旋轉的弧度
float radians = 180 * 3.14159f / 180.0f;
//求得弧度對于的sin\cos值
float s = sin(radians);
float c = cos(radians);
    
GLfloat zRotation[16] = {
        c, -s, 0, 0,
        s, c, 0, 0,
        0, 0, 1.0, 0,
        0.0, 0, 0, 1.0
 };
//設置旋轉矩陣
glUniformMatrix4fv(rotate, 1, GL_FALSE, (GLfloat *)&zRotation[0]);
/*
glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
location : 對于shader 中的ID
count : 個數
transpose : 轉置
value : 指針
*/

2.解壓圖片時,將圖片源文件翻轉

//將 UIImage 轉換為 CGImageRef
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
//讀取圖片的大小,寬和高
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
//獲取圖片字節(jié)數 寬*高*4(RGBA)
GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
//創(chuàng)建上下文
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
//在CGContextRef上,將圖片繪制出來
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);

CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
CGContextTranslateCTM(spriteContext, 0, rect.size.height);
CGContextScaleCTM(spriteContext, 1.0, -1.0);
CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
CGContextDrawImage(spriteContext, rect, spriteImage); 
//釋放上下文
CGContextRelease(spriteContext);
//綁定紋理到默認的紋理ID
glBindTexture(GL_TEXTURE_2D, 0);

CGContextDrawImage 使用的是Core Graphics框架涯肩,坐標系與UIKit 不一樣轿钠。UIKit框架的原點在屏幕的左上角,Core Graphics框架的原點在屏幕的左下角病苗。
CGContextDrawImage
參數1:繪圖上下文
參數2:rect坐標
參數3:繪制的圖片

3.修改片元著色器的紋理坐標

varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
    //gl_FragColor = texture2D(colorMap, varyTextCoord);
    gl_FragColor = texture2D(colorMap, vec2(varyTextCoord.x,1.0-varyTextCoord.y));
//因為紋理坐標的范圍是0-1疗垛,所以翻轉的話都統(tǒng)一用1去減 
}

4.修改頂點著色器紋理坐標

attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;

void main()
{
    //varyTextCoord = textCoordinate;
    varyTextCoord = vec2(textCoordinate.x,1.0-textCoordinate.y);
    gl_Position = position;

//因為紋理坐標的范圍是0-1,所以翻轉的話都統(tǒng)一用1去減 
}

同時也可以在頂點著色器中直接翻轉頂點坐標:

attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;

void main()
{
    varyTextCoord = textCoordinate;
    //varyTextCoord = vec2(textCoordinate.x,1.0-textCoordinate.y);
    gl_Position = vec4(position.x硫朦,-position.y贷腕,position.z,1.0f);
}
//在翻轉頂點時,就不是直接對Y值用1去減泽裳,因為頂點的取值范圍是-1 - 1 ,所以我們直接加上負號做翻轉即可

5.直接從源紋理坐標數據修改

//原始坐標
GLfloat attrArr[] =
     {
     0.5f, -0.5f, 0.0f,        1.0f, 1.0f, //右下
     -0.5f, 0.5f, 0.0f,        0.0f, 0.0f, // 左上
     -0.5f, -0.5f, 0.0f,       0.0f, 1.0f, // 左下
     0.5f, 0.5f, 0.0f,         1.0f, 0.0f, // 右上
     -0.5f, 0.5f, 0.0f,        0.0f, 0.0f, // 左上
     0.5f, -0.5f, 0.0f,        1.0f, 1.0f, // 右下
     };
//更改后的坐標
GLfloat attrArr[] =
    {
        0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
        -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
        -0.5f, -0.5f, -1.0f,    0.0f, 0.0f,
        
        0.5f, 0.5f, -1.0f,      1.0f, 1.0f,
        -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
        0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
    };

翻轉前效果圖:
翻轉前.png

翻轉后效果圖:
翻轉后.png

文中部分內容參考:CC老師 http://www.reibang.com/p/848d982db9f2

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末瞒斩,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子涮总,更是在濱河造成了極大的恐慌胸囱,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瀑梗,死亡現場離奇詭異烹笔,居然都是意外死亡,警方通過查閱死者的電腦和手機抛丽,發(fā)現死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門谤职,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亿鲜,你說我怎么就攤上這事允蜈。” “怎么了狡门?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵陷寝,是天一觀的道長。 經常有香客問我其馏,道長,這世上最難降的妖魔是什么爆安? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任叛复,我火速辦了婚禮,結果婚禮上扔仓,老公的妹妹穿的比我還像新娘褐奥。我一直安慰自己,他們只是感情好翘簇,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布撬码。 她就那樣靜靜地躺著,像睡著了一般版保。 火紅的嫁衣襯著肌膚如雪呜笑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天彻犁,我揣著相機與錄音叫胁,去河邊找鬼。 笑死汞幢,一個胖子當著我的面吹牛驼鹅,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼输钩,長吁一口氣:“原來是場噩夢啊……” “哼豺型!你這毒婦竟也來了?” 一聲冷哼從身側響起买乃,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤姻氨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后为牍,有當地人在樹林里發(fā)現了一具尸體哼绑,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年碉咆,在試婚紗的時候發(fā)現自己被綠了抖韩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡疫铜,死狀恐怖茂浮,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情壳咕,我是刑警寧澤席揽,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站谓厘,受9級特大地震影響幌羞,放射性物質發(fā)生泄漏。R本人自食惡果不足惜竟稳,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一属桦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧他爸,春花似錦聂宾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讨跟,卻和暖如春纪他,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背许赃。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工止喷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人混聊。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓弹谁,卻偏偏與公主長得像乾巧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子预愤,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容