OpenGL ES 实践教程(五)多重纹理实现图像混合
有开发者问我如何使用在一张大图上贴一张小图,原始的需求是在检测人脸,在返回的范围(矩形)内贴上一张图片。
有几点前提:
- 尽量少消耗CPU;
- 合成的数据是用于推流;
- 图片大小不一致;
说说如果没有上述几点前提下,可能的方案:
- 1、使用UIKit,新建一个透明的View,大小和原图像一致,在View上面对应的位置添加图像;
- 2、使用GPUImage,选择一个filter,添加两个原图像作为输入;
- 3、使用OpenGL ES,多重纹理;
因为数据要用于推流,故而最简单的方案1不行;
方案2可行,但是需要对GPUImage较为熟悉;
方案3相对方案2简单,同时对性能的要求最低,最为符合。
本文探究如何使用OpenGL ES实现两个图片的混合。
核心思路
自定义shader,传入两个纹理和对应矩形的坐标;
在像素着色器内判断当前点的范围,如果处于对应矩形内,则进行混合操作;
效果展示
具体细节
1、编译链接GLProgram
为了更方便开发,特引入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、上传顶点数据以及矩形坐标
通过GLProgram
的-uniformIndex:
和-attributeIndex:
方法,可以便捷的取到对应属性的索引,再通过glUniform1i
和glUniform2f
方法可以上次数据到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、上传纹理数据
这是本文的重点之一。
1、首先通过UIKit的方法,拿到图像的UIImage对象;
2、将UIImage转换成CGImage,通过CoreGraphics取到二进制数据;
// 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、选择对应的纹理单元,创建纹理对象并绑定;
glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glGenTextures(1, &_myTexture0); glBindTexture(GL_TEXTURE_2D, self.myTexture0);
4、上传纹理数据,并释放原来申请的内存;
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);
这里需要理解两个概念,
纹理单元
和纹理对象
。
纹理单元我没有找到很好的中文描述,讲下我自己的理解。
纹理单元对应GPU支持的纹理数量,在shader的表现是以uniform变量的形式表现uniform sampler2D myTexture0;uniform sampler2D myTexture1;
iOS对纹理单元的数量限制如下
纹理对象指的是纹理的索引,通常是用
glGenTextures
生成,如下是生成一个纹理对象。glGenTextures(1, &_myTexture0);
一个纹理单元上有1D、2D、3D、CUBE等几个目标,即是你可以在同一个纹理单元bind不同的纹理对象,但是不推荐刚开始就这么做。
glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_2D); glGenTextures(1, &(_myTexture1)); glBindTexture(GL_TEXTURE_2D, self.myTexture1);
如上,这是一段常用的使用纹理单元1的代码。
先选择(你也可以按照词面意思理解为激活)纹理单元1,同时开启2D的纹理目标;
然后生成一个纹理对象,把纹理对象绑定到纹理单元1的2D纹理上;
接下来所有的操作都是针对纹理单元1上的纹理对象,直到你再次通过glActiveTexture
选择其他纹理单元。
4、实现着色器
顶点着色器较为简单,只需把顶点数据转成varying变量,传给像素着色器即可;
像素着色器,收到顶点着色器传过来的varyOtherPostion顶点数据,判断当前点是否在leftBottom变量和rightTop变量形成的矩形内。
如果在矩形内,则通过自定义的操作来混合颜色,通常是使用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 0.8是为了测试,效果展示的图片就是alpha=0.8的效果图。### 总结最近几周都忙着[直播系列的补齐](http://www.jianshu.com/notebooks/5037333/latest),OpenGL ES的上一篇[OpenGL ES实践教程(四)VR全景视频播放](http://www.jianshu.com/p/0c8d080bb375)已经是一个月之前。接下来的文章主要还是以直播相关内容为主,图形图像的等简书的书友问道了再补上。#### 附1CaptureGPUFrame突然不好用,查了下文档,发现可能是以下原因> 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之前有书友问到,如何添加以下形状的图像。![](https://v1cdn.imspm.com/e/e413ee63a9e040ce9fad32cb650d14a3.png)这种格式不太试用本文的方法,需要引入一个新的shader变量`gl_LastFragData`。先绘制原来的图像,再绘制新的图像,通过`gl_LastFragData`来混合。 _有兴趣的来一份数据,弄个demo玩玩!!_ 文/落影loyinglin#产品经理#
版权声明
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!