上一篇咱们用GLKit加载了一张图片,学会了如何用GLKit渲染纹理,如今咱们将用GLKit作一些更有趣的事情,好比下面咱们来作一个这样的效果:数组
简单分析一下这个效果,一个正方体6个面都加载有一张纹理,并围绕一个任意角度的轴作旋转,下面具体看一下实现代码:缓存
#import "ViewController.h"
#import <GLKit/GLKit.h>
typedef struct {
GLKVector3 positionCoord;
GLKVector2 textureCoord;
} CMVertex;
@interface ViewController ()<GLKViewDelegate>
@property (nonatomic, strong) EAGLContext *mContext;
@property (nonatomic, strong) GLKBaseEffect *effect;
@property (nonatomic, assign) GLuint angle;
@property (nonatomic, strong) GLKView *glkView ;
@property (nonatomic, assign) GLuint buffer ;
@property (nonatomic, assign) CMVertex *vertices;
@property (nonatomic, strong) CADisplayLink *displayLink;
@end
static NSUInteger vertexCount = 36;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self prepareGlInfo];
[self prepareGlData];
[self setupDisplayLine];
}
- (EAGLContext *)mContext {
if(!_mContext) {
_mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if(!_mContext) {
_mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
}
return _mContext;
}
- (void)prepareGlInfo {
[EAGLContext setCurrentContext:self.mContext];
self.glkView = [[GLKView alloc] initWithFrame:self.view.bounds context:self.mContext];
self.glkView .delegate = self;
self.glkView .drawableDepthFormat = GLKViewDrawableDepthFormat24;
[self.view addSubview:self.glkView ];
glClearColor(1.0, 1.0, 1.0, 1.0);
}
- (void)dealloc
{
if([EAGLContext currentContext] == self.glkView.context) {
[EAGLContext setCurrentContext:nil];
}
if(_buffer) {
glDeleteBuffers(1, &_buffer);
_buffer = 0;
}
if (_vertices) {
free(_vertices);
_vertices = nil;
}
[self.displayLink invalidate];
}
- (void)prepareGlData {
_vertices = malloc(sizeof(CMVertex) * vertexCount);
//前面
_vertices[0] = (CMVertex){{-0.5, 0.5, 0.5}, {0.0, 1.0}};
_vertices[1] = (CMVertex){{-0.5, -0.5, 0.5}, {0.0, 0.0}};
_vertices[2] = (CMVertex){{0.5, -0.5, 0.5}, {1.0, 0.0}};
_vertices[3] = (CMVertex){{0.5, -0.5, 0.5}, {1.0, 0.0}};
_vertices[4] = (CMVertex){{0.5, 0.5, 0.5}, {1.0, 1.0}};
_vertices[5] = (CMVertex){{-0.5, 0.5, 0.5}, {0.0, 1.0}};
//右面
_vertices[6] = (CMVertex){{0.5, 0.5, 0.5}, {0.0, 1.0}};
_vertices[7] = (CMVertex){{0.5, -0.5, 0.5}, {0.0, 0.0}};
_vertices[8] = (CMVertex){{0.5, -0.5, -0.5}, {1.0, 0.0}};
_vertices[9] = (CMVertex){{0.5, -0.5, -0.5}, {1.0, 0.0}};
_vertices[10] = (CMVertex){{0.5, 0.5, -0.5}, {1.0, 1.0}};
_vertices[11] = (CMVertex){{0.5, 0.5, 0.5}, {0.0, 1.0}};
//后面
_vertices[12] = (CMVertex){{0.5, 0.5, -0.5}, {0.0, 1.0}};
_vertices[13] = (CMVertex){{0.5, -0.5, -0.5}, {0.0, 0.0}};
_vertices[14] = (CMVertex){{-0.5, -0.5, -0.5}, {1.0, 0.0}};
_vertices[15] = (CMVertex){{-0.5, -0.5, -0.5}, {1.0, 0.0}};
_vertices[16] = (CMVertex){{-0.5, 0.5, -0.5}, {1.0, 1.0}};
_vertices[17] = (CMVertex){{0.5, 0.5, -0.5}, {0.0, 1.0}};
//左
_vertices[18] = (CMVertex){{-0.5, 0.5, -0.5}, {0.0, 1.0}};
_vertices[19] = (CMVertex){{-0.5, -0.5, -0.5}, {0.0, 0.0}};
_vertices[20] = (CMVertex){{-0.5, -0.5, 0.5}, {1.0, 0.0}};
_vertices[21] = (CMVertex){{-0.5, -0.5, 0.5}, {1.0, 0.0}};
_vertices[22] = (CMVertex){{-0.5, 0.5, 0.5}, {1.0, 1.0}};
_vertices[23] = (CMVertex){{-0.5, 0.5, -0.5}, {0.0, 1.0}};
//上
_vertices[24] = (CMVertex){{-0.5, 0.5, -0.5}, {0.0, 1.0}};
_vertices[25] = (CMVertex){{-0.5, 0.5, 0.5}, {0.0, 0.0}};
_vertices[26] = (CMVertex){{0.5, 0.5, 0.5}, {1.0, 0.0}};
_vertices[27] = (CMVertex){{0.5, 0.5, 0.5}, {1.0, 0.0}};
_vertices[28] = (CMVertex){{0.5, 0.5, -0.5}, {1.0, 1.0}};
_vertices[29] = (CMVertex){{-0.5, 0.5, -0.5}, {0.0, 1.0}};
//下
_vertices[30] = (CMVertex){{-0.5, -0.5, 0.5}, {0.0, 1.0}};
_vertices[31] = (CMVertex){{-0.5, -0.5, -0.5}, {0.0, 0.0}};
_vertices[32] = (CMVertex){{0.5, -0.5, -0.5}, {1.0, 0.0}};
_vertices[33] = (CMVertex){{0.5, -0.5, -0.5}, {1.0, 0.0}};
_vertices[34] = (CMVertex){{0.5, -0.5, 0.5}, {1.0, 1.0}};
_vertices[35] = (CMVertex){{-0.5, -0.5, 0.5}, {0.0, 1.0}};
glGenBuffers(1, &_buffer);
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(CMVertex) * vertexCount, _vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(CMVertex), NULL + offsetof(CMVertex, positionCoord));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(CMVertex), NULL + offsetof(CMVertex, textureCoord));
NSString *path = [[NSBundle mainBundle] pathForResource:@"meinv" ofType:@"jpg"];
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:@{GLKTextureLoaderOriginBottomLeft:@1} error:nil];
self.effect = [[GLKBaseEffect alloc] init];
self.effect.texture2d0.name = textureInfo.name;
self.effect.texture2d0.target = textureInfo.target;
}
- (void)setupDisplayLine {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateAngle)];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)updateAngle {
self.angle += 1;
self.angle = self.angle % 360;
self.effect.transform.modelviewMatrix = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(self.angle), 0.3, 0.5, 0.2);
[self.glkView display];
}
# pragma mark -GLKViewDelegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
}
@end复制代码
下面来简单分析一下这段代码,首先咱们定义了一个结构体CMVertex用来表示顶点信息,包括顶点坐标(positionCoord)和纹理坐标(textureCoord).bash
上一篇咱们提到过OpenGL ES 须要渲染上下文和绘图表面才能完成图形图像的绘制,因此第一步须要配置好渲染上下文和绘制表面。这里咱们用prepareGlInfo方法来完成这个任务。须要注意的是这里咱们渲染的是一个立体画面,须要开启深度测试因此须要提早配置好深度缓冲区的格式,这里咱们设置为GLKViewDrawableDepthFormat24。函数
而后咱们须要配置好顶点和纹理坐标数据。简单说一下,由于正方体有6个面,每月由2个三角形组成因此是36个顶点。这里顶点坐标的设置就不说了,你们只须要知道原点在正方体正中心而后想象一下你站在6个方位(上下左右先后)就能知道顶点坐标,纹理坐标你们若是有不清楚的能够翻看前面关于OpenGL的文章。这里须要重点说一下顶点数据(包括顶点坐标和纹理坐标)是如何从cpu到gpu的以及OpenGL如何从GPU中读取这些数据。首先顶点数据存储在_vertices数组里,也就是cpu中,这数组也就是咱们常说的VAO(Vertex Array Object);而后利用glGenBuffer,glBindBuffer生成并绑定顶点缓冲区即VBO(Vertex Buffer Object),利用glBufferData将顶点数据拷贝到GPU中;接着利用glEnableVertexAttribArray开启相应通道并利用glVertexAttribPointer设置数据的读取方式,好比这里首先开启了顶点坐标的属性通道,而且告诉GPU从GLKVertexAttribPosition这个属性通道读取数据,第一个顶点数据的开始位置为NULL + offsetof(CMVertex, positionCoord),每次读取3个GLFloat数据,不须要作归一化,读取完一个顶点坐标后下一个顶点坐标的数据和当前顶点坐标数据间隔sizeof(CMVertex)个字节(也就是步长为sizeof(CMVertex)),有人可能会问为何是从NULL开始读取数据,不该该是某个指针地址吗?然而并非由于这里已经不是从你们熟悉的CPU内存读取数据了,而是在GPU缓存中。同理纹理数据也是同样的,咱们只要确认从哪里来读第一个纹理坐标,每次读几个基础数据(即一个纹理坐标包含几个基础数据),每次度的基础数据的类型,读取完后这些数据是否须要作归一化,以及读取下一个纹理坐标时须要移动多远开始读,就可以明确的告诉GPU纹理坐标的数据如何读取了。至于这些函数的具体参数说明在上一节已经介绍过,有疑惑的同窗不妨翻过头再看看。完成这些后最后咱们能够利用GLKTextureLoader载入纹理图片,并讲纹理信息配置到GLBaseEffect中。到这里关于OpenGL的准备工做基本就作完了,咱们能够开始绘制了,实现GLKViewDelegate的代理方法glkView:(GLKView *)view drawInRect:(CGRect)rect来完成具体的绘制工做,不过不要忘记开启深度测试。oop
上面已经完成了基本的绘制工做,不过只是一个不会动的静态效果,那么若是让这个正方体旋转起来呢?这里咱们利用CADisplayLink定时刷新并重绘画面就好了,重绘的时候注意须要利用GLBaseEffect设置模型视图矩阵来完成旋转操做。设置完模型视图矩阵后不要忘记调用[self.glkView display]从新绘制视图。post