教程
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)屬性的索引狗唉,再通過glUniform1i
和glUniform2f
方法可以上次數(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玩玩<朗尽肄满!***