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;

    //  设置图层的framebounds

    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.0f1.0f,

        1.0f1.0f

};

等比例拉伸

    GLfloat squareVertices1[8] = {

        -0.5625f, -1.0f,

        0.5625f, -1.0f,

        -0.5625f1.0f,

        0.5625f1.0f

};

这个数据是啥意思呢?看以下两个图


屏幕拍摄为1920*1080,所以1080/1920=0.5625.注意拍摄时候 宽高倒置。

posted on 2017-04-26 09:24  wgwyanfs  阅读(249)  评论(0编辑  收藏  举报

导航