AVCapture之5——OpenGL

渲染的方案之前探索过很多,但是很遗憾,那些方案都是基于系统控件,并没有接触到真正的OpenGL。网上也没有太多MacOS上使用纯OpenGL渲染的实现,这次我就基于苹果 GLEssentials 示例代码来实现。

使用纯OpenGL要掌握不少知识,对OpenGL不熟的可以先看看我前几篇GLFW文章。
首先,需要先创建vertex信息

- (GLuint) buildVAO
{   
    
    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,  1.0f, 1.0f,
        1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
        -1.0f,  1.0f, 0.0f, 1.f, 0.f,
        -1.0f, -1.0f, 0.0f,  1.0f, 1.0f,
        1.0f, -1.0f, 0.0f, 0.0f, 1.f,
        1.0f,  1.0f, 0.0f, 0.0f, 0.0f
    };
    
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
    glBindVertexArray(VAO);
    
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    
    glVertexAttribPointer(1, 2, GL_FLOAT,GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    
    glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind
    
    glBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)
    
    
    return VAO;
}

顶点信息是2个三角形组成的矩形,以及每个点的纹理坐标。后面一堆代码是创建VAO的。

接下来是写shader。我们的窗口非常简单,就是把纹理显示出来。

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;

out vec2 TexCoord;


void main()
{
    gl_Position = vec4(position.x, position.y, position.z, 1.0);
    TexCoord = texCoord;
}

TexCoord是纹理坐标,后面是要传给fragment shader的。

#version 330 core
in vec2 TexCoord;

out vec4 color; 

uniform sampler2D ourTexture;

void main()
{
    color = texture(ourTexture, TexCoord);
}

texture方法是从纹理中取出坐标上的颜色。

-(GLuint) buildTexture:(demoImage*) image
{
    GLuint texName;
    
    if (_characterTexName == 0) {
        // Create a texture object to apply to model
        glGenTextures(1, &texName);
    } else {
        texName = _characterTexName;
    }
    
    glBindTexture(GL_TEXTURE_2D, texName);
    // Set up filter and wrap modes for this texture object
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
    // Indicate that pixel rows are tightly packed 
    //  (defaults to stride of 4 which is kind of only good for
    //  RGBA or FLOAT data types)
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
    // Allocate and load image data into texture
    glTexImage2D(GL_TEXTURE_2D, 0, image->format, image->width, image->height, 0,
                 image->format, image->type, image->data);

    // Create mipmaps for this texture for better image quality
    glGenerateMipmap(GL_TEXTURE_2D);
    
    GetGLError();
    
    return texName;
}

- (void)setImage:(CVImageBufferRef)pixelBuffer {
    
//    glDeleteBuffers(1, &_characterTexName);
//    _characterTexName = 0;
    
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    
    size_t width = CVPixelBufferGetWidth(pixelBuffer);
    size_t height = CVPixelBufferGetHeight(pixelBuffer);
    
    demoImage image = {0};
    image.width = width;
    image.height = height;
    image.rowByteSize = image.width * 3;
    image.format = GL_RGB;
    image.type = GL_UNSIGNED_BYTE;
    image.size = CVPixelBufferGetDataSize(pixelBuffer);
    image.data = CVPixelBufferGetBaseAddress(pixelBuffer);
    
    _characterTexName = [self buildTexture:&image];
    
    CVPixelBufferUnlockBaseAddress(pixelBuffer,0);

}

buildTexture是方便创建纹理的辅助函数。AVCapture项目只需要一个纹理,所以在前面判断_characterTexName纹理是否存在,只有不存在的时候才需要创建(删除纹理并不能有效的释放纹理占有的内存,而且也没有这个必要)。
刷新纹理用的是CVImageBufferRef对象,这个对象从CMSampleBuffer中很容易获得。这次我选用的是RGB格式,因为OpenGL支持。使用pixelBuffer前,需要包地址锁住,不然取到的数据会是脏数据。

- (void) render
{
    // Calculate the projection matrix
    // Clear the colorbuffer
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    // Draw our first triangle
    glUseProgram(_characterPrgName);
    
    glBindTexture(GL_TEXTURE_2D, _characterTexName);
    
//    glActiveTexture(GL_TEXTURE0);
//    glBindTexture(GL_TEXTURE_2D, _characterTexName);
//    glUniform1i(glGetUniformLocation(_characterPrgName, "ourTexture"), 0);
    
    
    glBindVertexArray(_characterVAOName);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBindVertexArray(0);
}

渲染的过程就太简单了,首先启用纹理,最后把两个三角形画出来,图像就显示出来了。
render是方法是由单独的CVDisplayLink驱动的,DisplayLink的刷新频率要比摄像头快,所以我的做法是先将图像保存,render在刷新的时候取出。OpenGL不是线程安全,不能在Capture线程直接操作纹理。

最后实测下来,CPU的占用率和CIContext持平,都是7%,可见效率还是不错的。下一篇将介绍如何对I420格式的支持,毕竟YUV才是视频中的主流格式。基于目前OpenGL的框架,支持I420并不难,大部分工作都集中在Shader中,等我有时间了再写。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容