在上一篇文章中, 我们学习了OpenGL ES中的一些基本概念, 从这篇文章开始, 将一起学习, 如何通过OpenGL ES去渲染图形.
一、GLKit
GLKit是iOS 5引入的一个为简化OpenGL ES的使用的框架,它对OpenGL ES的函数进行了封装并提供了相关的类和函数,GLKit是Cocoa Touch以及多个其他的框架(包含UIKit)的一部分。而GLKView
和GLKViewController
类名字中的GLK前缀表明这些类是GLKit框架的一部分。
今天学习如何用GLView
来渲染一张图片:
1. 获取顶点数据
首先定义一个结构体,用来保存顶点数据. 用一个三维向量来保存(x,y,z)坐标, 一个二维向量来保存(u,v)坐标
/**
定义顶点类型
*/
typedef struct {
GLKVector3 positionCoord; // (X, Y, Z)
GLKVector2 textureCoord; // (U, V)
} CustomVertexInfo;
定义一个顶点数组, 来保存4个顶点:
@property (nonatomic, assign) CustomVertexInfo *verticesArray; //!< 顶点数组
初始化顶点数据:
self.verticesArray = malloc(sizeof(CustomVertexInfo) * 4);
self.verticesArray[0] = (CustomVertexInfo){{-1, 1, 0}, {0, 1}}; //!< 左上角
self.verticesArray[1] = (CustomVertexInfo){{-1, -1, 0}, {0, 0}}; //!< 左下角
self.verticesArray[2] = (CustomVertexInfo){{1, 1, 0}, {1, 1}}; //!< 右上角
self.verticesArray[3] = (CustomVertexInfo){{1, -1, 0}, {1, 0}}; //!< 右下角
2. 初始化GLKView, 设置EAGLContext
上下文
//!< 初始化 GLKView
CGRect frame = CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.width);
self.glkView = [[GLKView alloc] initWithFrame:frame context:context];
self.glkView.backgroundColor = [UIColor clearColor];
self.glkView.delegate = self;
[self.view addSubview:self.glkView];
//!< 设置 glkView 的上下文为当前上下文
[EAGLContext setCurrentContext:self.glkView.context];
3. 获取图片, 并用GLKTextureLoader
来加载纹理数据
//!< 通过 GLKTextureLoader 来加载纹理,并存放在 GLKBaseEffect 中
NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"example.jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft : @(YES)}; //!< 消除 UIKit 和 GLKit 的坐标差异,否则会上下颠倒
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage]
options:options
error:NULL];
4. 用GLKBaseEffect
保存纹理id
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.target = textureInfo.target;
5. 实现GLKView
的代理方法- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
, 并在代理方法中按照上一篇文章中的使用缓存的7个步骤, 来绘制图片
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
[self.baseEffect prepareToDraw];
//!< 创建顶点缓存
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer); // !< 1:生成
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); // !< 2:绑定
GLsizeiptr bufferSizeBytes = sizeof(CustomVertexInfo) * 4;
glBufferData(GL_ARRAY_BUFFER, bufferSizeBytes, self. verticesArray, GL_STATIC_DRAW); // !< 3:缓存数据
//!< 设置顶点数据
glEnableVertexAttribArray(GLKVertexAttribPosition); // !< 4:启用或禁用
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(CustomVertexInfo), NULL + offsetof(CustomVertexInfo, positionCoord)); // !< 5:设置指针
//!< 设置纹理数据
glEnableVertexAttribArray(GLKVertexAttribTexCoord0); // !< 4:启用或禁用
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(CustomVertexInfo), NULL + offsetof(CustomVertexInfo, textureCoord)); // !< 5:设置指针
//!< 开始绘制
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // !< 6:绘图
//!< 删除顶点缓存
glDeleteBuffers(1, &vertexBuffer); // !< 7:删除
vertexBuffer = 0;
}
6. dealloc
时释放内存, 清空EAGLContext
当前上下文
- (void)dealloc {
if ([EAGLContext currentContext] == self.glkView.context) {
[EAGLContext setCurrentContext:nil];
}
if (_verticesArray) {
free(_verticesArray);
_verticesArray = nil;
}
}
最终效果如图: