前言
最近在学习OpenGL ES,跟着教程做了一个Demo,在模拟器下的运行效果如下:
Demo代码在这里
OpenGL和OpenGL ES
OpenGL ES是OpenGL的子集,它主要运行在手机上。推荐一个超棒的OpenGL入门教程:Learn OpenGL ;当然也有热心的国人将他翻译过来了,在 这里。通过这些教程的学习,我也算是入了OpenGL的门。
关于将OpenGL运用于iOS上面,也就是使用OpenGL ES,可以看看Apple的官方文档 OpenGL ES Programming Guide for iOS
Demo实现
demo的实现可以分成2个部分:
- 运用OpenGL ES显示静态的场景
- 添加手势操作,让场景动起来
OpenGL ES 渲染
关于在iOS上使用OpenGL ES渲染,可以借助iOS的GLKit框架。他大大简化了OpenGL ES的使用难度。而渲染的主要步骤分成以下几步:
使用GLKit初始化
创建GLKViewController和GLKView,OpenGL ES渲染的结果将会显示在GLKView的layer上面。而将GLKViewController设置成GLKView的delegate,他会控制整个的渲染流程,比如每秒渲染多少次,何时开始渲染等等
设置GLKView的context属性
OpenGL ES渲染的时候需要一个上下文,也就是GLKView的context,对于他的设置如下:
1 2
| view.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3]; [EAGLContext setCurrentContext:view.context];
|
这里创建context是使用OpenGL ES的基石,就相当于你使用GLFW和GLEW对整个环境进行初始化,定义viewport之类的
设置OpenGL ES
设置OpenGL ES的代码和OpenGL的基本相似。
创建shader程序
也就是对你写的glsl文件进行编译链接,最终持有一个Shader programe。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| // load glsl file NSString *vertexFile = [[NSBundle mainBundle] pathForResource:vertexPath ofType:@"glsl"]; NSString *fragmentFile = [[NSBundle mainBundle] pathForResource:fragmentPath ofType:@"glsl"];
NSError *error;
NSString *vertexString = [NSString stringWithContentsOfFile:vertexFile encoding:NSUTF8StringEncoding error:&error]; if (!vertexString) { NSLog(@"vanney code log : error loading vertex shader : %@", error.localizedDescription); exit(1); }
NSString *fragmentString = [NSString stringWithContentsOfFile:fragmentFile encoding:NSUTF8StringEncoding error:&error]; if (!fragmentString) { NSLog(@"vanney code log : error loading fragment shader : %@", error.localizedDescription); exit(1); }
// compiler GLuint vertexShader, fragmentShader; GLint compilerSuccess, linkSuccess; GLchar messages[512];
vertexShader = glCreateShader(GL_VERTEX_SHADER); const GLchar *vertexStringUTF8 = [vertexString UTF8String]; glShaderSource(vertexShader, 1, &vertexStringUTF8, NULL); glCompileShader(vertexShader); glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compilerSuccess); if (compilerSuccess == GL_FALSE) { glGetShaderInfoLog(vertexShader, sizeof(messages), NULL, messages); NSLog(@"vanney code log : compile vertex shader error : %@", [NSString stringWithUTF8String:messages]); exit(1); }
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); const GLchar *fragmentStringUTF8 = [fragmentString UTF8String]; glShaderSource(fragmentShader, 1, &fragmentStringUTF8, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compilerSuccess); if (compilerSuccess == GL_FALSE) { glGetShaderInfoLog(fragmentShader, sizeof(messages), NULL, messages); NSLog(@"vanney code log : compile fragment shader error : %@", [NSString stringWithUTF8String:messages]); exit(1); }
// link self.program = glCreateProgram(); glAttachShader(self.program, vertexShader); glAttachShader(self.program, fragmentShader); glLinkProgram(self.program); glGetProgramiv(self.program, GL_LINK_STATUS, &linkSuccess); if (linkSuccess == GL_FALSE) { glGetProgramInfoLog(self.program, sizeof(messages), NULL, messages); NSLog(@"vanney code log : link shader error : %@", [NSString stringWithUTF8String:messages]); exit(1); }
glDeleteShader(vertexShader); glDeleteShader(fragmentShader);
|
设置各种和顶点相关的缓冲
在缓冲中写入顶点数据,以及定义好GPU如何对这些顶点数据进行读取等等,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // initialize vertex data glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); //glGenBuffers(1, &VEO);
glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.VEO); //glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid *) 0); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid *) (3 * sizeof(GLfloat))); glEnableVertexAttribArray(GLKVertexAttribTexCoord0); glBindVertexArray(0);
|
设置各种纹理缓冲
注意这里要使用 GLKit的GLKTextureLoader
和 GLKTextureInfo
,代码如下:
1 2 3 4 5
| NSError *error; NSString *containerPath = [[NSBundle mainBundle] pathForResource:@"container" ofType:@"jpg"]; self.containerTexture = [GLKTextureLoader textureWithContentsOfFile:containerPath options:nil error:&error]; glActiveTexture(GL_TEXTURE0); glBindTexture(self.containerTexture.target, self.containerTexture.name);
|
开始渲染
只需实现GLKView的delegate (void)glkView:(GLKView *)view drawInRect:(CGRect)rect;
就可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { glEnable(GL_DEPTH_TEST); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //glClear(GL_COLOR_BUFFER_BIT);
[self.shader use];
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(M_PI_4, self.view.bounds.size.width / self.view.bounds.size.height, 0.1f, 100.0f); glUniformMatrix4fv(glGetUniformLocation(self.shader.program, "projection"), 1, GL_FALSE, projectionMatrix.m); GLKMatrix4 viewMatrix = GLKMatrix4MakeLookAt(0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); glUniformMatrix4fv(glGetUniformLocation(self.shader.program, "view"), 1, GL_FALSE, viewMatrix.m);
// bind texture glActiveTexture(GL_TEXTURE0); glBindTexture(self.containerTexture.target, self.containerTexture.name); glUniform1i(glGetUniformLocation(self.shader.program, "ourTexture1"), 0); glActiveTexture(GL_TEXTURE1); glBindTexture(self.faceTexture.target, self.faceTexture.name); glUniform1i(glGetUniformLocation(self.shader.program, "ourTexture2"), 1);
glBindVertexArray(VAO); GLKMatrix4 transModel = [self.transformation getModelViewMatrix]; for (int i = 0; i < 10; ++i) { //GLKMatrix4 modelMatrix = GLKMatrix4TranslateWithVector3(GLKMatrix4Identity, cubePositions[i]); GLKMatrix4 modelMatrix = GLKMatrix4TranslateWithVector3(transModel, cubePositions[i]); GLfloat radian = (GLfloat) (20.0f * i / 180 * M_PI); modelMatrix = GLKMatrix4RotateWithVector3(modelMatrix, radian, GLKVector3Make(1.0f, 0.3f, 0.5f)); glUniformMatrix4fv(glGetUniformLocation(self.shader.program, "model"), 1, GL_FALSE, modelMatrix.m); glDrawArrays(GL_TRIANGLES, 0, 36); } glBindVertexArray(0); }
|
添加手势
参看这篇教程 OpenGL ES Transformations with Gestures
他的中心思想就是:向整个GLKView添加各种手势,通过手势传递的信息来调整物体的model的matrix,也就是从局部坐标到世界坐标转换的matrix。
具体的代码可以参考Github上的Transformation类
参考