OpenGL ES實踐教程(五)多重紋理實現(xiàn)圖像混合

教程

OpenGL ES實踐教程1-Demo01-AVPlayer
OpenGL ES實踐教程2-Demo02-攝像頭采集數(shù)據(jù)和渲染
OpenGL ES實踐教程3-Demo03-Mirror
OpenGL ES實踐教程4-Demo04-VR全景視頻播放
其他教程請移步OpenGL ES文集

有簡書的開發(fā)者問我如何使用在一張大圖上貼一張小圖懦底,原始的需求是在檢測人臉爱咬,在返回的范圍(矩形)內(nèi)貼上一張圖片旱捧。
有幾點前提:

  • 盡量少消耗CPU航背;
  • 合成的數(shù)據(jù)是用于推流宏怔;
  • 圖片大小不一致愉豺;

說說如果沒有上述幾點前提下费彼,可能的方案:

  • 1、使用UIKit痰娱,新建一個透明的View弃榨,大小和原圖像一致,在View上面對應(yīng)的位置添加圖像梨睁;
  • 2鲸睛、使用GPUImage,選擇一個filter坡贺,添加兩個原圖像作為輸入官辈;
  • 3、使用OpenGL ES遍坟,多重紋理拳亿;

因為數(shù)據(jù)要用于推流,故而最簡單的方案1不行政鼠;
方案2可行风瘦,但是需要對GPUImage較為熟悉;
方案3相對方案2簡單公般,同時對性能的要求最低万搔,最為符合。

本文探究如何使用OpenGL ES實現(xiàn)兩個圖片的混合官帘。

核心思路

自定義shader瞬雹,傳入兩個紋理和對應(yīng)矩形的坐標(biāo);
在像素著色器內(nèi)判斷當(dāng)前點的范圍刽虹,如果處于對應(yīng)矩形內(nèi)酗捌,則進行混合操作;

效果展示

具體細(xì)節(jié)

1、編譯鏈接GLProgram

為了更方便開發(fā)胖缤,特引入Jeff LaMarche's GLProgram頭文件尚镰。

This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book.
A description of this can be found at his page on the topic:
http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html
I've extended this to be able to take programs as NSStrings in addition to files, for baked-in shaders

2、上傳頂點數(shù)據(jù)以及矩形坐標(biāo)

通過GLProgram-uniformIndex:-attributeIndex:方法哪廓,可以便捷的取到對應(yīng)屬性的索引狗唉,再通過glUniform1iglUniform2f方法可以上次數(shù)據(jù)到OpenGL ES。

    GLuint texture0Uniform = [self.mProgram uniformIndex:@"myTexture0"];
    GLuint texture1Uniform = [self.mProgram uniformIndex:@"myTexture1"];
    GLuint leftBottomUniform = [self.mProgram uniformIndex:@"leftBottom"];
    GLuint rightTopUniform = [self.mProgram uniformIndex:@"rightTop"];
    GLuint displayPositionAttribute = [self.mProgram attributeIndex:@"position"];
    GLuint displayTextureCoordinateAttribute = [self.mProgram attributeIndex:@"textCoordinate"];

注意涡真,shader里面的attribute變量用-attributeIndex分俯,uniform變量用-uniformIndex,紋理是uniform變量哆料;
從頂點shader傳值到像素shader需要用varing變量缸剪。

3、上傳紋理數(shù)據(jù)

這是本文的重點之一东亦。

  • 1杏节、首先通過UIKit的方法,拿到圖像的UIImage對象讥此;

  • 2拢锹、將UIImage轉(zhuǎn)換成CGImage谣妻,通過CoreGraphics取到二進制數(shù)據(jù)萄喳;

// 1獲取圖片的CGImageRef
    CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
    if (!spriteImage) {
        NSLog(@"Failed to load image %@", fileName);
        exit(1);
    }
    
    // 2 讀取圖片的大小
    size_t width = CGImageGetWidth(spriteImage);
    size_t height = CGImageGetHeight(spriteImage);
    
    GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte)); //rgba共4個byte
    
    CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,
                                                       CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
    
    // 3在CGContextRef上繪圖
    CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
    
    CGContextRelease(spriteContext);
  • 3、選擇對應(yīng)的紋理單元蹋半,創(chuàng)建紋理對象并綁定他巨;
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &_myTexture0);
    glBindTexture(GL_TEXTURE_2D, self.myTexture0);
  • 4、上傳紋理數(shù)據(jù)减江,并釋放原來申請的內(nèi)存染突;
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    float fw = width, fh = height;
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
    //    glBindTexture(GL_TEXTURE_2D, 0);
    free(spriteData);

這里需要理解兩個概念,紋理單元紋理對象辈灼。
紋理單元我沒有找到很好的中文描述份企,講下我自己的理解。
紋理單元對應(yīng)GPU支持的紋理數(shù)量巡莹,在shader的表現(xiàn)是以uniform變量的形式表現(xiàn)

uniform sampler2D myTexture0;
uniform sampler2D myTexture1;

iOS對紋理單元的數(shù)量限制如下


紋理對象指的是紋理的索引司志,通常是用glGenTextures生成,如下是生成一個紋理對象降宅。

    glGenTextures(1, &_myTexture0);

一個紋理單元上有1D骂远、2D、3D腰根、CUBE等幾個目標(biāo)激才,即是你可以在同一個紋理單元bind不同的紋理對象,但是不推薦剛開始就這么做。

    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &(_myTexture1));
    glBindTexture(GL_TEXTURE_2D, self.myTexture1);
    ```
如上瘸恼,這是一段常用的使用紋理單元1的代碼劣挫。
先選擇(你也可以按照詞面意思理解為激活)紋理單元1,同時開啟2D的紋理目標(biāo)东帅;
然后生成一個紋理對象揣云,把紋理對象綁定到紋理單元1的2D紋理上;
接下來所有的操作都是針對紋理單元1上的紋理對象冰啃,直到你再次通過`glActiveTexture`選擇其他紋理單元邓夕。

####4、實現(xiàn)著色器
頂點著色器較為簡單阎毅,只需把頂點數(shù)據(jù)轉(zhuǎn)成varying變量焚刚,傳給像素著色器即可;
像素著色器扇调,收到頂點著色器傳過來的varyOtherPostion頂點數(shù)據(jù)矿咕,判斷當(dāng)前點是否在leftBottom變量和rightTop變量形成的矩形內(nèi)。
如果在矩形內(nèi)狼钮,則通過自定義的操作來混合顏色碳柱,通常是使用alpha值,一個變量 \* alpha熬芜,一個變量 \* (1-alpha)莲镣。

varying lowp vec2 varyTextCoord;
varying lowp vec2 varyOtherPostion;

uniform lowp vec2 leftBottom;
uniform lowp vec2 rightTop;

uniform sampler2D myTexture0;
uniform sampler2D myTexture1;

void main()
{
if (varyOtherPostion.x >= leftBottom.x && varyOtherPostion.y >= leftBottom.y && varyOtherPostion.x <= rightTop.x && varyOtherPostion.y <= rightTop.y) {

    lowp vec2 test = vec2((varyOtherPostion.x - leftBottom.x) / (rightTop.x - leftBottom.x), 1.0 -  (varyOtherPostion.y - leftBottom.y) / (rightTop.y - leftBottom.y));
    lowp vec4 otherColor = texture2D(myTexture1, test);
 //   otherColor.a = 0.8;
    gl_FragColor = otherColor * otherColor.a + texture2D(myTexture0, 1.0 - varyTextCoord) * (1.0 - otherColor.a);
}
else {
    gl_FragColor = texture2D(myTexture0, 1.0 - varyTextCoord);
}

}

>0.8是為了測試,效果展示的圖片就是alpha=0.8的效果圖涎拉。


###總結(jié)
最近幾周都忙著[直播系列的補齊](http://www.reibang.com/notebooks/5037333/latest)瑞侮,OpenGL ES的上一篇[OpenGL ES實踐教程(四)VR全景視頻播放](http://www.reibang.com/p/0c8d080bb375)已經(jīng)是一個月之前。
接下來的文章主要還是以直播相關(guān)內(nèi)容為主鼓拧,圖形圖像的等簡書的書友問道了再補上半火。


####附1
CaptureGPUFrame突然不好用,查了下文檔季俩,發(fā)現(xiàn)可能是以下原因
>Note: Some features of the FPS gauge and GPU report rely on a display link timer. If you do not use the CADisplayLink or GLKViewController classes to animate your OpenGL ES displays, the gauge and report cannot show performance relative to a target frame rate or provide accurate CPU frame time information.

####附2
之前有書友問到钮糖,如何添加以下形狀的圖像。
![](http://upload-images.jianshu.io/upload_images/1049769-f492641d933b30a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
這種格式不太試用本文的方法酌住,需要引入一個新的shader變量`gl_LastFragData `店归。先繪制原來的圖像,再繪制新的圖像赂韵,通過`gl_LastFragData `來混合娱节。
***有興趣的來一份數(shù)據(jù),弄個demo玩玩<朗尽肄满!***
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谴古,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子稠歉,更是在濱河造成了極大的恐慌掰担,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怒炸,死亡現(xiàn)場離奇詭異带饱,居然都是意外死亡,警方通過查閱死者的電腦和手機阅羹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門勺疼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捏鱼,你說我怎么就攤上這事执庐。” “怎么了导梆?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵轨淌,是天一觀的道長。 經(jīng)常有香客問我看尼,道長递鹉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任藏斩,我火速辦了婚禮躏结,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灾茁。我一直安慰自己窜觉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布北专。 她就那樣靜靜地躺著,像睡著了一般旬陡。 火紅的嫁衣襯著肌膚如雪拓颓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天描孟,我揣著相機與錄音驶睦,去河邊找鬼。 笑死匿醒,一個胖子當(dāng)著我的面吹牛场航,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播廉羔,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼溉痢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孩饼,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤髓削,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后镀娶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體立膛,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年梯码,在試婚紗的時候發(fā)現(xiàn)自己被綠了宝泵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡轩娶,死狀恐怖鲁猩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情罢坝,我是刑警寧澤廓握,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站嘁酿,受9級特大地震影響隙券,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闹司,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一娱仔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧游桩,春花似錦牲迫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铐刘,卻和暖如春陪每,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镰吵。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工檩禾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疤祭。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓盼产,卻偏偏與公主長得像,于是被迫代替她去往敵國和親勺馆。 傳聞我的和親對象是個殘疾皇子戏售,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容