上一篇咱們用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