几个基本概念:
-
帧缓存:内存中用于储存渲染结果的地方,可以有任意数量,分为前帧缓存和后帧缓存,渲染结果被保存在后帧缓存中,需要显示的时候变为前帧缓存,显示完成后前帧缓存又会再次变为后帧缓存。在iOS中前后帧缓存的切换是由系统控制的,用户无法操作。
-
图层:在Core Animation中,同一时刻可以存在任意数量的图层,一幅图对应的帧缓存由多个图层混合而成。
GLKit是iOS基于openGL实现的一个框架,目的在于简化OpenGL的操作,其提供了GLKView和GLKViewController,openGL相关的操作需要在这两个类中的子类中完成。下面的例子将展示如何使用OpenGl在屏幕中绘制一个三角形,流程如下:
- 生成上下文 EAGLContext
- 设置清理颜色和渲染颜色,清理颜色(画布颜色) glClearColor,渲染颜色由 GLKBaseEffect 管理
- 为GPU分配缓存,并将数据从CPU管理内存复制到GPU缓存
3.1 生成标识符 glGenBuffers
3.2 将标识符绑定内存 glBindBuffer
3.3 复制数据 glBufferData - 绘制在 glkView:drawInRect 中完成
4.1 清理颜色 glClear
4.2 渲染 glEnableVertexAttribArray -> glVertexAttribPointer->glDrawArrays - 清理上下文 glDeleteBuffers
声明文件:OpenGLESViewController.h
#import <GLKit/GLKit.h>
@interface OpenGLESViewController : GLKViewController{
//存放了缓存标识符
GLuint vertexBufferID;
}
//openGL ES的对外接口,里面处理了openGL各个版本的不同,并提供简易操作
@property (strong, nonatomic) GLKBaseEffect *baseEffect;
@end
实现文件:OpenGLESViewController.m
#import "OpenGLES_Ch2_1ViewController.h"
@implementation OpenGLES_Ch2_1ViewController
//告诉编译器使用baseEffect,而不是加了下划线的_baseEffect
@synthesize baseEffect;
static const GLKVector3 vertices[] =
{
{{-0.5f, -0.5f, 0.0}}, // lower left corner
{{ 0.5f, -0.5f, 0.0}}, // lower right corner
{{-0.5f, 0.5f, 0.0}} // upper left corner
};
- (void)viewDidLoad
{
[super viewDidLoad];
// 验证view的类型
GLKView *view = (GLKView *)self.view;
NSAssert([view isKindOfClass:[GLKView class]],
@"View controller's view is not a GLKView");
//opengl 是基于c语言的,这就意味着需要上下文来保存一些状态
//kEAGLRenderingAPIOpenGLES2 表示使用opengl es 2.0
view.context = [[EAGLContext alloc]
initWithAPI:kEAGLRenderingAPIOpenGLES2];
//将view.context设为接下来运算所要用到的上下文
[EAGLContext setCurrentContext:view.context];
//opengl各个版本有明显的不同,这里GLKBaseEffect帮我们做了统一的处理
//GLKBaseEffect在这个例子中用于颜色渲染
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
//这里返回GLKVector4结构体
self.baseEffect.constantColor = GLKVector4Make(
1.0f, // Red
1.0f, // Green
1.0f, // Blue
1.0f);// Alpha
//清除颜色,清理帧缓存的时候,将其全部设置为这种颜色
//buffer对应的是整块屏幕,屏幕中并不是所有部分都会被渲染,这里相当于设置画布的颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // background color
//下面三步:生成GPU专用缓存
/*1:为缓存生成一个独一无二的标识符,储存在vertexBufferID中
第一个参数表示要生成的标识符的数量*/
glGenBuffers(1,&vertexBufferID);
//2:将标识符绑定缓存
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
//3:复制数据到该缓存中 cpu->gpu
glBufferData( // STEP 3
GL_ARRAY_BUFFER, //GL_ARRAY_BUFFER表示一个顶点数组,还有个选项GL_ELEMENT_ARRAY_BUFFER
sizeof(vertices), // 需要复制的字节数
vertices, // 指向数组的指针
GL_STATIC_DRAW); // 缓存在未来的运算中会如何使用,这里表示这段缓存修改较少,GL_DYNAMIC_DRAW表示修改较多
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
[self.baseEffect prepareToDraw];
// 之前调用glClearColor设置了清理颜色,这里进行颜色清理
glClear(GL_COLOR_BUFFER_BIT);
// 启用顶点缓存渲染操作
//告诉opengl使用多维数组保存数据
glEnableVertexAttribArray(GLKVertexAttribPosition);
//多维数组渲染顶点时,数据的读取方式
glVertexAttribPointer(
GLKVertexAttribPosition, //告诉opengl当前缓存包含每个顶点的位置信息
3, // 每个顶点用三维向量表示
GL_FLOAT, // 向量的每个值是float类型
GL_FALSE, // 小数点固定数据是否可以被改变
sizeof(GLKVector3), // “步幅”,每个顶点需要多少字节保存,这样opengl就知道读取一个顶点后需要跳过多少字节到下一个顶点,使用 sizeof(SceneVertex) 表示缓存中没有额外字节,顶点与顶点之间没有间隔
NULL); // 从缓存的开始位置访问
// 开始渲染
glDrawArrays(GL_TRIANGLES, // 如何处理顶点缓存内的顶点数据,这里告诉opengl去渲染三角形
0, // 所需渲染的第一个顶点的位置
3); // 需要渲染顶点的数量
}
- (void)viewDidUnload
{
[super viewDidUnload];
GLKView *view = (GLKView *)self.view;
[EAGLContext setCurrentContext:view.context];
if (0 != vertexBufferID){
glDeleteBuffers (1,&vertexBufferID);
//设置为0是为了避免缓存被删除后还使用其无效的标识符
vertexBufferID = 0;
}
// Stop using the context created in -viewDidLoad
((GLKView *)self.view).context = nil;
//清除缓存,缓存引用计数为0
[EAGLContext setCurrentContext:nil];
}
@end