博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[OpenGL ES 01]iOS上OpenGL ES之初体验
阅读量:7093 次
发布时间:2019-06-28

本文共 8758 字,大约阅读时间需要 29 分钟。

[OpenGL ES 01]OpenGL ES之初体验

罗朝辉 ()

本文遵循“”创作公用协议
 

一,什么是 OpenGL ES?

OpenGL ES 是专门为手持设备制定的 3D 规范,它是 OpenGL 的简化版,该规范由制定,目前最新规范版本为 。 OpenGL ES 可以在不同手机系统上实现,也可以在浏览器上实现(Web GL)。目前较新的 iOS 支持OpenGL ES 2.0,在这里,我将介绍如何在 iOS 上使用 OpenGL ES 2.0。

 

二,在iOS上如何使用OpenGL ES?

1,准备工作

1),打开XCode(我使用的是4.2),创建一个 Empty Application。

2),命名为 Tutorial01,选择Device Family为iPhone,保持默认选中的use Automatic Reference Counting来使用自动引用计数。

3),添加需要用到的库,在iOS平台上进行OpenGL ES 开发,OpenGLES.framework和QuartzCore.framework这两个库是必须的,选中Target:Tutorial01,在Build phase->Link Binary With Libraries中点击 + 号来添加这两个库:

添加完毕,工程结构如下图,你可以把这两个 framework 拖到 Frameworks 文件夹中,谁也不想工程结构乱七八糟的吧?

4),至此,编译运行,模拟器是一片空白的!因为Empty Application模版就是Empty,里面甚至连一个Window都木有。因此,我们需要添加一个 Window。右击 Supporting Files文件夹,选择 New File->User Interface->Window:

输入名称:MainWindow

5),为了让 AppDelegate 与 Window 关联起来,我们还需要在MainWindow.xib中创建一个Object对象。选中MainWindow.xib,向其中拖入一个 Object 对象:

添加完毕,效果如下:

6),然后我们修改该 Object 的Custom Class为 AppDelegate,这样它在 xib 中代表代码中的 AppDelegate了。

7),为了将 Window与App Delegate 关联起来,我们需要在 AppDelegate.h中的代码 window 属性前添加 IBOutlet 修饰符:

@property (strong, nonatomic) IBOutlet UIWindow *window;

8),选中MainWindow.xib,右击 AppDelegate,将Outlet window拖拽到其上方的 Window上,这样AppDelegate中的window就与真实的 Window 关联起来。

9),同样,我们还需要修改File's Owner的 Custom Class 为 UIApplication,使用与8)中同样的拖拽技巧,将 File's Owner的 delegate 与 App Delegate 关联起来。

10),至此准备工作完毕,不妨编译运行一下,模拟器依然一片空白,那是因为我们还没有在 Window 上添加 view,下面我们将来添加一个 view。

2,设置 OpenGL ES 运行环境

1),虽然 iOS 5在 GLKit 中提供了方便使用 OpenGL ES 的辅助 GLKView,但在这里,我们还是从零开始手工打造我们自己 GL ES view,从而更进一步了解在 iOS 上 OpenGL ES 是使用的。在Tutorial01目录中 New File,选择 User Interface->View作为模版,命名为 OpenGLView:

2),修改 OpenGLView.h为:

#import 
#import
#include
#include
@interface OpenGLView : UIView { CAEAGLLayer* _eaglLayer; EAGLContext* _context; GLuint _colorRenderBuffer; GLuint _frameBuffer;}@end

这些变量在后面会有介绍。

3),在 OpenGLView.m 中添加如下函数:

+ (Class)layerClass {    // 只有 [CAEAGLLayer class] 类型的 layer 才支持在其上描绘 OpenGL 内容。    return [CAEAGLLayer class];}

为了让 UIView 显示 opengl 内容,我们必须将默认的 layer 类型修改为 CAEAGLLayer 类型(这种动态修改返回类类型的手段在  一文也有应用)。

4),默认的 CALayer 是透明的,我们需要将它设置为 opaque 才能看到在它上面描绘的东西。为此,我们使用匿名 category 技巧,在 OpenGLView.m的开头(在@implementation OpenGLView 的上面)添加匿名 category,并声明私有函数 setupLayer:

// 使用匿名 category 来声明私有成员@interface OpenGLView()-(void)setupLayer;@end

接着,在 @implementation 与 @end 之间,添加 setupLayer 的实现:

