[OpenGL ES 01]OpenGL ES之初体验

罗朝辉 (http://blog.csdn.net/kesalin)

本文遵循“署名-非商业用途-保持一致”创作公用协议

一,什么是 OpenGL ES?

OpenGL ES 是专门为手持设备制定的 3D 规范,它是 OpenGL 的简化版,该规范由khronos.org制定,目前最新规范版本为 3.0。 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 <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;GLuint _frameBuffer;
}
@end

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

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

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

为了让 UIView 显示 opengl 内容,我们必须将默认的 layer 类型修改为 CAEAGLLayer 类型(这种动态修改返回类类型的手段在 [深入浅出Cocoa]详解键值观察(KVO)及其实现机理 一文中也有应用)。

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中对应的实现为EAGLContext),这个 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);// 设置为当前 framebufferglBindFramebuffer(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:(NSUInteger)target 是将指定 renderbuffer 呈现在屏幕上,在这里我们指定的是前面已经绑定为当前 renderbuffer 的那个,在 renderbuffer 可以被呈现之前,必须调用renderbufferStorage:fromDrawable: 为之分配存储空间。在前面设置 drawable 属性时,我们设置 kEAGLDrawablePropertyRetainedBacking 为FALSE,表示不想保持呈现的内容,因此在下一次呈现时,应用程序必须完全重绘一次。将该设置为 TRUE 对性能和资源影像较大,因此只有当renderbuffer需要保持其内容不变时,我们才设置 kEAGLDrawablePropertyRetainedBacking  为 TRUE。

三,进行渲染

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

#import <UIKit/UIKit.h>
#import "OpenGLView.h"@interface AppDelegate : UIResponder <UIApplicationDelegate>
{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 中吧,时常提交代码是个好习惯。后续文章我们还将使用到在这里编写的代码。本文源代码可以在这里查看与下载:https://github.com/kesalin/OpenGLES

四,Refference

OpenGL ES 2.0 for iPhone

OpenGL ES 2.0 Programming Guide

[OpenGL ES 01]OpenGL ES之初体验相关推荐

  1. 学习opengl官方指南 01 opengl介绍

    申明:翻译的不好,勿喷,轻喷. 本章的目标 1. 可以知道opengl一些常见的专有名词 2. 识别不同级别的渲染复杂度 3. 理解opengl的命令语法 4. 知道opengl管段渲染的系列操作 5 ...

  2. matlab 背包问题动态规划,从01背包问题理解动态规划---初体验

    这个问题有两种解法,动态规划和贪婪算法.本文仅涉及动态规划. 先不套用动态规划的具体定义,试着想,碰见这种题目,怎么解决? 首先想到的,一般是穷举法,一个一个地试,对于数目小的例子适用,如果容量增大, ...

  3. Java Web学习笔记01:动态网站初体验

    文章目录 一.课程概述 1.课程地位 2.课程目标 二.动态网站

  4. LVGL v8学习笔记 | 01 - LVGL PC模拟器初体验

    一.LVGL LVGL官网:https://lvgl.io/. LVGL全称Light and Versatile Graphics Library,轻量化和多功能的图形库,遵循MIT开源许可协议,具 ...

  5. [OpenGL ES 02]OpenGL ES渲染管线与着色器

    http://blog.csdn.net/kesalin/article/details/8223649 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循"署名 ...

  6. OpenGl文章 Android OpenGL ES 简明开发教程

    Android OpenGL ES 简明开发教程 分类:android学习笔记2011-12-14 15:04375人阅读评论(0)收藏举报 ApiDemos 的Graphics示例中含有OpenGL ...

  7. linux 拷机软件,拷机软件跑起来,OS X平台下OpenGL初体验

    拷机软件跑起来,OS X平台下OpenGL初体验 2012-11-12 18:55:03 作者:tina Tag:Mac 热度:745℃ 苹果自成一套的体系可以尽情展示自家的技术水准,但是也会给一些程 ...

  8. android opengl版本,Android OpenGL ES(一)开发入门

    早就听过大名鼎鼎的 OpenGL,却迟迟没有实践学习,有些惭愧.今天开始通过实践+博文方式学习掌握 OpenGL.此文对于 OpenGL 的学习分为以下部分: OpenGL 基础概念 OpenGL 坐 ...

  9. 【OpenGL ES】OpenGL ES简介

    [参考-khronos]https://www.khronos.org/opengles/ 1.简介 OpenGL ES(OpenGL for Embeded System)是OpenGL(Open ...

最新文章

  1. 网页静态化技术Freemarker
  2. r语言 断轴 画图_R语言基础画图/绘图/作图
  3. 网络安全导论课程-windows开启远程
  4. linux重新安装mysql步骤_Linux下MySQL安装及相关操作过程
  5. 这 30 个常用的 Maven 命令你必须熟悉!
  6. linux各机器之间配置无密码访问
  7. Spring3系列12-Spring AOP AspectJ
  8. 本人计划继续写飞鸽传书,支持的人有吗?
  9. 基于 Java Web 的毕业设计选题管理平台--选题报告与需求规格说明书
  10. java web传递参数_Javaweb的八种传值方式
  11. 利用TreeView实现C#工具箱效果
  12. python浪漫代码表白npy_【交大表白墙】表白dxy小姐姐,十里春风不如你,三里桃花不及卿,要每天开心哦!...
  13. Bat 下载文件并处理
  14. SAP PO750 Process Orchestration 安装及初始化(刘欣)
  15. 【游戏逆向】《**明月刀》BUFF及技能预判
  16. 电脑计算机窗口不见了怎么调出来,电脑桌面上的任务栏不见了怎么办
  17. 甄别客户需求,提高解决问题的效率
  18. scrum要素读书笔记
  19. C# 遇到 R6034 Runtime Error的解决办法
  20. Windows程序开机自启动

热门文章

  1. 相信这道奥数题,小时候难倒很多人,现在程序一秒告诉你答案
  2. 国家高新技术企业优惠政策重点及国家高新申报条件介绍,补贴20-50万
  3. python新年快乐代码_先祝福大家新年快乐 Python 大神们 帮忙看看是什么问题
  4. 利用云打码来破解登录遇到验证码的问题
  5. SQL Pass北京举办第四次线下活动,欢迎报名
  6. 高等数学精讲02 第一章第二节 极限01
  7. Press Ping Yilan contemporary culture the cultivation of imagination Writing
  8. 【百度地图api】前端 百度地图添加地理围栏
  9. 下载了HDTV版本的终结者2回顾~
  10. 【Python基础】parse_args()的使用