OpenGL ES是什么
OpenGL ES(Open Graphics Library Embedded Systems):一种跨平台的渲染技术,定义了一个跨编程语言、跨平台编程的专业图形程序接口,可用于二维或三维图像的处理与渲染。由于跨平台设计,所以在每个平台上都要有它的具体实现,即要提供OpenGL ES的上下文环境以及窗口的管理。在OpenGL的设计中,OpenGL是不负责管理窗口的,窗口的管理将交由各个设备自己来完成,上下文环境也是一样的。所以,为了在OpenGL的渲染输出和设备的屏幕之间架起一个桥梁,Khronos group创建了EGL的API。值得注意的是,不同的平台会为了适应自己的设备和窗口系统而修改EGL的API,所以要根据不同平台的需求查看文档。
OpenGL ES可以做什么
OpenGL主要是做图形图像处理的库,尤其是在移动设备上进行图形图像处理,它的性能优势更能体现出来。若要使用OpenGL ES2.0就要使用GLSL(OpenGL Shading Language)这个OpenGL的着色器语言,开发人员利用这种语言编写程序运行在GPU(Graphic Processor Unit ,图形图像处单元,可以理解为是一种高并发的运算器)上以进行图像的处理或渲染。GLSL着色器代码分为两个部分,即Vertex Shader(顶点着色器)与Fragment Shader(片元着色器)两部分,分别完成各自在OpenGL渲染管线中的功能。在知名开源库GPUImage中,可以找到大部分图形图像处理Shader的实现:亮度、对比度、饱和度、色调曲线、白平衡、灰度等颜色处理,还包括锐化、高斯模糊等图像像素处理,还有素描、卡通效果、浮雕效果等视觉效果实现,开发者也可以自己实现一些有意思的Shader,如美颜滤镜效果、瘦脸效果及粒子效果等。
iOS上的视频渲染
即接收到视频的原始数据后,利用iOS平台提供的API构造出OpenGL ES环境,然后利用统一的OpenGL程序(program)将一张图片渲染到屏幕(view)上。具体来讲,在iOS平台上使用EAGL(Embedded Apple Graphics Library)提供本地平台对OpenGL ES的实现,那么需要了解一下EAGL是什么。
EGL与EAGL
先看看EGL的工作模式,iOS开发可以大概了解一下
-
EGL最先需要知道的是在什么地方显示内容,一般通过eglGetDisplay方法返回的EGLDisplay数据结构得以知晓,而平台实现的常量EGLDEFAULTDISPLAY可以返回默认的显示位置。然后通过eglInitialize方法初始化EGL API。
EGLDisplay display; display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (eglInitialize(display, NULL, NULL)) { // Proceed with your code. }
-
接下来是配置。EGL知道屏幕所在后,通过eglGetConfigs或eglChooseConfigs方法配置一些包括color format、大小、透明度、pixel format等参数,EGL就可以桥接OpenGL的渲染输出和设备屏幕了。
-
配置完后,即可以命令EGL API创建一个渲染surface,这个surface就是OpenGL渲染输出的位置。选择屏上渲染,就可以把渲染结果直接展示到屏幕上。选择离屏渲染,则可以把渲染结果输出到一个buffer或一个图片文件、一个快照或其他等。可用的方法有eglCreateWindowSurface和eglCreatePbufferSurface。
桥有两头,EGL已经知道了设备窗口系统的一侧,接下来EGL需要知道OpenGL的渲染buffer在哪。
很简单。
- 创建一个EGL context并且设为当前context(道理上是可以创建很多context的),只要创建好context,接下来使用的Frame Buffer就可以在EGL context中使用了。
-
EGL是双缓冲的工作模式,即有一个Back Frame Buffer和一个Front Frame Buffer,正常绘制操作的目标都是Back Frame Buffer,操作完毕后,调用eglSwapBuffer这个API,将绘制完毕的FrameBuffer交换到Front Frame Buffer并显示出来。
swap_buffers_example
重要的是EAGL,即iOS平台上搭建上下文环境
安卓平台上,使用的是EGL这一套机制。
iOS平台为OpenGL提供的实现是EAGL,OpenGL ES系统与本地窗口(UIKit)桥接由EAGL上下文系统实现。
与EGL不用的是,iOS不会让应用直接向Back Frame Buffer和Front Frame Buffer绘图,也不会让应用直接控制Back Frame Buffer和Front Frame Buffer的swap。操作系统为自己保留了这些操作,以便它可以随时使用Core Animation合成器来控制显示的最终外观。
Core Animation是iOS上图形渲染和动画的核心基础架构。可以使用托管不同iOS系统(UIKit,Quartz 2D和OpenGL ES)内容的图层来合成应用的用户界面或者其他视觉显示。OpenGL ES通过CAEAGLLayer与Core Animation连接,CAEAGLLayer是一种特殊类型的Core Animation图层,它的内容来自OpenGL ES的renderbuffer,Core Animation将renderbuffer的内容与其他图层合成,并在屏幕上显示生成的图像。所以同一时刻可以有任意数量的层。Core Animation合成器会联合这些层并在后帧缓存中产生最终的像素颜色,然后切换缓存。一个应用提供的层与操作系统提供的层混合起来可以产生最终的显示外观。如下图所示,OpenGL ES层显示了一个应用生成的旋转立方体,但是在显示器顶部的显示状态栏层是由操作系统生成和控制的,此图显示的是合并两个层来产生后帧缓存中的颜色数据的过程,交换后,我们看到的就是前帧缓存上的内容。
由于iOS平台不允许直接渲染到屏幕上,所以通过FrameBuffer和RenderBuffer来进行渲染——先创建一个RenderBuffer,然后让OpenGL ES渲染到该RenderBuffer上去。而该RenderBuffer则需要绑定到一个CAEAGLLayer上面去,这样开发者最后调用EAGLContext的presentRenderBuffer方法,就可以将那个渲染结果输出到屏幕上去了。实际上,EAGL也会执行类似EGL的swapBuffer过程,将OpenGL ES的渲染结果绘制到物理屏幕上去(view的Layer),具体步骤如下:
Core Animation 与 OpenGL ES共享renderbuffer-
写一个View类,继承自UIView,然后重写父类UIView的一个方法layerClass,并且返回CAEAGLLayer类型:
+ (Class)layerClass { return [CAEAGLLayer class]; }
-
在该View的initWithFrame方法中,获得layer并且强制类型转换为CAEAGLLayer类型的变量,同时为layer设置参数,其中包括色彩模式等属性:
- (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[self layer]; NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGB565,kEAGLDrawablePropertyColorFormat, nil]; [eaglLayer setOpaque:YES]; [eaglLayer setDrawableProperties:dict]; } return self; }
-
接下来构造 EAGLContext与RenderBuffer并且绑定到Layer上。必须为每一个线程绑定OpenGL ES上下文。所以首先必须开辟一个线程,可以使用dispatch_queue,也可以使用NSOperationQueue,设置pthread也可以,反正必须在一个线程中执行一下操作:
- 创建OpenGL ES的上下文:
EAGLContext *_context; _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- 然后实施绑定操作,设置当前上下文环境并告知EAGLContext,即该线程中的后续OpenGL ES调用将与该上下文环境绑定,若不绑定,则下面的GL调用都无返回值:
[EAGLContext setCurrentContext:_context];
此时已经为该线程绑定好了刚刚创建好的上下文环境了,也就是说已经建立好了iOS的EAGL与OpenGL ES的连接,接下来建立另一端的连接(OpenGL ES与RenderBuffer的连接,误?)
- 创建帧缓冲区和绘制缓冲区(有说渲染缓冲区,一个意思):
glGenFramebuffers(1, &_FrameBuffer); glGenRenderbuffers(1, &renderbuffer);
- 绑定帧缓冲区到渲染管线 | 绑定绘制缓存区到渲染管线:
glBindFramebuffer(GL_FRAMEBUFFER, _FrameBuffer); glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
- 为绘制缓冲区分配存储区,此处将CAEAGLLayer的绘制存储区作为绘制缓冲区的存储区:
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
- 获取绘制缓冲区的像素宽度和高度:
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
- 将绘制缓冲区绑定到帧缓冲区:
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderbuffer);
- 检查FrameBuffer的status:
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { NSLog(@"failed to make complete framebuffer object %x", status); return FALSE; }
- 创建OpenGL ES的上下文:
至此EAGL与Layer连接起来了,绘制完一帧之后(绘制过程也必须在这个线程之中),调用一下代码:[_context presentRenderbuffer:GL_RENDERBUFFER];
,这样就可以将绘制的结果显示到屏幕上了。至此就搭建好了iOS平台的OpenGL ES的上下文环境,以便在此基础上进行业务开发。
引用文章:
音视频开发进阶指南
Khronos EGL and Apple EAGL
Apple OpenGL ES Programming Guide____Drawing to Other Rendering Destinations
GPUImage
OpenGL ES 3.0 编程指南
OpenGL ES 数据可视化
OpenGL ES应用开发实践指南
网友评论