- (void)setupLayer{    _eaglLayer = (CAEAGLLayer*) self.layer;        // CALayer 默认是透明的,必须将它设为不透明才能让其可见    _eaglLayer.opaque = YES;        // 设置描绘属性,在这里设置不维持渲染内容以及颜色格式为 RGBA8    _eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:                                    [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];}

5),至此 layer 的配置已经就绪,下面我们来创建与设置与 OpenGL ES 相关的东西。首先,我们需要创建OpenGL ES 渲染上下文(在iOS中对应的实现为),这个 context 管理所有使用OpenGL ES 进行描绘的状态,命令以及资源信息。然后,需要将它设置为当前 context,因为我们要使用 OpenGL ES 进行渲染(描绘)。在匿名 category 中添加 -(void)setupContext; 声明,并在@implement与@end之间添加其实现。这与使用 Core Graphics 进行描绘必须创建 Core Graphics Context 的道理是一样。

- (void)setupContext {    // 指定 OpenGL 渲染 API 的版本,在这里我们使用 OpenGL ES 2.0     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);    }}

6),创建 renderbuffer

有了上下文,openGL还需要在一块 buffer 上进行描绘,这块 buffer 就是 RenderBuffer(OpenGL ES 总共有三大不同用途的color buffer,depth buffer 和 stencil buffer,这里是最基本的 color buffer)。下面,我们依然创建私有方法 setupRenderBuffer 来生成 color buffer:

- (void)setupRenderBuffer {    glGenRenderbuffers(1, &_colorRenderBuffer);    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);    // 为 color renderbuffer 分配存储空间    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];

glGenRenderbuffers 的原型为:

void glGenRenderbuffers (GLsizei n, GLuint* renderbuffers)

它是为 renderbuffer 申请一个 id(或曰名字)。参数 n 表示申请生成 renderbuffer 的个数,而 renderbuffers 返回分配给 renderbuffer 的 id,注意:返回的 id 不会为0,id 0 是OpenGL ES 保留的,我们也不能使用 id 为0的 renderbuffer。

glBindRenderbuffer 的原型为:

void glBindRenderbuffer (GLenum target, GLuint renderbuffer)

这个函数将指定 id 的 renderbuffer 设置为当前 renderbuffer。参数 target 必须为 GL_RENDERBUFFER,参数 renderbuffer 是就是使用 glGenRenderbuffers 生成的 id。当指定 id 的 renderbuffer 第一次被设置为当前 renderbuffer 时,会初始化该 renderbuffer 对象,其初始值为:

width 和 height:像素单位的宽和高,默认值为0;

internal format:内部格式,三大 buffer 格式之一 -- color,depth or stencil;

Color bit-depth:仅当内部格式为 color 时,设置颜色的 bit-depth,默认值为0;

Depth bit-depth:仅当内部格式为 depth时,默认值为0;

Stencil bit-depth: 仅当内部格式为 stencil,默认值为0;

函数 - (BOOL)renderbufferStorage:(NSUInteger)target fromDrawable:(id<EAGLDrawable>)drawable; 在内部使用 drawable(在这里是 EAGLLayer)的相关信息(还记得在 setupLayer 时设置了drawableProperties的一些属性信息么?)作为参数调用了 glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); 后者 glRenderbufferStorage 指定存储在 renderbuffer 中图像的宽高以及颜色格式,并按照此规格为之分配存储空间。在这里,将使用我们在前面设置 eaglLayer 的颜色格式 RGBA8, 以及 eaglLayer 的宽高作为参数调用 glRenderbufferStorage。

7),创建 framebuffer object

framebuffer object 通常也被称之为 FBO,它相当于 buffer(color, depth, stencil)的管理者,三大buffer 可以附加到一个 FBO 上。我们是用 FBO 来在 off-screen buffer上进行渲染。下面,我们依然创建私有方法 setupFrameBuffer 来生成 frame buffer:

- (void)setupFrameBuffer {        glGenFramebuffers(1, &_frameBuffer);    // 设置为当前 framebuffer    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);    // 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,                               GL_RENDERBUFFER, _colorRenderBuffer);}

setupFrameBuffer 大体与前面的 setupRenderBuffer 相同,由 glGenFramebuffers分配的 id也不可能是 0,id 为 0 的 framebuffer 是OpenGL ES 保留的,它指向窗口系统提供的 framebuffer,我们同样不能使用 id 为 0 的framebuffer,否则系统会出错。glFramebufferRenderbuffer的函数原型为:

void glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)

该函数是将相关 buffer(三大buffer之一)attach到framebuffer上(如果 renderbuffer不为 0,知道前面为什么说glGenRenderbuffers 返回的id 不会为 0 吧)或从 framebuffer上detach(如果 renderbuffer为 0)。参数 attachment 是指定 renderbuffer 被装配到那个装配点上,其值是GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT中的一个,分别对应 color,depth和 stencil三大buffer。

