IOS:Camera的特性分析与使用3_OPENGL特效
本来想用2个篇幅结束Camera软件部分的介绍,后来发现,很重要的一点OpenGL还没介绍,所以又添加了这一篇。
这篇主要描写叙述一下几个方面的内容:
(1)录像界面OPENGL展示
(2)录像实时特效处理
(3)视频等比例缩放、旋转 如:等比例、16:9 4:3 1:1等
这个部分我思来想去缺失不太好讲,设计到的知识太多,尤其是OpenGL的一些专业知识,通过一篇博客普及OpenGL的知识显然不科学,所以仅仅能了解一个流程。至于里面究竟是怎么回事,请大家找本OpenGL的书看看,我想等这几个博客完工之后,也写几篇OpenGL的博客呵呵。
我们的整个流程是。首先从AVCaptureSession拿到视频拍摄时候的数据流,然后特效处理(特效这块能够參考还有一个Image&Animation专栏),然后初始化OpenGL開始进行纹理贴图。
(1)怎样拿到视频数据流?
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if ( videooutput == captureOutput ) {
OSStatus err = CMBufferQueueEnqueue(previewBufferQueue, sampleBuffer);
if ( !err ) {
dispatch_async(dispatch_get_main_queue(), ^{
CMSampleBufferRef sbuf = (CMSampleBufferRef)CMBufferQueueDequeueAndRetain(previewBufferQueue);
if (sbuf) {
CVImageBufferRef pixBuf = CMSampleBufferGetImageBuffer(sbuf);
if (effectflag) {
特效处理
}
OpenGL纹理展示
CFRelease(sbuf);
}
});
}
}
}
AVCaptureSession初始化完毕之后我们能够设置一个回调方法,在这个回调方法中,能够非常方便的拿到我们须要处理的图像数据。
(2)怎样进行图片的特效处理
这又是一个很复杂的内容。我也专门为此写了另外一篇博客:
这中间牵扯到各种图像处理算法,neon、汇编优化,ARM内部寄存器的使用等等。
这里我们仅仅说怎样吧ImageBuffer转化为RGBA像素:
unsigned char *pixel = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);
这里pixel存放的就是图片的RGBA像素值。
(3)OpenGL纹理贴图
3.1//在使用Opengles的时候须要重构CAEAGLLayer图层
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
3.2// 设置CAEAGLLayer
CAEAGLLayer*eaglLayer = (CAEAGLLayer *)self.layer;
3.3设置CAEAGLLayer层的属性RGBA8
3.4// 使用opengl2.0 创建图像绘制上下文
oglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
3.5// 设置oglContext为当前上下文
3.2 // glEnable(GL_DEPTH_TEST):用来开启更新深度缓冲区的功能
3.3 // 创建帧缓冲区
3.4 // 讲帧缓冲区绑定在画图管线上
3.5 // 创建画图缓冲区
3.6 // 讲画图缓冲区绑定在管线上
3.7 // 为画图缓冲区(或者叫渲染缓冲区分配空间)
3.8 // 获取当前画图缓冲区(渲染缓冲区的)宽和高
3.9 // 讲渲染缓冲区与帧缓冲区绑定在一起
3.10 // 检查当前帧缓冲区的状态是否有效
3.11 // 创建一个opengl的纹理对象
3.12 // 载入定点和片段着色器
3.13 // 创建并初始化这个project对象
相应代码例如以下:
//在使用Opengles的时候须要重构CAEAGLLayer图层
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
- (BOOL)initializeBuffers
{
// 设置oglContext为当前上下文
if ([EAGLContext currentContext] != oglContext) {
if ([EAGLContext setCurrentContext:oglContext]) {
NSLog(@"setCurrentContext error... ...");
}
}
BOOL success = YES;
// 设置图层的frame和bounds
CGRect rtFullscreem = [[UIScreen mainScreen] bounds];
CGRect rtCurrframe = self.layer.frame;
CGRect rtCurrbounds = self.layer.bounds;
self.layer.frame = rtFullscreem;
self.layer.bounds = rtFullscreem;
NSLog(@"size{%f %f %f %f}", rtFullscreem.origin.x, rtFullscreem.origin.x, rtFullscreem.size.width, rtFullscreem.size.height);
// glEnable(GL_DEPTH_TEST): 用来开启更新深度缓冲区的功能,也就是。假设通过比較后深度值发生变化了,会进行更新深度缓冲区的操作。启动它,OpenGL就能够跟踪再Z轴上的像素,这样,它仅仅会再那个像素前方没有东西时。才会绘画这个像素。
// 一般这个功能开启之后绘制3D效果比較好
glDisable(GL_DEPTH_TEST);
// 创建帧缓冲区
glGenFramebuffers(1, &frameBufferHandle);
// 讲帧缓冲区绑定在画图管线上
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferHandle);
// 创建画图缓冲区
glGenRenderbuffers(1, &colorBufferHandle);
// 讲画图缓冲区绑定在管线上
glBindRenderbuffer(GL_RENDERBUFFER, colorBufferHandle);
// 为画图缓冲区(或者叫渲染缓冲区分配空间)
[oglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
// 获取当前画图缓冲区(渲染缓冲区的)宽和高
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &renderBufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &renderBufferHeight);
// 讲渲染缓冲区与帧缓冲区绑定在一起
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
// 检查当前帧缓冲区的状态是否有效
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Failure with framebuffer generation 0x%X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
success = NO;
}
// Create a new CVOpenGLESTexture cache
// 创建一个opengl的纹理对象
// 在oglContext 中创建纹理对象
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, oglContext, NULL, &videoTextureCache);
if (err) {
NSLog(@"Error at CVOpenGLESTextureCacheCreate %d", err);
success = NO;
}
// Load vertex and fragment shaders
// 载入定点和片段着色器
const GLchar *vertSrc = str_passThrough_v;//[self readFile:@"passThrough.vsh"];
const GLchar *fragSrc = str_passThrough_f;// [self readFile:@"passThrough.fsh"];
// attributes
GLint attribLocation[NUM_ATTRIBUTES] = {
ATTRIB_VERTEX, ATTRIB_TEXTUREPOSITON,
};
GLchar *attribName[NUM_ATTRIBUTES] = {
"position", "textureCoordinate",
};
// 创建并初始化这个project对象
glueCreateProgram(vertSrc, fragSrc,
NUM_ATTRIBUTES, (const GLchar **)&attribName[0], attribLocation,
0, 0, 0, // we don't need to get uniform locations in this example
&passThroughProgram);
if (!passThroughProgram)
success = NO;
self.layer.frame = rtCurrframe;
self.layer.bounds = rtCurrbounds;
return success;
}
最后我们再来看看怎样对所播放的视频屏幕进行等比例缩放,16:9等缩放这里我们首先须要设置一个属性:
glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0, textureVertices);
而textureVertices 是一个数组。用于进行纹理贴图时画面设置:全屏幕播放
GLfloat squareVertices0[8] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
等比例拉伸
GLfloat squareVertices1[8] = {
-0.5625f, -1.0f,
0.5625f, -1.0f,
-0.5625f, 1.0f,
0.5625f, 1.0f
};
这个数据是啥意思呢?看以下两个图
屏幕拍摄为1920*1080,所以1080/1920=0.5625.注意拍摄时候 宽高倒置。