前段时间在做视频水印的时候,用到了GPUImage,为了能够更好的阅读GPUImage的源码,把OpenGLES 研究了一下,后续会写一些OpenGL ES 系列文章。以自己的学习顺序为序,相关代码会开源在Github上,我写的比较浅,适合初学者,有想学习OpenGL ES的朋友可以去看一下。另外,我也会贴出我在学习时用到的资料,网站,免去大家到处查找资料的烦恼。
老规矩,第一篇文章当然是hello world。当然,在OpenGL ES中并不是打印hello world,而是了解一些OpenGL ES 的基本概念和一些基本的API。
我在学习OpenGL ES 的时候,苹果已经将OpenGL ES 标记为废弃了,取而代之的是苹果自己推出的metal框架。但是,并不是说就没有学习的价值,至少现存的大量的项目中使用到了,另外,OpenGL ES 的狂平台性,是Metal 无法取代的。
首先,我们新建一个OpenGLView,继承UIView。在OpenGLView 中新建两个变量
@interface OpenGLView ()
{
CAEAGLLayer *_eaglLayer;
EAGLContext *_context;
}
@end
然后,我们添加两个函数
+ (Class)layerClass {
// 只有 [CAEAGLLayer class] 类型的 layer 才支持在其上描绘 OpenGL 内容。
return [CAEAGLLayer class];
}
- (void)setupLayer {
_eaglLayer = (CAEAGLLayer *)self.layer;
_eaglLayer.opaque = YES;
}
为了让UIView 显示OpenGL 的内容,我们将默认的layer 类型修改为CAEAGLLayer 类型。
然后初始化context
- (void)setupContext {
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!_context) {
NSLog(@"Failed to initialize context");
exit(1);
}
if (![EAGLContext setCurrentContext:_context]) {
NSLog(@"Failed to set current context!1");
exit(1);
}
}
OpenGLES 是一个巨大的状态机,这个context 管理所有使用OpenGLES进行秒回的状态,命令以及信息资源。
设置缓存
@interface OpenGLView ()
{
CAEAGLLayer* _eaglLayer;
EAGLContext* _context;
GLuint _colorRenderBuffer;
GLuint _framebuffer;
}
@end
添加两个变量颜色缓存_colorRenderBuffer
和帧缓存_framebuffer
- (void)setupRenderBuffer {
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
- (void)setupFrameBuffer {
glGenRenderbuffers(1, &_framebuffer);
// 设置为当前 framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
// 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);
}
OpenGLES 是一种软件技术。部分运行在CPU上,部分运行在GPU上,OpenGLES横跨在两个处理器之间,协调两个内存区域之间的数据交换。对于渲染速度最快的数据交换方式是没有数据交换。
首先,从一个内存区域复制到另一个内存区域速度是相对较慢的,更糟糕的是,除非非常小心,在内存复制的时候CPU和GPU都不能把内存另做他用。因此内存区域之间的数据交换需要尽量避免。 其次,所有的内存访问都是相对较慢的。这意味着,除非CPU能够在每次从内存读取一块数据后有效的运行更多个运算,否则处理器的性能就处于次优状态,这种状态叫做“数据饥饿”。而这种情况对于GPU 来说更明显。因此,就提出了缓存。
OpenGL ES为两个内存区域间的额数据交换定义了缓存的概念,缓存是指图形处理器能够控制和管理的连续的arm。程序从CPU 的内存复制数据到OpenGLES 的缓存。在GPU取得一个缓存的所有权以后,运行在CPU 中的程序理想情况下将不再接触这个缓存。通过控制独占的缓存,GPU就能够尽可能以最邮箱的方式读写内存,图像处理器把它处理大量数据的能力一部同时的应用到缓存中。这意味着GPU使用缓存中的数据工作的同时,运行在CPU中的程序可以急促执行。
在OpenGL ES 中定义了三大缓存color(颜色缓存)、depth(深度缓存)和stencil(模板缓存)。而我们刚刚定义的framebuffer,它相当于三大缓存的管理者,三大缓存可以附加到一个FBO上。
最后,绘制
- (void)render {
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
- (void)layoutSubviews {
[self setupLayer];
[self setupContext];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self render];
}
这样,第一个OpenGL ES 程序就出来了,当然,现在什么都没有。
最后,附上我自己在学习过程中的源码:
参考:
[1]:OpenGL ES 应用开发实践指南iOS卷