8),当 UIView 在进行布局变化之后,由于 layer 的宽高变化,导致原来创建的 renderbuffer不再相符,我们需要销毁既有 renderbuffer 和 framebuffer。下面,我们依然创建私有方法 destoryRenderAndFrameBuffer 来销毁生成的 buffer:

- (void)destoryRenderAndFrameBuffer{    glDeleteFramebuffers(1, &_frameBuffer);    _frameBuffer = 0;    glDeleteRenderbuffers(1, &_colorRenderBuffer);    _colorRenderBuffer = 0;}

9), 至此,理论也讲得足够多了,让我们来画点东西看看效果如何。下面,我们依然创建私有方法 render 来进行真正的描绘:

- (void)render {    glClearColor(0, 1.0, 0, 1.0);    glClear(GL_COLOR_BUFFER_BIT);    [_context presentRenderbuffer:GL_RENDERBUFFER];}

glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampfalpha) 用来设置清屏颜色,默认为黑色;glClear (GLbitfieldmask)用来指定要用清屏颜色来清除由mask指定的buffer,mask 可以是 GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT的自由组合。在这里我们只使用到 color buffer,所以清除的就是 clolor buffer。- (BOOL)presentRenderbuffer:()target 是将指定 renderbuffer 呈现在屏幕上,在这里我们指定的是前面已经绑定为当前 renderbuffer 的那个,在 renderbuffer 可以被呈现之前,必须调用 为之分配存储空间。在前面设置 drawable 属性时,我们设置  为FALSE,表示不想保持呈现的内容,因此在下一次呈现时,应用程序必须完全重绘一次。将该设置为 TRUE 对性能和资源影像较大,因此只有当renderbuffer需要保持其内容不变时,我们才设置   为 TRUE。

三,进行渲染

1,有了前面的准备工作,我们来看看我们的成果吧。首先在 AppDelegate中使用 OpenGLView作为 window 的view,修改 AppDelegate.h为:

#import 
#import "OpenGLView.h"@interface AppDelegate : UIResponder
{ OpenGLView* _glView;}@property (strong, nonatomic) IBOutlet UIWindow *window;@property (strong, retain) IBOutlet OpenGLView *glView;@end

2,在 AppDelegate.m 中实现如下代码:

@implementation AppDelegate@synthesize window = _window;@synthesize glView = _glView;- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];        CGRect screenBounds = [[UIScreen mainScreen] bounds];        self.glView = [[OpenGLView alloc] initWithFrame:screenBounds];    [self.window addSubview:self.glView];        self.window.backgroundColor = [UIColor whiteColor];    [self.window makeKeyAndVisible];    return YES;}

由于我们使用 ARC,所以不必担心资源的释放。

3,返回 OpenGLView.m,在其中添加函数:

- (void)layoutSubviews {    [self setupLayer];            [self setupContext];        [self destoryRenderAndFrameBuffer];    [self setupRenderBuffer];            [self setupFrameBuffer];            [self render];}

4,编译运行,小功告成:

 

5,如果你还没有保存你的代码,选择 File-Source Control->Commit, 提交你的代码到 git 中吧,时常提交代码是个好习惯。后续文章我们还将使用到在这里编写的代码。本文源代码可以在这里查看与下载:

四,Refference

转载地址:http://zniql.baihongyu.com/

你可能感兴趣的文章
Linux学习历程(持续更新整理中)
查看>>
Linux查看物理CPU个数、核数、逻辑CPU个数
查看>>
软件设计模式详解:OCP原则
查看>>
Apache服务器常规操作
查看>>
qt cef嵌入web
查看>>
Java程序员面试失败的5大原因
查看>>
过滤器(Filter)
查看>>
外观模式
查看>>
Webmin|Linux管理员远程管理工具
查看>>
【温故而知新-Javascript】比较 undefined 和 null 值
查看>>
CentOS中iptables防火墙 开放80端口方法
查看>>
Kafka 在行动:7步实现从RDBMS到Hadoop的实时流传输
查看>>
[内核]Linux workqueue
查看>>
云计算开始。。。
查看>>
利用sys.dm_db_index_physical_stats查看索引碎片等数据
查看>>
jquery html动态添加的元素绑定事件详解
查看>>
日常英语---九、MapleStory Link Skills Guide
查看>>
最强科技实力支撑海尔走出“全球化”道路
查看>>
“平潭-高雄”货运直航顺利首航
查看>>
2018年澳门赌场毛收入增14% 贵宾厅增长放缓
查看>>