OpenGL ES基本概念
CPU和GPU各自有着专门为其分配的内存空间,渲染图形的速度绝大部分取决于这两部分内存的交互。OpenGL ES是一种软件技术,用于协调这两部分内存区域之间的数据交换。
〜
缓存
缓存是为CPU与GPU的内存区域交换数据产生的。CPU内存中的数据复制到缓存中后,CPU一般不会再访问这个缓存,GPU会独占缓存,有效的读写内存。几乎所有GPU数据都应该放入缓存中。
具体C语言函数:
- glGenBuffers():为缓存生成一个独一无二的标识符;
- glBindBuffer():绑定缓存,接下来的运算将要使用这个;
- glBufferData()或glBufferSubData():为缓存分配内存,并复制数据到其中
- glEnableVertexAttribArray()或glDisableVertexAttribArray():告诉OpenGL ES在接下来的渲染中是否使用缓存中的数据
- glVertexAttribPointer():告诉OpenGL ES在缓存中的数据类型和所有需要访问的数据内存偏移值
- glDrawArrays():使用当前绑定并启用的缓存数据执行绘图
- glDeleteBuffers ():删除缓存并释放相关的资源
〜
〜
3D坐标系
左右为X轴,上下为Y轴,前后为Z轴
〜
OpenGL ES的简单使用
生成数据
// 定义一个结构体,用于数据处理
typedef struct {
GLKVector3 positionCoords;
}
SceneVertex;
// 顶点数据数组
static const SceneVertex vertices[] =
{
{{-0.5f, -0.5f, 0.0}}, // 左下点
{{ 0.5f, -0.5f, 0.0}}, // 右下点
{{-0.5f, 0.5f, 0.0}}, // 左上点
{{ 0.5f, -0.5f, 0.0}}, // 右下点
{{-0.5f, 0.5f, 0.0}}, // 左上点
{{0.5f, 0.5f, 0.0}} // 右上点
};
创建GLKViewController子类,生成EAGLContext实例,并将其设为当前上下文,初始化GLKBaseEffect,配置OpenGL缓存信息。点击GLKViewController 查看该类更多信息。
// 视频将要加载
- (void)viewDidLoad {
[super viewDidLoad];
// CLKit 为简化iOS中OpenGL ES的使用提供类和函数
// 获取GLKView对象,GLKView简化了通过用Core Animation层来自动创建并管理帧缓存和渲染缓存共享内存所需要做的工作。
GLKView *view = (GLKView *)self.view;
NSAssert([view isKindOfClass:[GLKView class]], @"View controller's view is not a GLKView");
// 初始化EAGLContext实例,并指定OpenGL ES版本为2.0
// 这个实例会封装一个特定于某个平台的OpenGL ES上下文
view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
// 设置当前上下文
// (一个应用可以使用多个上下文,所有的的OpenGL ES操作都是针对当前上下文的,不重新设置的话,会在原来的上下文中操作)
[EAGLContext setCurrentContext:view.context];
// 初始化GLKBaseEffect实例,简化OpenGL ES的常用操作
self.baseEffect = [[GLKBaseEffect alloc] init];
// 颜色不变
self.baseEffect.useConstantColor = GL_TRUE;
// 填充颜色RGBA
self.baseEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
// 设置当前上下文清除颜色(默认背景色)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// 1.为缓存生成一个独一无二的标识符
glGenBuffers(1, &vertexBufferID);
// 2.绑定缓存,接下来的运算将要使用这个
// 第一个参数:指定要绑定哪一种类型的缓存,GL_ARRAY_BUFFER类型代表指定一个顶点数组
// 第二个参数:标识ID
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
// 3.为缓存分配内存,并复制数据到其中
glBufferData(GL_ARRAY_BUFFER, // 指定要绑定哪一种类型的缓存
sizeof(vertices), // 数据字节数
vertices, // 被复制数据的地址
GL_STATIC_DRAW); // 缓存在未来将会被怎么使用
// GL_STATIC_DRAW:缓存中的内容很少被修改,适合复制到GPU控制的内存
// GL_DYNAMIC_DRAW:缓存中的内容会频繁改变
}
绘制图片
// 已经准备好当前OpenGL ES的上下文,可以开始绘图
// 该方法的语义与drawRect:方法相同,用来绘制view的内容的
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// 开始绘图
[self.baseEffect prepareToDraw];
// 开始清除颜色,前面有用glClearColor设置过
glClear(GL_COLOR_BUFFER_BIT);
// 4.告诉OpenGL ES在接下来的渲染中使用缓存中的数据
glEnableVertexAttribArray(GLKVertexAttribPosition);
// 5.告诉OpenGL ES在缓存中的数据类型和所有需要访问的数据内存偏移值
glVertexAttribPointer(GLKVertexAttribPosition,
3, // 每个位置有3部分
GL_FLOAT, // 每个部分都是一个浮点型的值
GL_FALSE, // 小数点固定数据是否可以改变,使用可节省内存,不使用可减少GPU运算且提高精度,
sizeof(SceneVertex), // 步幅,每个顶点的保存需要多少字节
NULL); // OpenGL ES可以从当前绑定的顶点缓存的开始位置访问顶点数据
// 6.使用当前绑定并启用的缓存数据执行绘图
glDrawArrays(GL_TRIANGLES, //怎么处理在绑定的顶点缓存内的顶点数据(这里是渲染三角形)
0, // 第一个顶点的位置
6); // 需要渲染的顶点数量
}
清空数据
// 控制器要销毁时,清空数据
- (void)dealloc {
GLKView *view = (GLKView *)self.view;
[EAGLContext setCurrentContext:view.context];
// 避免缓存已被删除
if (0 != vertexBufferID) {
// 7.删除缓存并释放相关的资源
glDeleteBuffers (1, &vertexBufferID);
vertexBufferID = 0;
}
((GLKView *)self.view).context = nil;
[EAGLContext setCurrentContext:nil];
}