教程截图:
OpenGL ES 是可以在iphone上实现2D和3D图形编程的低级API。
OpenGL ES1.0:
利用opengles2.0,甚至还能创建下面的这种很酷的灯光和阴影效果:
OpenGL ES2.0只能够在iphone 3GS+、iPod Touch 3G+ 和所有版本的ipad上运行。庆幸现在大多数用户都在这个范围。
如你所见的,Window-based 模板创建了一个没有view、没有view controller或者其它东西的项目。它只包含了一个必须的UIWindow。
File/New File,新建文件:选择iOS\Cocoa Touch\Objective-c Class, 点击下一步。
选择subclass UIView,点击下一步,命名为 OpenGLView.m., 点击保存。
接下来,你要在这个OpenGLView.m 文件下加入很多代码。
1) 添加必须的framework (框架)
加入:OpenGLES.frameworks 和 QuartzCore.framework
在项目的Groups&Files 目录下,选择target “HelloOpenGL”,展开Link Binary with Libraries部分。这里是项目用到的框架。
“+”添加,选择OpenGLES.framework, 重复一次把QuartzCore.framework也添加进来。
2)修改OpenGLView.h
如下: 引入OpenGL的Header,创建一些后面会用到的实例变量。
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
@interface OpenGLView : UIView {
CAEAGLLayer* _eaglLayer;
EAGLContext* _context;
GLuint _colorRenderBuffer;
}
@end
3)设置layer class 为 CAEAGLLayer
+ (Class)layerClass {
return [CAEAGLLayer class];
}
想要显示OpenGL的内容,你需要把它缺省的layer设置为一个特殊的layer。(CAEAGLLayer)。这里通过直接复写layerClass的方法。
- (void)setupLayer {
_eaglLayer = (CAEAGLLayer*) self.layer;
_eaglLayer.opaque = YES;
}
因为缺省的话,CALayer是透明的。而透明的层对性能负荷很大,特别是OpenGL的层。
- (void)setupContext {
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
_context = [[EAGLContext alloc] initWithAPI:api];
if (!_context) {
NSLog(@"Failed to initialize OpenGLES 2.0 context");
exit(1);
}
if (![EAGLContext setCurrentContext:_context]) {
NSLog(@"Failed to set current OpenGL context");
exit(1);
}
}
无论你要OpenGL帮你实现什么,总需要这个 EAGLContext。
- (void)setupRenderBuffer {
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
Render buffer 是OpenGL的一个对象,用于存放渲染过的图像。Frame buffer也是OpenGL的对象,它包含了前面提到的render buffer,以及其它后面会讲到的诸如:depth buffer、stencil buffer 和 accumulation buffer。
- (void)setupFrameBuffer { GLuint framebuffer; glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer); }
- (void)render {
glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
为了尽快在屏幕上显示一些什么,在我们和那些 vertexes、shaders打交道之前,把屏幕清理一下,显示另一个颜色吧。(RGB 0, 104, 55,绿色吧)
// Replace initWithFrame with this
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupLayer];
[self setupContext];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self render];
}
return self;
}
// Replace dealloc method with this
- (void)dealloc
{
[_context release];
_context = nil;
[super dealloc];
}
10)把App Delegate和OpenGLView 连接起来
// At top of file
#import "OpenGLView.h"
// Inside @interface
OpenGLView* _glView;
// After @interface
@property (nonatomic, retain) IBOutlet OpenGLView *glView;
接下来修改.m文件:
// At top of file
@synthesize glView=_glView;
// At top of application:didFinishLaunchingWithOptions
CGRect screenBounds = [[UIScreen mainScreen] bounds];
self.glView = [[[OpenGLView alloc] initWithFrame:screenBounds] autorelease];
[self.window addSubview:_glView];
// In dealloc
[_glView release];
一切顺利的话,你就能看到一个新的view在屏幕上显示。
添加shaders: 顶点着色器 和 片段着色器 在OpenGL ES2.0 的世界,在场景中渲染任何一种几何图形,你都需要创建两个称之为“着色器”的小程序。 着色器由一个类似C的语言编写- GLSL。知道就好了,我们不深究。 这个世界有两种着色器(Shader):
打开你的xcode,File\New\New File… 选择 iOS\Other\Empty, 点击下一步。命名为: SimpleVertex.glsl 点击保存。 打开这个文件,加入下面的代码:
我们一行一行解析:
一个简单的vertex shader 就是这样了,接下来我们再创建一个简单的fragment shader。
下面解析:
编译 Vertex shader 和 Fragment shader 目前为止,xcode仅仅会把这两个文件copy到application bundle中。我们还需要在运行时编译和运行这些shader。 你可能会感到诧异。为什么要在app运行时编译代码? 这样做的好处是,我们的着色器不用依赖于某种图形芯片。(这样才可以跨平台嘛) 下面开始加入动态编译的代码,打开 OpenGLView.m 在 initWithFrame: 方法上方加入:
下面解析:1 这是一个UIKit编程的标准用法,就是在NSBundle中查找某个文件。大家应该熟悉了吧。 2 调用 glCreateShader来创建一个代表shader 的OpenGL对象。这时你必须告诉OpenGL,你想创建 fragment shader还是vertex shader。所以便有了这个参数:shaderType 3 调用glShaderSource ,让OpenGL获取到这个shader的源代码。(就是我们写的那个)这里我们还把NSString转换成C-string 4 最后,调用glCompileShader 在运行时编译shader 5 大家都是程序员,有程序的地方就会有fail。有程序员的地方必然会有debug。如果编译失败了,我们必须一些信息来找出问题原因。 glGetShaderiv 和 glGetShaderInfoLog 会把error信息输出到屏幕。(然后退出) 我们还需要一些步骤来编译vertex shader 和frament shader。 - 把它们俩关联起来 - 告诉OpenGL来调用这个程序,还需要一些指针什么的。 在compileShader: 方法下方,加入这些代码
下面是解析:
1 在 initWithFrame方法里,在调用render之前要加入这个:
2 在 @interface in OpenGLView.h 中添加两个变量:
编译!运行!如果你仍能正常地看到之前那个绿色的屏幕,就证明你前面写的代码都很好地工作了。 |
为这个简单的长方形创建 Vertex Data! 在这里,我们打算在屏幕上渲染一个正方形,如下图:
在你用OpenGL渲染图形的时候,时刻要记住一点,你只能直接渲染三角形,而不是其它诸如矩形的图形。所以,一个正方形需要分开成两个三角形来渲染。
这段代码的作用是:
创建Vertex Buffer 对象 传数据到OpenGL的话,最好的方式就是用Vertex Buffer对象。 基本上,它们就是用于缓存顶点数据的OpenGL对象。通过调用一些function来把数据发送到OpenGL-land。(是指OpenGL的画面?) 这里有两种顶点缓存类型 – 一种是用于跟踪每个顶点信息的(正如我们的Vertices array),另一种是用于跟踪组成每个三角形的索引信息(我们的Indices array)。 下面我们在initWithFrame中,加入一些代码:
下面是定义这个setupVBOs:
如你所见,其实很简单的。这其实是一种之前也用过的模式(pattern)。glGenBuffers - 创建一个Vertex Buffer 对象 glBindBuffer – 告诉OpenGL我们的vertexBuffer 是指GL_ARRAY_BUFFER glBufferData – 把数据传到OpenGL-land 想起哪里用过这个模式吗?要不再回去看看frame buffer那一段? 万事俱备,我们可以通过新的shader,用新的渲染方法来把顶点数据画到屏幕上。 用这段代码替换掉之前的render:
你可能会疑惑,为什么这个长方形刚好占满整个屏幕。在缺省状态下,OpenGL的“camera”位于(0,0,0)位置,朝z轴的正方向。 当然,后面我们会讲到projection(投影)以及如何控制camera。
|