Qt 与 Objective-C 的混合编程

多年来有一点个利用Qt的恋人问起 Qt for iOS
的政工,因为本身在此下面的经历非常少,写不出系统的稿子来,特别抱歉,无法交到令人满足的应对,推荐大家去看 Jason’s
Home ,在本身博客左边边栏的友情链接里也可能有,他提供了
Qt for iOS 的片段足够有意义的篇章,何况是依靠实行的,他的 App 已经在 App
Store 中上线。

         [ jimmyzhouj 翻译]  Nehe iOS
OpenGL ES 2.0教程

关于自个儿啊,在这里篇小说里,简要介绍部分哪些混合 Qt 与 OC 编制程序。

  引子:          这几天要读书iOS
上的OpenGL ES的从头到尾的经过,在互联英特网找了某个学科来看。开掘存关OpenGL
ES2.0的教程相当少。想起了资深的Nehe OpenGL
教程,就上nehe.gamedev.net上找了找,开掘她们有叁个用于移动装备的OpenGL
ES教程,何况是依据OpenGL ES
2.0的。以后还独有两篇教程,小编先把这两篇翻译出来,以供大家学习。有啥样难点亟需交换的,能够联系小编:
[email protected]。
 
豁免权利申明:本课程翻译原稿均出自网络,仅供就学沟通之用,请勿进行买卖传播。相同的时候,转发时毫无移除本表明。如产生其余争辨,均与本博客全体人和发表该翻译稿之人无别的关联。感谢同盟!
    IOS Lesson 01 – 设置GL ES
原来的文章链接地址:
  大家好,现在开始大家新的iOS OpenGL ES系列教程!   前言      
 当大家最初那一个课程的时候,你该知情,笔者对Objective-C和iOS是叁个新手,所以固然有何样地点我弄错了,或许如什么地点方可以用越来越好的艺术来兑现,请报告本人。小编只是在与OS人机联作的时候才使用Objective-C,全体和OpenGL相关的代码都以用C++达成的。小编会尽量解释大家看看的兼具地方,但本人也假诺你已经有大旨的编制程序知识,并且知道面向对象编制程序的基本概念。
教程附带的代码能够在iOS4,iOS5上中国人民解放军海军事工业程高校业作,所以就到底在小米4S上也能正常运作。 我们希图接收XCode4,假若你还不曾安装的话,请到MAC
APPStore上下载并设置它。假若有人愿意可以在Windows或然Linux机器上支付摩托罗拉App,小编只好可惜的告知你无法这么做。
大家知晓,第一课看起来很焦灼,它恐怕是这一多样教程中最无聊的一课,到本节课结束也看不到什么非常酷的事物…
🙁
 可是,这节课包括了广大有价值的剧情,而且明白程序框架的不等部分之间的相互是相当重大的。所以您须要把它通读贰遍,你没有必要领会有所的原委,当您之后想要知道内部原因的话,能够返回来再细致看。遵照NeHe的的好习于旧贯,笔者会尽量详整每一行代码!       概要  
让大家先看看须求运用到的类。犹如自身事情未发生前说的,大家供给有个别Objective-C的类铁锈棕框)和局地C++的类紫灰框)。
 

自个儿要说的原委吧,大多数在 Qt 帮忙里皆有,请大家到目录格局下,键入”Qt for
iOS”,找到 Qt for iOS
那篇文章来看。它介绍了搭建开采遇到澳门新葡萄京官网首页 ,、编写翻译应用混合OC编程那三个方面,已经极度详尽了。

 

设若你不想啃România语,那可以接着作者的篇章往下看。

澳门新葡萄京官网首页 1

系列设置

既然如此要聊 Qt 混合 OC 编制程序,首先要简要介绍一下 Objective-C
。作者独有一句话:Go,问寻觅引擎去。因为笔者所知实在点儿,怕错误的指导了您。当然假设你不怕,往下看呢。

和装有的C/C++程序同样,应用程序的起源是main方法。我们施行UIApplicationMain,用InterfaceBuilder配置贰个带有了EAGLView的UIWindow,並且用Lesson01AppDelegate去管理全数的平地风波。Window是UIApplicationMain自动创制的,能够浮现大家的view。View包涵了大家的OpenGL
conext,能够经过那个context访谈到用OpenGL ES来作画的canvas。

Objective-C源文件介绍

第一作者要说一下 Objective-C 的源文件,后缀是.m 或 .mm ,在 .mm
文件里,能够一直动用 C++ 代码。所以,我们要混合 Qt 代码与
Objective-C 代码,就要求在 Qt 项目里步入 mm 文件。

小编们要钩到操作系统的run
loop中去,尽或许频仍的重绘frame。在之后关于动画的学科中这是必得的。油画动作是在Lesson对象中的draw方法中完结的,大概更确切的说,是在Lesson01对象中得以完结的,因为在类Lesson中,init)和draw)都以虚函数。
  轻松浏览  
