原文链接地址:http://www.raywenderlich.com/3664/opengl-es-2-0-for-iphone-tutorial
免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!
ps:非常感谢skingTree为我们提供的这篇翻译教程,感谢yy、小狼、北方加入我的教程翻译团队,谢谢你们!
教程截图:
OpenGL ES 是可以在iphone上实现2D和3D图形编程的低级API。
如果你之前接触过 cocos2d,sparrow,corona,unity 这些框架,你会发现其实它们都是基于OpenGL上创建的。
多数程序员选择使用这些框架,而不是直接调用OpenGL,因为OpenGL实在是太难用了。
而这篇教程,就是为了让大家更好地入门而写的。
在这个系列的文章中,你可以通过一些实用又容易上手的实验,创建类似hello world的APP。例如显示一些简单的立体图形。
流程大致如下:
·创建一个简单的OpenGL app
·编译并运行 vertex & fragment shaders
·通过vertex buffer,在屏幕上渲染一个简单矩形
·使用投影和 model-view 变形。
·渲染一个可以 depth testing的3D对象。
说明:
我并非OpenGL的专家,这些完全是通过自学得来的。如果大家发现哪些不对的地方,欢迎指出。
OpenGL ES1.0 和 OpenGL ES2.0
第一件你需要搞清楚的事,是OpenGL ES 1.0 和 2.0的区别。
他们有多不一样?我只能说他们很不一样。
OpenGL ES1.0:
针对固定管线硬件(fixed pipeline),通过它内建的functions来设置诸如灯光、,vertexes(图形的顶点数),颜色、camera等等的东西。
OpenGL ES2.0:
针对可编程管线硬件(programmable pipeline),基于这个设计可以让内建函数见鬼去吧,但同时,你得自己动手编写任何功能。
“TMD”,你可能会这么想。这样子我还可能想用2.0么?
但2.0确实能做一些很cool而1.0不能做的事情,譬如:toon shader(贴材质).
利用opengles2.0,甚至还能创建下面的这种很酷的灯光和阴影效果:
OpenGL ES2.0只能够在iphone 3GS+、iPod Touch 3G+ 和所有版本的ipad上运行。庆幸现在大多数用户都在这个范围。
开始吧
尽管Xcode自带了OpenGL ES的项目模板,但这个模板自行创建了大量的代码,这样会让初学者感到迷惘。
因此我们通过自行编写的方式来进行,通过一步一步编写,你能更清楚它的工作机制。
启动Xcode,新建项目-选择Window-based Application, 让我们从零开始。
点击下一步,把这个项目命名为HelloOpenGL,点击下一步,选择存放目录,点击“创建”。
CMD+R,build and run。你会看到一个空白的屏幕。
如你所见的,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的方法。
4) 设置layer为不透明(Opaque)
- (void)setupLayer {
_eaglLayer = (CAEAGLLayer*) self.layer;
_eaglLayer.opaque = YES;
}
因为缺省的话,CALayer是透明的。而透明的层对性能负荷很大,特别是OpenGL的层。
(如果可能,尽量都把层设置为不透明。另一个比较明显的例子是自定义tableview cell)
5)创建OpenGL context
- (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。
EAGLContext管理所有通过OpenGL进行draw的信息。这个与Core Graphics context类似。
当你创建一个context,你要声明你要用哪个version的API。这里,我们选择OpenGL ES 2.0.
(容错处理,如果创建失败了,我们的程序会退出)
6)创建render buffer (渲染缓冲区)
- (void)setupRenderBuffer {
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
Render buffer 是OpenGL的一个对象,用于存放渲染过的图像。
有时候你会发现render buffer会作为一个color buffer被引用,因为本质上它就是存放用于显示的颜色。
创建render buffer的三步:
1. 调用glGenRenderbuffers来创建一个新的render buffer object。这里返回一个唯一的integer来标记render buffer(这里把这个唯一值赋值到_colorRenderBuffer)。有时候你会发现这个唯一值被用来作为程序内的一个OpenGL 的名称。(反正它唯一嘛)
我的补充:glGenRenderbuffers (GLsizei n, GLuint* renderbuffers)第一个参数表示要申请几个renderbuffer,第二个参数是一个无符号整型指针,指向生成renderbuffer唯一id的存放处。
2. 调用glBindRenderbuffer ,告诉这个OpenGL:我在后面引用GL_RENDERBUFFER的地方,其实是想用_colorRenderBuffer。其实就是告诉OpenGL,我们定义的buffer对象是属于哪一种OpenGL对象
3. 最后,为render buffer分配空间。renderbufferStorage
7)创建一个 frame buffer (帧缓冲区)
- (void)setupFrameBuffer {
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
}
Frame buffer也是OpenGL的对象,它包含了前面提到的render buffer,以及其它后面会讲到的诸如:depth buffer、stencil buffer 和accumulation buffer。
前两步创建frame buffer的动作跟创建render buffer的动作很类似。(反正也是用一个glBind什么的)
而最后一步 glFramebufferRenderbuffer 这个才有点新意。它让你把前面创建的buffer render依附在frame buffer的GL_COLOR_ATTACHMENT0位置上。
8)清理屏幕
- (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,绿色吧)
这里每个RGB色的范围是0~1,所以每个要除一下255.
下面解析一下每一步动作:
1. 调用glClearColor ,设置一个RGB颜色和透明度,接下来会用这个颜色涂满全屏。
2. 调用glClear来进行这个“填色”的动作(大概就是photoshop那个油桶嘛)。还记得前面说过有很多buffer的话,这里我们要用到GL_COLOR_BUFFER_BIT来声明要清理哪一个缓冲区。
3. 调用OpenGL context的presentRenderbuffer方法,把缓冲区(render buffer和color buffer)的颜色呈现到UIView上。
9)把前面的动作串起来修改一下OpenGLView.m
// 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 连接起来
在HelloOpenGLAppDelegate.h 中修改一下:
// 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在屏幕上显示。
这里是OpenGL的世界。