我们开始一步一步完成。你能够从今未来处(
获取代码,然后展开项目文件Lesson01.xcodeproj。

pro 文件配置

Qt SDK for Mac ,安装之后, Qt Creator 会使用 XCode
提供的编写翻译工具链来编写翻译代码,能够准确编写翻译 mm 文件,也得以链接 iOS
的库文件。

而要混合 Objective-C 代码,须求改换一下 pro 文件。三个是加多 mm
文件,一个是连接针对 iOS 的库文件。

增多源文件,使用 OBJECTIVE_SOURCES 这些变量,比方那样子:

OBJECTIVE_SOURCES += ocview.mm

链接库 XCode 提供的库,则需求选拔 QMAKE_LFLAGS ,相仿那样子:

ios {
    QMAKE_LFLAGS    += -framework OpenGLES
    QMAKE_LFLAGS    += -framework GLKit
    QMAKE_LFLAGS    += -framework QuartzCore
    QMAKE_LFLAGS    += -framework CoreVideo
    QMAKE_LFLAGS    += -framework CoreAudio
    QMAKE_LFLAGS    += -framework CoreImage
    QMAKE_LFLAGS    += -framework CoreMedia
    QMAKE_LFLAGS    += -framework AVFoundation
    QMAKE_LFLAGS    += -framework AudioToolbox
    QMAKE_LFLAGS    += -framework CoreGraphics
    QMAKE_LFLAGS    += -framework UIKit
}

地点是本人使用 Qt 针对 iOS 编制程序的布局。作者利用了大多目的性 iOS
的库,所以增添了好些个 framework 。

“ -framework UIKit ”这种参数,是路过 Makefile 传递给 Clang
的参数,-framework
是用来提醒要链接有个别框架(大概说库)的第一字,它背后跟的是框架(库)名。

亟待留意的是,大家接收针对 iOS 的库,不是透过“ LIBS +=
”这种方法来引进哦。当然,你和煦通过 Qt 实现的 .a 库,依旧亟待运用“ LIBS
+= ”这种艺术。

         注意:你以往创立你本人的OpenGL ES 项目标时候,你能够动用project
wizard。生成的门类的构造有有个别错落有致,然而全体构成是一模二样的。Draw方法会在yourProjectNameViewController:drawFrame(卡塔尔(قطر‎里。我们的代码只是简单的将他们都免去了,而且将我们的OpenGL代码和window隔断开了罢了。

指定plist文件

一时你必要为您的连串内定 plist 文件, plist 文件全名是 Property List
,后缀是 .plist 。它用来定义 iOS 应用的质量,举个例子Bundle(iOS上的三个采纳被称作三个 Bundle )
的显得名字、可推行文件名字、签字、证书等等,当然也能够保留一些配置数据。具体的介绍参谋iOS 开拓的文书档案吧。

要在 pro 文件里增加 plist ,要动用 QMAKE_INFO_PLIST
关键字。如上边那标准:

QMAKE_INFO_PLIST += MultiWindow.plist

好啊,关于 pro 文件中与混合使用Objective-C
相关的构造项,概略就这几个了。接下来咱们看怎么写 Objective C 代码啦。

 
你会在品种侧面看见3个文本夹:Lesson01,Frameworks和Products。Lesson01还包罗了2个子目录Supporting
Files和Basecode,Lesson01包涵了大家富有的代码。Frameworks包涵了全体我们要用的要么项目要求采用的frameworks。从别的语言依旧操作系统转过来的开垦者可以叫它们是libraries。Products列出类全部要转移的applications,今后就唯有五个Lesson01.app。
在大家随后的学科的代码中,大家最主要和LessonXX
类打交道,每一回AppDelegate都会生成四个当下科目标实例。在率先课,大家先详细看一看Basecode和Supporting
Files。  
让大家遵照代码履行时的拜望顺序来拜谒这个文件。仿佛本身事前提出的一模二样,全数程序都以从main方法伊始施行的。它在Lesson01/Supporting
Files/main.m中。  

混合使用Objective C 代码

小珍宝,很害怕啊,这是自己的劣点,没写过多少
Objective-C 代码。所以,请不要问作者 OC 有关的难题,作者真不知道……

#import <UIKit/UIKit.h>   //standard main method, looks like that for every iOS app.. int main(int argc, char *argv[]) {     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];     NSLog(@"Running app");     int retVal = UIApplicationMain(argc, argv, nil, nil);     [pool release];     return retVal; } 

背景

本身的示范,是在 QML 的分界面上叠合iOS原生的分界面,即
UIView、UIWindow之类的。因为 OC 是 C 的近亲,和 C++
有着自然的血脉,混合起来非常方便哈,比 Android 上利用 JNI 编制程序好用多了。

不过有好几, OC 都适用 [] 这种语法来调用函数,使用 XCode
的话,语法提示和自动完毕成效特别苍劲,基本不用思考的就能够找到你要用的函数。而
Qt Creator 么,嘿嘿,就没这么好相与了,纯粹要手写啊。我立马都以开着
XCode 看 API 文书档案找的,相比痛楚。

那几个法子在每一种黑莓app里都差不离是千篇一律的。NSAutoreleasePool是Objective-C的废料搜集类别所急需的,我们打字与印刷一条log,然后是重大的一部分:将应用程序的参数传给UIApplicationMain方法,然后将调控权也转给它。当最后程序的调整权转移回去的时候,大家释放garbage
collection utility,用UIApplicationMain给出的重返值来终结程序。
如同它的名字雷同,UIApplicationMain方法运转叁个顾客分界面。在我们项目标设定中,选中Targets->Lesson01,
选Summary选项卡,MainInterface设置为MainWindow。那告诉App,程序运维的时候,大家要展现MainWindow.xib。那几个文件也在Supporting
Files目录下,能够在InterfaceBuilder中开荒。
张开MainWindow.xib,在编辑器的左边栏,你能够看出Placeholder和Objects。在Objects下,有Lesson01AppDelegate和一个内嵌了View的Window。借使您未有做过其它GUI编制程序,你能够认为那么些就好像您最赏识的Office软件(=window卡塔尔(قطر‎,张开了多个文书档案(=view卡塔尔国。以后您能够在这里些文书档案(=views,包涵了UI成分比方buttons,text
田野同志s恐怕OpenGL ES
canvas卡塔尔国中切换,而无需关闭全部程序。可是你须要有二个展开的文书档案(view卡塔尔(قطر‎本事观察当中的内容。

QQuickView 是什么

本人测量试验时的亲自去做,用的是 Qt Quick App 项目模板,使用 QQuickView 来加载 QML
文书档案。这里也以此为例来声明。

先是要说 QQuickView 到底是怎么。

QQuickView 呢,其实是二个 UIView 。UIView 则是 iOS
开拓框架里非常多界面成分的根儿,举例 UIWindow 便是 UIView 的子类。

Qt 的 QQuickView 是一个 UIView ,创制了 QQuickView 实例后,就有了一个UIView ,然后 Qt 玩了部分法力,得到了 UIView 的 OpenGL Context ,跑起了
Qt 的风浪循环,在此个 OpenGL Context 上从零开端绘制了温馨的景观和 UI
系统。

就这么轻松,你能够查看 Qt 源码来更是询问。

需求注意的是, QML 分界面成分的渲染,与 UIView
这种原生分界面包车型客车渲染,不在三个线程中。并且 iOS 对 OpenGL ES
的支撑很好,你能够而且使用多个 OpenGL Context
。更加好的是,你能够窗口情势来创立二个 OpenGL Context 。不像 Android
版本哦, Qt 使用 OpenGL 的时候都以全屏方式,局地更新不扶助,所以大家在
Android 上使用 QML 里的 Camera 和 VideoOutput 来支付拍照应用时,
VideoOutput 必需是全屏情势(必需fill_parent)。而在 iOS
上,则尚未那几个范围了。看来 iOS 依旧比非常美丽好的啊。

作者靠,扯得有一点点儿远,近日写技艺随笔少了,越来越罗嗦了。言归正传吧。

因为 QQuickView 实际上就是叁个 UIView ,所以能够强迫转变为 UIView
,然后接纳 OC 的法子来创制新的 UIView 或然 UIWindow ,那样就有了原生的
UI 组件了,你能够在这里个原生的 UI 组件上选用 OpenGL
绘制自个儿的事物照旧加多任何原生的控件,特别美好。

而是要表达的是,通过这种方式创制出来的 iOS 原生分界面成分,会始终在 QML
界面之上,把 QML 场景里的分界面成分给盖住。

当你control-click(只怕右键卡塔尔Lesson01AppDelegate的时候,你会映重视帘2个概念好的outlets:glView和window,它们各自涉嫌到InterfaceBuilder中的View和Window。三个Outlet能够让app
delegate代码中的叁个变量包涵了它所波及的InterfaceBuilder凉月素的援引。

混合代码

要选取 OC 的类库,须求在 mm
文件内蕴涵相关的头文件,又有几有个别做事要做。二个是在 pro 文件里到场 SDK
路径,使用 INCLUDEPATH 变量就可以,非常少说了。其它一些是在 mm 文件内蕴含Objective-C 的头文件,与 C++
头文件二个理儿,可是要动用 #import 哦。近似酱紫:

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

包涵了头文件,就足以应用 Objective-C 类库了。比方自个儿要在 QQuickView
上边创制四个新的 iOS 原生的 UIView ,.mm 文件里能够这么:

void addOCView(QQuickWindow *w)
{
    UIView *view = reinterpret_cast<UIView *>(w->winId());

    CGRect  viewRect = CGRectMake(10, 10, 100, 100);
    UIView* myView = [[UIView alloc] initWithFrame:viewRect];
    [myView setBackgroundColor:[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0]];
    [view addSubview: myView];
}

如你所见,作者写了多少个 addOCView 方法,它的参数是 QQuickView 。在
addOCView 方法里,作者把 QQuickView 逼迫转变为 UIView 来使用。

作者创立了一个新的 UIView ,设置了它的背景颜色,然后把它助长为 QQuickView
的子窗口。诺,就那样轻便了。

说下 main.cpp ,看它什么利用 addOCView(卡塔尔国 方法。代码如下:

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView viewer;
    viewer.setResizeMode(QQuickView::SizeRootObjectToView);
    viewer.setSource(QUrl("qrc:/main.qml"));
    viewer.show();

    addOCView(&viewer);

    return app.exec();
}

少数都不惊奇是吗,正是从来调用了 addOCView 哦。哈哈,确实那样了。

  Lesson01AppDelegate  
大家来看率先个关键的代码文件:Lesson01AppDelegate.h

iOS 原生分界面与 QML 成分的职位映射

掺杂使用 iOS 原生分界面时,也能够完毕原生分界面与 QML
分界面包车型客车无缝集成。关键就在于计算 QML 分界面成分的职位,然后依照 QML
分界面元素之处来安装原生分界面包车型大巴岗位。

#import <UIKit/UIKit.h>   //we want to create variables of these classes, but don't need their implementation yet, //so we just tell the compiler that they exist - called forward declaration @class EAGLView; class Lesson;   //This is our delegate class. It handles all messages from the device's operating system @interface Lesson01AppDelegate : NSObject <UIApplicationDelegate> { @private     //we store a pointer to our lesson so we can delete it at program shutdown     Lesson *lesson; }   //we configure these variables in the interface builder (IB), thus they have to be declared as IBOutlet //properties get accessor methods by synthesizing them in the source file (.mm)   //in this window we will embed a view which acts as OpenGL context @property (nonatomic, retain) IBOutlet UIWindow *window;   //our main window, covering the whole screen @property (nonatomic, retain) IBOutlet EAGLView *glView;   @end 

QML成分地方换算

QML 提供了换算成分地方的艺术,Item 有个艺术,叫作 mapToItem(State of Qatar,它能够把二个针锋相投于 Qt Quick Item 的区域映射到另一个 Item
上,获得坐标了。例如你的 qml 文件是底下的旗帜:

Rectangle {

    ...

    Rectangle {
        id: videoLayer;
        anchors.margins: 8;
        anchors.left: parent.left;
        anchors.right: parent.right;
        anchors.top: parent.top;
        anchors.bottom: actionBar.top;
        color: "green";
    }

    ...

}

您想把原生的 UIView 定位到 id 为 videoLayer
的要素内部,能够接近上边换算一个坐标并利用它:

var coordinate = videoLayer.mapToItem(null, 8, 8, videoLayer.width - 16, videoLayer.height - 16);
winUtil.addUIView(coordinate.x, coordinate.y, coordinate.width, coordinate.height);

换算出的coordinate有 x 、 y 、 width 、 height 属性。当 mapToItem
的首先个参数为 null 时,换算的结果正是争持于 QQuickView 的。这我们在增加UIView 时,就能够用那一个换算结果来布局三个 CGRect 对象,用这一个 CGRect
来发轫化 UIView 之处。

在Objective-C风格的类定义中,我们先是在头文件宣称了interface,之后在相应的源文件(.mm卡塔尔中应用implementation完成情势。从地方的代码中仍可以够看来,有部分分子变量,window,
glView,
lesson,这几个变量会在AppDelegate领头化的时候被创建。要小心的是,properties定义为IBOutlet后,能力够被InterfaceBuilder使用。由于要拍卖窗口的平地风波,当程序早先运营的时候,会自动创造两个AppDelegate的对象实例。为了能管理窗口事件,AppDelegate完成了接口(大概叫protocol卡塔尔UIApplicationDelegate。Protocol增多在类名的概念后,用<>符号包起来。
 
窗口成立后,触发的第八个事件是didFinishLauchingWithOptions。代码如下,在Lesson01AppDelegate.mm中。
 

设置 UIView 的位置

日前示例代码中的 winUtil 是作者在 C++ 内完成的叁个帮忙类,导出到了 QML
情形中。它的 addUIView 方法依据传入的坐标来设置原生 UIView
的岗位。参谋代码如下:

UIView *v = reinterpret_cast<UIView*>(view->winId());
    uiw = [[UIWindow alloc] initWithFrame:CGRectMake(x, y, width, height)];
    [v addSubview: uiw];

地点代码中,view 是 QQuickView 。此次大家成立 UIView 时接纳了流传的 x 、
y 、 width 、 height,那样新建的 UIView 就和 QML
成分整合在联合了,看起来好疑似一体的。

OK,这就是全方位了。

#import "Lesson01AppDelegate.h"   #import "EAGLView.h" #include "Lesson01.h"   //now we implement all methods needed by our delegate @implementation Lesson01AppDelegate   // @synthesize window; @synthesize glView;   //this method tells us, that our application has started and we can set up our OpenGL things, //as the window is set up, and thus our glView is going to be displayed soon - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {     //for the first lesson we don't need a depth buffer as we're not drawing any geometry yet     [glView setDepthBufferNeeded:FALSE];           //we create our lesson which contains the OpenGL code     //(allocated with new -> has to be cleaned up with delete!)     lesson = new Lesson01();           //we tell our OpenGL view which lesson we want to use for rendering.     [glView setLesson:lesson];           return YES; } 

首先,大家配备了glView,然后创立了四个lesson对象的实例。要小心的是,大家定义的成员变量lesson是指向Lesson对象的指针,可是由于Lesson01是Lesson的子类,所以它们提供了相像的接口,能够这么使用。
还要小心,代码中synthesize了头文件定义的七个property,那会自动生产getter和setter方法。
   
Lesson01AppDelegate.mm中的其他代码部分管理了窗口触发的其它的风云,那一个事件都和采取变为可以预知,或许成为后台程序有关。当变为可以知道时,我们让view周期性的幼功代谢。当使用被移到后台只怕关闭后,停止刷新。最终,当AppDelegate被释放时,它的dealloc方法被调用,大家在此个主意里自由以前分配的内部存款和储蓄器。在Objective-C里用release释放,在
C++里,用new来成立,用delete来删除。

- (void)applicationWillResignActive:(UIApplication *)application {     //the app is going to be suspended to the background,     //so we should stop our render loop     [glView stopRenderLoop]; }   - (void)applicationDidEnterBackground:(UIApplication *)application {     //we could do something here when the application entered the background }   - (void)applicationWillEnterForeground:(UIApplication *)application {     //we could start preparing stuff for becoming active again }   - (void)applicationDidBecomeActive:(UIApplication *)application {     //we're on stage! so let's draw some nice stuff     [glView startRenderLoop]; }   - (void)applicationWillTerminate:(UIApplication *)application {     //before shutdown we stop the render loop     [glView stopRenderLoop]; }   //dealloc is the destructor in Objective-C, so clean up all allocated things - (void)dealloc {     [window release];     [glView release];     delete lesson;     [super dealloc]; }   @end 

  未来让大家来看包括OpenGL context 的创始和担当壁画的类。   EAGLView  
正如大家所知,当运转UIApplicationMain时,会自行创设window和delegate。因为我们在delegate中定义了相应的outlet,当Window被创制时,它明白要求一个EAGLView作为Window所包罗的View。所以Window会自动成立三个view来突显。大家先看EAGLView.h

//forward declarations again @class EAGLContext; class Lesson;   // This class combines our OpenGL context (which is our access to all drawing functionality) // with a UIView that can be displayed on the iOS device. It handles the creation and presentation // of our drawing surface, as well as handling the render loop which allows for seamless animations. @interface EAGLView : UIView { @private     // The pixel dimensions of the CAEAGLLayer.     GLint framebufferWidth;     GLint framebufferHeight;           // These are the buffers we render to: the colorRenderbuffer will contain the color that we will     // finaly see on the screen, the depth renderbuffer has to be used if we want to make sure, that     // we always see only the closest object and not just the one that has been drawn most recently.     // The framebuffer is a collection of buffers to use together while rendering, here it is either     // just the color buffer, or color and depth renderbuffer.     GLuint defaultFramebuffer, colorRenderbuffer, depthRenderbuffer;           // The display link is used to create a render loop     CADisplayLink* displayLink;           // Do we need a depth buffer     BOOL useDepthBuffer;           // The pointer to the lesson which we're rendering     Lesson* lesson;           // Did we already initialize our lesson?     BOOL lessonIsInitialized; }   // The OpenGL context as a property (has autogenerated getter and setter) @property (nonatomic, retain) EAGLContext *context;   // Configuration setters - (void) setDepthBufferNeeded:(BOOL)needed; - (void) setLesson:(Lesson*)newLesson;   //if we want OpenGL to repaint with the screens refresh rate, we use this render loop - (void) startRenderLoop; - (void) stopRenderLoop; @end 

 
类EAGLView来源于UIView。作为UIView的子类,大家得以重写(overwriteState of Qatar一些主意(在Objective-C中叫selector卡塔尔(قطر‎,下边大家会在源文件中见到。

     表明:并非挟持必须用EAGLView这几个名字,但在iOS GL
ES程序中听而不闻都用它,因为context被称为EAGLContext。EAGL恐怕意味着”Embedded
AGL”,AGL是APPLE的OPENGL扩大。

正如我辈所见,EAGLView封装了OpenGL ES context。OpenGL
context被以为是允许采用OpenGL调用来作画的认同。它追踪记录了作者们设定的富有意况,举个例子说当前的颜色,当前哪一幅图片被用来做纹理。Context要和canvas大家能够描绘的地点)合营起来使用。Canvas通过一种叫framebuffer的布局来贯彻。framebuffer由存款和储蓄分化音信的多层buffer组成。咱们常用到的多少个层是color
renderbuffer和depth renderbuffer。Color
renderbuffer里面存款和储蓄了各种像素点的每种color
channel,如同JPEG图像同样。那是最终显示在显示屏上的从头到尾的经过。The depth
renderbuffer记录了color
buffer中的各样像素点离荧屏的离开。如若大家画了一座间隔显示屏10单位远的屋子和三个偏离显示屏5单位远的人,则无论先画的是屋企如故人,人三回九转呈现在房屋的前边。那被称呼是深度测验。Depth
buffer内容不会被出示。

EAGLView类的大超多分子变量如下:咱们存款和储蓄了framebuffer的小幅和惊人(颜色缓存和纵深缓存尺寸也和那等同State of Qatar,大家还保留的依次缓存的ID,用来作为在OpenGl中应用时的名字。
 
上面是CADisplayLink成员,它同意我们进去系统的主循环,并伏乞按一秒约五18遍的频率重绘。我们还应该有一个开关用于启用/禁用深度缓存。因为缓存很花销展现微芯片的可贵内部存款和储蓄器,要是无需深度的话,大家应当剥夺深度缓存。
大家还须求二个指南针指向我们的lesson对象,那样就足以调用draw(卡塔尔(قطر‎方法了。还索要三个表明来代表是不是已经初叶化了那么些lesson对象。
以前一向在说的context被保留为EAGLContext  property.
还大概有4个方法是AppDelegate要动用的,那4个办法的名字早已很好的疏解了它们的效应。
     上面大家先看看EAGLView.mm中伊始化的一部分。

#import <QuartzCore/QuartzCore.h>   #import "EAGLView.h" #include "Lesson.h"   //declare private methods, so they can be used everywhere in this file @interface EAGLView (PrivateMethods) - (void)createFramebuffer; - (void)deleteFramebuffer; @end     //start the actual implementation of our view here @implementation EAGLView   //generate getter and setter for the context @synthesize context;   // We have to implement this method + (Class)layerClass {     return [CAEAGLLayer class]; } 

首先大家给类扩张了几个村办方法的宣示。就算不那样做的话,大家就必得把那多少个办法的兑今世码放在调用它们的代码的日前。为了保险头文件的简短,大家不把证明放到头文件中,可是大家将来如此做也绝非当真扩大源码的可读性。

接下去大家开头完成EAGLView,合成(synthesize卡塔尔国context以自动生成getter和setter方法。然后须要重写UIView的layerClass方法,因为今后大家用的view不是明媒正娶的UI元素,而是要画到多少个CAEAGL层上(CA指
CoreAnimationState of Qatar。

//our EAGLView is the view in our MainWindow which will be automatically loaded to be displayed. //when the EAGLView gets loaded, it will be initialized by calling this method. - (id)initWithCoder:(NSCoder*)coder {     //call the init method of our parent view     self = [super initWithCoder:coder];           //now we create the core animation EAGL layer     if (!self) {         return nil;     }       CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;           //we don't want a transparent surface     eaglLayer.opaque = TRUE;           //here we configure the properties of our canvas, most important is the color depth RGBA8 !     eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:                                     [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,                                     kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,                                     nil];           //create an OpenGL ES 2 context     context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];           //if this failed or we cannot set the context for some reason, quit     if (!context || ![EAGLContext setCurrentContext:context]) {         NSLog(@"Could not create context!");         [self release];         return nil;     }       //do we want to use a depth buffer?     //for 3D applications we usually do, so we'll set it to true by default     useDepthBuffer = FALSE;           //we did not initialize our lesson yet:     lessonIsInitialized = FALSE;           //default values for our OpenGL buffers     defaultFramebuffer = 0;     colorRenderbuffer = 0;     depthRenderbuffer = 0;                 return self; } 

当view初步化的时候,会奉行initWithCoder方法。当时大家就从头设置context了。
首先大家初阶化父类UIView,检查是不是一切平常。然后大家从View的层中开创CAEAGLLayer並且将它设为可壁画的(不要问笔者那有个别的内情:State of Qatar下一步是创建大家的OpenGL context,我们须求它是本子2的从HUAWEI 3S和iPod
Touch3起支撑),通过调用initWithAPI:kEAGLRenderingAPIOpenGLES2来落成。假若创设context成功,何况也足以将context设为当前context,我们接下去将成员变量设为缺省值。
还记得前边关于framebuffer的段落么?大家来成立canvas作为上面全部科目实行写生职业的根底。

//on iOS, all rendering goes into a renderbuffer, //which is then copied to the window by "presenting" it. //here we create it! - (void)createFramebuffer {     //this method assumes, that the context is valid and current, and that the default framebuffer has not been created yet!     //this works, because as soon as we call glGenFramebuffers the value will be > 0     assert(defaultFramebuffer == 0);           NSLog(@"EAGLView: creating Framebuffer");           // Create default framebuffer object and bind it     glGenFramebuffers(1, &defaultFramebuffer);     glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);           // Create color render buffer     glGenRenderbuffers(1, &colorRenderbuffer);     glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); 

率先检查是不是还尚无framebuffer。就算还一贯不,调用OpenGL的措施来发出三个framebuffer对应的ID,那样发生的ID能够保障是独一的,并且ID总是大于0。获得ID后,大家将framebuffer和ID绑定。OpenGL会追踪活动对象的好多业务,比方移动的framebuffer,最终设定的颜色,活动的纹理或许活动着色器程序(shader
program卡塔尔等等。那招致下边全体有关framebuffer的API都影响当下绑定的framebuffer。
下直面color renderbuffer做相仿的劳作,产生三个ID并且绑定。  

//get the storage from iOS so it can be displayed in the view     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];     //get the frame's width and height     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);           //attach this color buffer to our framebuffer     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer); 

那些color
renderbuffer是异样的。我们目的在于大家描绘的颜色能够被用于UIView的颜料,那正是干什么以前我们要成立CAEAGLLayer。今后咱们要求得到layer,将它用来renderbuffer存款和储蓄。那样做,大家不须要再叁次拷贝缓存中的内容就足以将安装的颜色展现在UIView上。
那要经过调用context的
renderbufferStorage方法来兑现。函数完结的很精细,能够自行获取四个和view的尺码相适应的buffer。再上边两行用来询问framebuffer的上涨的幅度和高度。

glFramebufferRenderbuffer是可怜重要的。贰个framebuffer由八个layer组成,那么些layer被叫做attachments。这里大家告知OpenGL,当前绑定的framebuffer要增大学一年级个名为colorrenderbuffer的color
buffer。GL_COLOR_ATTACHMENT_0表示一个framebuffer能够有多少个color
attachments,但是这几个剧情超越了本节课程的界定了。

//our lesson needs to know the size of the renderbuffer so it can work with the right aspect ratio if(lesson != NULL) {     lesson->setRenderbufferSize(framebufferWidth, framebufferHeight); } 

笔者们刚刚精晓了实在的渲染窗口的分寸,必要将以此参数字传送递给lesson对象,那样它就足以渲染全屏的尺寸了。
 

if(useDepthBuffer)     {         //create a depth renderbuffer         glGenRenderbuffers(1, &depthRenderbuffer);         glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);         //create the storage for the buffer, optimized for depth values, same size as the colorRenderbuffer         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferWidth, framebufferHeight);         //attach the depth buffer to our framebuffer         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);     } 

当要求深度缓存的时候,大家可以像以前颜色缓存那样达成。这一回索要通过调用glRenderbufferStorage方法自个儿创制存款和储蓄,供给提供部分参数,如存款和储蓄何种类型的多寡(DEPTH_COMPOMENT16是各样像素十六个人卡塔尔(قطر‎和buffer须求多大。
 

//check that our configuration of the framebuffer is valid     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)         NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); } 

末段大家需要承认framebuffer已经思虑好了。因为有很各个或者会出错的情事,所以最棒对每一个framebuffer对象(短FBOState of Qatar都实践检查。
   

//deleting the framebuffer and all the buffers it contains - (void)deleteFramebuffer {     //we need a valid and current context to access any OpenGL methods     if (context) {         [EAGLContext setCurrentContext:context];                   //if the default framebuffer has been set, delete it.         if (defaultFramebuffer) {             glDeleteFramebuffers(1, &defaultFramebuffer);             defaultFramebuffer = 0;         }                   //same for the renderbuffers, if they are set, delete them         if (colorRenderbuffer) {             glDeleteRenderbuffers(1, &colorRenderbuffer);             colorRenderbuffer = 0;         }                   if (depthRenderbuffer) {             glDeleteRenderbuffers(1, &depthRenderbuffer);             depthRenderbuffer = 0;         }     } } 

在有些时间点,我们创制的保有东西都急需被去除掉,FBOs和renderbuffers也不例外。大家有一个有效的且是时下的context的话,就能够动用OpenGL函数了。通过调用glDeleteFramebuffers和glDeleteRenderbuffers来删除。
       

//this is where all the magic happens! - (void)drawFrame {     //we need a context for rendering     if (context != nil)     {         //make it the current context for rendering         [EAGLContext setCurrentContext:context];                   //if our framebuffers have not been created yet, do that now!         if (!defaultFramebuffer)             [self createFramebuffer];           glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);                   //we need a lesson to be able to render something         if(lesson != nil)         {             //check whether we have to initialize the lesson             if(lessonIsInitialized == FALSE)             {                 lesson->init();                 lessonIsInitialized = TRUE;             }                           //perform the actual drawing!             lesson->draw();         }                   //finally, get the color buffer we rendered to, and pass it to iOS         //so it can display our awesome results!         glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);         [context presentRenderbuffer:GL_RENDERBUFFER];     }     else         NSLog(@"Context not set!"); } 

当今是100%app中最主要的部分,drawFrame方法。当大家的display
link触发,每一趟frame被渲染的时候,都会调用这些主意。首先大家要保障有三个context,framebuffer已经创办何况绑定了,lesson对象已经创设而且开首化了。即使前边几条都落到实处了,大家调用lesson->draw(State of Qatar方法,那个点子是前边的学科着力聚集的地点。最终几行很有趣。调用完lesson->draw(State of Qatar后,renderbuffers已经有必要渲染的源委了。为了告知系统有新内容要来得,供给绑定color
buffer并且供给context将它展现出来,调用的办法是[context
presentRenderbuffer:GL_RENDERBUFFE陆风X8。 大家恰巧提到了display
link。还记得大家从AppDelegate中调用startRenderLoop和stopRenderLoop,使伏贴使用在移动时,能够周期性的功底代谢。
 

//our render loop just tells the iOS device that we want to keep refreshing our view all the time - (void)startRenderLoop {     //check whether the loop is already running     if(displayLink == nil)     {         //the display link specifies what to do when the screen has to be redrawn,         //here we use the selector (method) drawFrame         displayLink = [self.window.screen displayLinkWithTarget:self selector:@selector(drawFrame)];                   //by adding the display link to the run loop our draw method will be called 60 times per second         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];         NSLog(@"Starting Render Loop");     } } 

为了开始周期性的换代荧屏,当分明未有安装好displayLink时,创立三个显示器的CADisplayLink,况兼告诉它重绘显示器的时候它须求做什么样。大家将target设为self,选取selector
drawFrame传递给艺术displayLinkWithTarget。为了博取大致每秒五十八次左右的刷新率,须要将displayLink增多到系统的runloop里去。代码的下一行完结那些职能,大家调用NSRunLoop
的静态方法currentRunloop来的到系统的runLoop,然后将它传递给displayLink的addToRunLoop方法。
将来大家得以渲染了!不过,当app步入后台的时候,怎么着甘休渲染呢?

//we have to be able to stop the render loop - (void)stopRenderLoop {     if (displayLink != nil) {         //if the display link is present, we invalidate it (so the loop stops)         [displayLink invalidate];         displayLink = nil;         NSLog(@"Stopping Render Loop");     } } 

为了休息displayLink,大家只须求调用方法invalidate就能够。它自动试行清管事人业。方法实行落成后,指针指向的是未开首化的内部存款和储蓄器。大家必要把指针指向nil,千万不要让指针成为野指针!
   
我们大多将EAGLView深入分析实现了,只剩下七个简易的setter方法,一个析构函数(在Objective-C中叫dealloc方法卡塔尔,和三个窗口事件的回调函数。

//setter methods, should be straightforward - (void) setDepthBufferNeeded:(BOOL)needed {     useDepthBuffer = needed; }   - (void) setLesson:(Lesson*)newLesson {     lesson = newLesson;     //if we set a new lesson, it is not yet initialized!     lessonIsInitialized = FALSE; }   //As soon as the view is resized or new subviews are added, this method is called, //apparently the framebuffers are invalid in this case so we delete them //and have them recreated the next time we draw to them - (void)layoutSubviews {     [self deleteFramebuffer]; }   //cleanup our view - (void)dealloc {     [self deleteFramebuffer];        [context release];     [super dealloc]; } 

八个setter方法超轻便精通。当UIView发生变化时,比如UIView的尺码变了,大概有新的丰硕走入了新的subview,回调函数layoutSubviews就能够被触发。在回调函数里,大家只供给删除framebuffer就足以了,因为drawFrame方法中,借使在渲染时意识framebuffer不真实的话,会变动八个新的framebuffer。

Dealloc方法祛除我们创设的整个,即framebuffers和context。大家调用deleteFramebuffer来排除framebuffer。我们用release方法来刑满释放解除劳教context,因为它是通过garbage
collection来保管的。最终我们调用父类的dealloc方法,在那父类是UIView。在Objective-C里必要手动调用父类的dealloc方法,而在C++里父类的析构函数会被机关调用。

    Lesson  
大家算是研讨到了将要这里后的课程中起到重大职能的类了。Lesson负担绘制每一个轻巧易行frame,它还开端化一些OpenGL的事物,举个例子加载图片,加载shaders,也许向显示集成电路传送几何数据。看Basecode/Lesson.h

#include <OpenGLES/ES2/gl.h> #include <OpenGLES/ES2/glext.h>   //this is our general lesson class, providing the two most important methods init and draw //which will be invoked by our EAGLView class Lesson { public:     //constructor     Lesson();     //the destructor has always to virtual!     virtual ~Lesson();           //abstract methods init and draw have to be defined in derived classes     virtual void init() = 0;     virtual void draw() = 0;           //we need to know the size of our drawing canvas (called renderbuffer here),     //so this method just saves the parameters in the member variables     virtual void setRenderbufferSize(unsigned int width, unsigned int height);       //all protected stuff will be visible within derived classes, but from nowhere else    protected:     //fields for the renderbuffer size     unsigned int m_renderbufferWidth, m_renderbufferHeight; }; 

前两行代码包蕴了OpenGL头文件,那一个头文件定义了颇负的API。然后定义了类,它的接口很简短。它有一个布局函数和二个虚析构函数。在C++里,全数在派生类中要重写的函数,必须在父类和派生类中都定义为虚函数。记住,结构函数无法是虚函数,可是析构函数一定是虚函数。
init方法和draw方法定义了类接口的主题作用。因为Lesson只是二个通用接口,大家不期待在此边达成那四个主意,而是计划放在Lesson的派生类(比如Lesson01卡塔尔国中落成。那就是为啥那四个函数不可是虚函数,还被设为0的来由。在C++中这样做暗暗表示了这一个类是抽象类。抽象类是不可能实例化的,那样就防止了未曾兑现的函数被私下调用。
还大概有五个protected的成员变量,用于记录renderbuffer的尺码。protected指的是它们只在在当前类依然其派生类中可以知道,别的地点则不可知。大家在EAGLView中的createFramebuffer中调用了setRenderbufferSize方法将参数字传送给lesson,在那大家收起输入的参数。
   
Basecode/Lesson.mm的落成是相当简单的。结构函数只是将renderbuffer的尺码设为0,析构函数什么清理也不用做,因为在这里边大家尚无分配任何存款和储蓄空间。

#include "Lesson.h"   //Lesson constructor, set default values Lesson::Lesson():     m_renderbufferWidth(0),     m_renderbufferHeight(0) {       }   //Lesson destructor Lesson::~Lesson() {     //cleanup here }   //save the renderbuffer size in the member variables void Lesson::setRenderbufferSize(unsigned int width, unsigned int height) {     m_renderbufferWidth = width;     m_renderbufferHeight = height;           glViewport(0, 0, m_renderbufferWidth, m_renderbufferHeight); } 

在setRenderbuffSize函数里有了贰个风趣的OpenGL函数调用。在把宽度和高度保存到成员变量后,大家调用了glViewport(int
left, int bottom, int width, int
height卡塔尔。通过这一个函数,大家告诉OpenGL大家希图绘制到显示屏的哪部分。通过把起先点设为左下角,使用一切的幅度和惊人,大家指明了须求用到全部荧屏。
        Lesson01  
未来我们已经精晓了应用程序的每一小部分,除了实际绘图的OpenGL部分。今后该起来了然了,笔者希望从现在起能够有相当少的代码和超多的分解和图表:卡塔尔

//We derive our current lesson class from the general lesson class class Lesson01 : public Lesson { public:     //overwrite all important methods     Lesson01();     virtual ~Lesson01();           virtual void init();     virtual void draw(); }; 

每一课我们都会派生出多个新的LessonXX的类,稳步步入更是多的OpenGl性格。那节课的Lesson.h极度轻松,大家只是达成了在超类Lesson中定义的那么些抽象方法。
那节课大家早先最简便易行的操作:清除荧屏。代码在Lesson01.mm中。大家首先定义布局函数和析构函数,这多个函数什么也不做。

#include "Lesson01.h"   //lesson constructor Lesson01::Lesson01() {     //initialize values }   //lesson destructor Lesson01::~Lesson01() {     //do cleanup } 

 
当大家开首化lesson对象的时候,设定想要使用的颜色来覆盖原有的其余颜色。在OpenGL中,颜色被钦命为红,绿,蓝颜色通道大家熟谙的奥迪Q7GB颜色)的强度(intensity卡塔尔(قطر‎。强度值是一个介于0(无强度卡塔尔和1(最大强度卡塔尔之间的浮点数。一时候(举个例子在JPEG图像中卡塔尔(قطر‎强度值是介于0和255里面包车型大巴。上边增添的颜色模型允许大家描述Computer显示屏能够展现的具备颜色。

 

澳门新葡萄京官网首页 2

(图片来源于: )

 
大家将免去颜色设为浅深绿。那表示将革命通道设为最大强度,淡蓝和乌紫通道设为0强度。

//initializing all OpenGL related things void Lesson01::init() {     NSLog(@"Init..");           //set the color we use for clearing our colorRenderbuffer to red     glClearColor(1.0, 0.0, 0.0, 1.0); } 

来看没,要传递4个参数给glClearColor?最终二个参数指明了阿尔法值(奥德赛GBA颜色卡塔尔,阿尔法值定义了opacity,这几个值在绝大多数面上都设为1。阿尔法值可用以每叁个像素点混合当前的颜料和新的颜色(称为blendingState of Qatar,因为在那间大家将各类像素的值设为福特ExplorerGBA-tupel,不行使
blending,所以实际上阿尔法值是不值得一提的。
上边大家告诉OpenGL大家盼望扫除color
buffer。那能够用glClear(GL_COLOR_BUFFER_BITState of Qatar命令来兑现。因为我们在每一帧的初阶都要用这么些命令,所以把它内置draw(卡塔尔(قطر‎方法中。

//drawing a frame void Lesson01::draw() {     //clear the color buffer     glClear(GL_COLOR_BUFFER_BIT);           //everything should be red now! yay :) } 

恭喜你!你已经成功了您在iOS上的率先个OpenGL程序了。试试其他颜色,确信你已经了然了冠道GB颜色模型,下一回大家就真正要从头画画了。
  敬请关心! Carsten      

jimmyzhouj 翻译] Nehe iOS OpenGL ES 2.0教程
引子 : 近些日子要学习iOS 上的OpenGL
ES的剧情,在网络络找了有个别课程来看。开采成关OpenGL
ES2.0的教程…

发表评论

电子邮件地址不会被公开。 必填项已用*标注