2.3、OpenGLES_Ch2_1示例

你可以从网址http://peogles.cosmicthump.com/learning opengl-es-sample-code/上下载关于本书的示例代码。Xcode工程以及所有创建本书的示例所需要的文件都包含在里面。一个叫做OpenGLES_Ch2_1.xcodeproj 的文件保存了关于工程自身的信息。在电脑上正确地安装了苹果的iOs5软件开发工具包和Xcode之后,双击OpenGLES_Ch2_1.xcodeproj文件来开启Xcode并加载工程。在加载完成后,点击Xcode工具栏的Run按钮来编译并连接工程中的文件,然后会启动苹果的iPhone模拟器来运行OpenGLES_Ch2_1 应用。


注意 本书的所有例子都是用苹果的ARC技术来为Objective-C对象管理内存的。在新 创建的iOS Xcode工程中会默认开启ARC。使用ARC避免了手动为对象管理内 存并简化了例子代码。


图2-6列出了例子中构建和连接的文件。本节的其余 部分介绍了每个文件的内容和目的。OpenGLES Ch2 1 工程是使用Xcode的标准单视图应用( Single View Application)模板创建的。这个模板设置新工程以创建一个由一个单独的填满整个屏幕的UIView(或者其子类)实例组成的简单应用。其他模板为其他类型的iOs应用提供了-一个起始点。单视图应用模板生成了--个命名为OpenGLES_ Ch2_1 AppDelegate的自定义应用委托类,一个命名为OpenGLES_Ch2_1ViewController 的自定义视图控制器类。

图 2-6

2.3.1、OpenGLES_Ch2_1AppDelegate类

OpenGLES_Ch2_1AppDelegate.h 文件是在工程刚创 建时由Xcode自动生成的。OpenGLES_Ch2_1 AppDelegate.h文件包含对OpenGLES_Ch2_1AppDelegate 类的Objective-C 声明。OpenGLES_Ch2_1例子可以不用做任何修改使用生成的类。

OpenGLESCh2_1AppDelegate.m文件也是在工程刚创建时由Xcode自动生成的。OpenGLES Ch2 1AppDelegate.m 文件包含对OpenGLES Ch2_ lAppDelegate 类的Objective-C实现代码。生成的代码包含多个-一般由应用的委托实现的方法的存根实现。OpenGLES_Ch2_1例子可以不用做任何修改使用生成的类。

2.3.2 、Storyboards

MainStoryboard iPhone.storyboard 和MainStoryboard iPad.storyboard 文件也是由Xcode 的单视图应用模板生成的。在Xcode中可以可视化地编辑这些文件以设定用户界面。iPad与iPhone的屏幕大小不同,因此会有不同的用户界面。当例子运行时,Open-GLES_Ch2_1 示例会自动读取与当前设备相适合的Storyboard。Storyboards 会把UIViewController实例以及与其相关联的UIView实例联系起来。Storyboards会指定视图控制器之间的过渡,自动化应用的大部分用户交互设计,潜在地消除了原本需要编写的代码。但是,OpenGLES_Ch2_1这个例子是如此简单以至于它只使用了一个视图控制器一OpenGLES_Ch2_1ViewController 类的一个实例。

2.3.3、OpenGLES_Ch2_1ViewController类的interface

OpenGLES Ch2 1ViewController.h 文件原本是在工程刚创建的时候由Xcode生成的,但是在这个例子中做了一-些修改。OpenGLES Ch2 1ViewController.h 包含修改过的用于OpenGLESCh2 1 ViewController类的Objective-C声明。粗体的代码是对所生成代码的主要修改。

// OpenGLES_ Ch2_ 1ViewController.h
// OpenGLES_ Ch2_ 1

#import <GLKit/GLKit.h>
@interface OpenGLES_Ch2_1ViewController : GLKViewController
(
GLuint vertexBufferID;
)
@property (strong, nonatomic) GLKBaseBffect *baseBffect;
@end

文件以一段注释开始。Cocoa Touch GLKit框架接口是通过#import编译指令导人的,这与ANSI C的#include指令类似。两个指令都把特定文件的内容插人到包含这些指令的编译文件中。当相同的文件内容被导人到每个编译文件的次数超过一次时,Objective-C的#import指令就会自动将其阻止,这里首选#import,虽然Objective-C也 支持#include。

OpenGLES_Ch2_1ViewController 是GLKViewController类的个子类并从GLKViewController 继承了很多基本功能,GLKViewController又从它的超类UIViewController继承了很多功能。特别地,GLKViewController 会自动地重新设置OpenGL ES和应用的GLKView实例以响应设备方向的变化并可视化过渡效果,例如淡出和淡人。

在OpenGLES_Ch2_1ViewController 的接口中声明的vertexBufferID变量保存了用于盛放本例中用到的顶点数据的缓存的OpenGl ES标识符。OpenGLES_Ch2_1View- Controller类的实现代码解释了缓存标识符的初始化和使用。

OpenGLES_Ch2_1ViewController 接口中的baseEffect属性声明了一个GLK BaseEffect实例的指针。Objective-C属性声明了与实例变量相似的值。Objective-C对象的属性 可以使用“点符号” 访问,例如someObject.baseEffect ;或者使用方法访问,比如按 照-set命名约定的用于设定属性值的方法和按照-命名约定的用于返回属性值的方法。这些特别命名的方法叫做访问器(accessor) 。-baseEffct访问器用于返回OpenGLES_Ch2_1ViewController 的baseEffect属性的值。用于设置这个属性值的访问器是“-setBaseEffect:”。属性通常并不一定是以实例变量的形式实现的,它们的值可能是按需计算的或者是从数据库里加载的。Objective-C的属性语法提供了对象不需要暴露类声明中的值是怎么保存的就能提供值的声明方式。当点符号被用来获取或设置--个属性的值时,Objective-C编译器会自动地替换成对于适当命名的 访问器方法的访问。Objective-C还提供了一个自动生成访问器方法的实现代码的方法,这个我们会在OpenGLES_Ch2_1ViewController 的实现代码中讲到。

GLK BaseEffect是GLKit提供的另-一个内建类。GLKBaseEffect的存在是为了简化OpenGLES的很多常用操作。GLKBaseEffect隐藏了iOs设备支持的多个OpenGL ES版本之间的差异。在应用中使用GLKBaseEffect能减少需要编写的代码量。在OpenGLES_Ch2_1ViewController 的实现中详细解释了GLKBaseEffect。

2.3.4、OpenGLES_Ch2_1ViewController类的实现

OpenGLES_Ch2_1ViewController.m 文件是原先那个工程刚建立时Xcode自动生成的文件,不过为这个例子做了一些修改。OpenGLES_Ch2_1 ViewController.m文件包含了针对OpenGLES_Ch2_1ViewController 类的Objctive-C的实现。在实现代码中只定义了3个方法:-viewDidLoad、-glkView:drawInRect: 和-viewDidUnload。本节会详细i解释这3个方法。本章和后面章节中的例子会在这个例子的源码之上建立。以下就是实现代码的开始部分:


// OpenGLES_ Ch2_ 1ViewController .m .
// OpenGLES_ Ch2_ 1

#import "OpenGLES_Ch2_1ViewController.h"
@implementation OpenGLES_Ch2_1ViewController
@synthesize baseEffect ;

@synthesize baseEffect ;表达式让Objective-C编译器为baseEffect属性自动地生成访问器方法。相对于使用@synthesize表达式,另一种方法是在代码中显式实现恰当 命名的访问器方法。对于这个例子来说没有显式实现其访问器的任何理由,因为标准的访问器行为就足够了。只有在属性的存储需要特殊处理时或者属性值的变化需要调用自 定义应用逻辑时才需要显式地编写访问器。

在OpenGLES_Ch2_1ViewController.m 中的接下来一段代码定义了一个C结构体SceneVertex,用来保存一个GLKVector3类型的成员positionCoords。回顾一下第 1章,顶点位置可以用一个起始于坐标系原点的矢量来表示。GLKit的GLKVector3类型保存了3个坐标:X、Y和Z。 vertices变量是一个用顶点数据初始化的普通C数组,这个变量用来定义一个三角形。

The vertices variable is as an ordinary C array initialized with vertex data to define a triangle.
///////////////////////////////
// This data type is used to store information for each vertex
typedef struct {
    GLKVector3 posit ionCoords;    
}
SceneVertex;

///////////////
// Define vertex data for a triangle to use in example

static const SceneVertex vertices[ ] = {
    {{-0.5f, -0.5f, 0.0)}, //lower left corner
    {{0.5f, -0.5f, 0.0}}, // lower right corner
    {{-0.5f,0.5f, 0.0}}// upper left corner
};

这个例子的顶点位置坐标是挑选出来的,因为默认的用于-一个OpenGL上下文的可见坐标系是分别沿着X、Y、Z轴从-1.0延伸到1.0的。例子三角形的坐标把它置于可见坐标系的中央并且与X和Y轴所形成的平面对齐。图2-7显示了vertices[] 定义的在一个代表默认OpenGL ES坐标系的可见部分的立方体内的三角形。

1. -viewDidLoad

接下来的-viewDidLoad方法为OpenGL ES提供三角形的顶点数据。-viewDidLoad方法继承自GLKViewController类,当与GLKViewController相关联的应用的GLKView 实例被从storyboard文件加载时会自动调用这个方法。OpenGLES_Ch2_1ViewController提供了它自己的-viewDidLoad方法的实现,在込个奕現中会首先凋用它的超类的实现:

图 2-7
// Called when the view controller's view is loaded
//Perform initialization before the view ìs asked to draw
- (void)viewDidLoad {
    [super viewDidLoad];
    // Verify the type of view created automatically by the
    //Interface Builder storyboard
    GLKView *view = (GLKView *)self.view;
    NSAssert( [view isKind0fClass:[GLKView class]],@"view controller's view is not a GLKView" );
    // Create an OpenGL ES 2.0 context and provide it to the
    // view
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPI0penGLES2];
    // Make the new context current
    [EAGLContext setCurrentContext:view.context];
    // Create a base effect that provides standard 0penGL ES 2.0
    // Shading Language programs and set constants to be used for
    // a11 subsequent rendering
    self.baseEffect = [[GLKBaseEffect alloc] init];
    self.baseEffect.seConstantColor = GL_TRUE;
    self.baseEffect.constantColor = GLKVector4Make(
    1.0f, // Red
    1.0f, // Green
    1.0f, // Blue
    1.0f);// Alpha
// Set the background color stored in the current context
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //    background color
// Generate, bind, and initialize contents of a buffer to be
// stored in GPU memory
    glGenBuffers(1,//  STEP 1
        &vertexBufferID);
    glBindBuffer(GL_ ARRAY_ BUPFER, // STEP 2
        vertexBufferID);
    glBufferData(//STEP 3
        GL_ ARRAY_ BUFFER, // Initialize buffer contents
        sizeof (vertices), //Number of bytes to copy
        vertices,// Address of bytes to copy
        GL_ STATIC_ DRAW); 1//Hint: cache in GPU memory
}

-viewDidLoad方法会将它继承的view属性的值转换为GLKView类型。类似OpenGLES_Ch2_1ViewController的GLKViewController的子类只能与GLKView实例或者是GLKView子类的实例一起正确工作。但是,这个例子storyboard文件定义了哪一个是与应用的GLKViewController实例相关联的视图。使用NSAssert()函数的一个运行时检查会验证在运行时从storyboard加载的视图是否确实是正确的类型。如果验证的条件为false,那么NSAssert()会向调试器或iOS设备控制台发送一个错误消息。NSAssert()还会生成一个 如果不做处理就停止应用的NSInternalInconsistencyException。在这个例子中,无法从一个加载自storyboard的错误视图还原应用的界面,因此在运行时监测到错误的时候最好先停止应用。

如在第1章介绍的,OpenGL ES的上下文不仅会保存OpenGL ES的状态,还会控制GPU去执行渲染运算。OpenGLES_Ch2_1ViewController的-viewDidLoad方法会分配并初始化-一个内建的EAGLContext类的实例,这个实例会封装-一个特定于某个平台的OpenGL ES上下文。苹果还没有说明开头的EAGL前缀代表什么,但是它可能代表的是“Embedded Apple GL”。苹果iOS中的OpenGLES框架一般是以EAGL为前缀来声明Objective-C类和函数的。

在任何其他的OpenGLES配置或者渲染发生之前,应用的GLKView实例的上下文属性都需要设置为当前EAGLContext实例既支持OpenGLES1.1,又支持OpenGL ES 2.0。本书中的例子使用的是2.0版本。下面的代码行在为视图的上下文属性赋值之前,分配了一个新的EAGLContext的实例,并用粗体标注的常量将它初始化为OpenGL ES 2.0。


view.context = [[EAGLContext alloc]
initWithAPI:kEAGLRenderingAPIOpenGLES2];

// Make the new context current
[EAGLContext setCurrentContext:view.context] ;

一个应用可以使用多个上下文。EAGLContext 的方法“+setCurrentContext:”会为接下来的OpenGLES运算设置将会用到的上下文。“+setCurrentContext:”方法前面的加号表明“ +setCurrentContext:"是- 一个类方法。在Objective-C中,类方法是在即使没有这个类的实例时仍然可以被类自身调用的方法。

苹果的OpenGLES.framework为EAGLContext的“-initWithAPI:” 方法声明了常量kEAGLRenderingAPIOpenGLES2。同时,还存在着一个kEAGLRendering APIOpen-GLES1常量。OpenGL ES 2.0标准与以前的版本有很大的不同。尤其是OpenGL ES 2.0省略了很多特性和在上一个标准中定义的应用支持基础结构。作为替代,OpenGLES2.0提供了一个新的更灵活的可编程GPU概念。为了灵活性,苹果建议我们在新应用中使用OpenGL ES 2.0。在介绍苹果的GLKit之前,OpenGL ES 2.0还需要一些前期工作以编程GPU并重新创建一些2.0版本丢失的、1.1 版本默认包含的便利功能。GLKit现在替换了大部分OpenGL ES 1.1 的基础结构,同时让OpenGL ES 2.0与OpenGLES 1.1 使用起来一样容易。

-viewDidLoad方法接着设置OpenGLES_Ch2_1ViewController 的baseEffect属性为 一个新分配并初始化的GLKBaseEffect类型的实例,同时设置GLKBaseEffect实例的一些属性为比较适合这个例子的值。

// Create a base effect that provides standard OpenGL ES 2.0
// Shading Language programs and set constants to be used for
// all subsequent rendering
self.baseBffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;

GLKBaseEffect类提供了不依赖于所使用的OpenGL ES版本的控制OpenGLES渲染的方法。OpenGL ES 1.1OpenGL ES 2.0的内部工作机制是非常不同的。2.0 版本执行为GPU专门定制的程序。如果没有GLKitGLKBaseEffect类,完成这个简单的例子就需要用OpenGLES2.0的“ShadingLanguage”编写一个小的GPU程序。GLKBaseEffect会在需要的时候自动地构建GPU程序并极大地简化本书中的例子。

控制渲染像素颜色的方式有很多种。这个应用的GLKBaseEffect实例使用一个恒定 不变的白色来渲染三角形。这就意味着在三角形中的每一-个像素都有相同的颜色。下面的代码使用在GLKit中定义的用于保存4个颜色元素值的C数据结构体GLK Vector4来 设置这个恒定的颜色:

self.baseEffect.constantColor = GLKVector4Make (
    1.0f, // Red
    1.0f, // Green
    1.0f, // Blue
    1.0f);// Alpha
//Set the background color stored in the current context
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //background color

前三个颜色元素是第1章图1-2中介绍的红、绿、蓝。第4个值为透明度,它决定了像素是半透明还是不透明。透明度元素会在第3章做详细介绍。设置红、绿、蓝为满值1.0,以设置成白色。设置透明度为满值以使颜色为完全不透明。红、绿、蓝和透明度值统称为一个RGBA颜色。GLKVector4Make() 函数返回一个用指定的值初始化的GLKit GLKVector4结构体。

glClearColor()函数设置当前OpenGL ES的上下文的“ 清除颜色”为不透明黑色。清除颜色由RGBA颜色元素值组成,用于在上下文的帧缓存被清除时初始化每个像素的颜色值。

第1章介绍了用于在CPU控制的内存和GPU控制的内存之间交换数据的缓存的概念。用于定义要绘制的三角形的顶点位置数据必须要发送到GPU来渲染。创建并使用一个用于保存顶点数据的顶点属性数组缓存。前3个步骤如下:

  1. 为缓存生成一个独一无二的标识符。
  2. 为接下来的运算绑定缓存。
  3. 复制数据到缓存中。

下面来自-viewDidLoad方法的实现的代码执行了前3步:

//Generate, bind, and initialize contents of a buffer to be
// stored in GPU memory
glGenBuffers(l, // STEP 1
    &vertexBufferID) ;
glBindBuffer(GL_ ARRAY_ BUEFER, // STEP 2
    vertexBuf ferID);

glBufferData(// STEP 3
    GL_ARRAY_BUFFER, // Initialize buffer contents
    sizeof (vertices), // Number of bytes to copy
    vertices , // Address of bytes to copy
    GL_ STATIC_ DRAW) ;// Hint: cache in GPU memory

在第1步中,glGenBuffers()函数的第一个参数用于指定要生成的缓存标识符的数量,第二个参数是一个指针,指向生成的标识符的内存保存位置。在当前情况下,一个标识符被生成,并保存在vertexBufferID实例变量中。

在第2步中,glBindBuffer0函数绑定用于指定标识符的缓存到当前缓存。OpenGL ES保存不同类型的缓存标识符到当前OpenGLES上下文的不同部位。但是,在任意时刻每种类型只能绑定- . 个缓存。如果在这个例子中使用了两个顶点属性数组缓存,那么在同一时刻它们不能都被绑定。

glBindBuffer()的第-一个参数是一个常量,用于指定要绑定哪--种类型的缓存。OpenGLES2.0对于glBindBuffer()的实现只支持两种类型的缓存,GLARRAY_BUFFERGL_ELEMENT_ARRAY_BUFFERGL_ELEMENT_ARRAY_BUFFER将会在第6章详细解释。GL_ARRAY_BUFFER类型用于指定一个顶点属性数组,例如本例中三角形顶点的位置。glBindBuffer() 的第二个参数是要绑定的缓存的标识符。


注意

缓存标识符实际上是无符号整型。0值表示没有缓存。用0作为第二个参数调用glBindBuffer()函数来配置当前上下文的话,没有指定类型的缓存会被绑定。缓存标识符在OpenGL ES文档中又叫做“names”。


在第3步中,glBufferData函数复制应用的顶点数据到当前上下文所绑定的顶点缓存中。


glBufferData(//STEP3.
    GL_ ARRAY_ BUFFER, //Initialize buffer contents
    sizeof (vertices), //Number of bytes to copy
    vertices ,// Address of bytes to copy
    GL_ STATIC_ DRAW) ;//Hint: cache in GPU memory

glBufferData()的第-一个参 数用于指定要更新当前上下文中所绑定的是哪一个缓存。第二个参数指定要复制进这个缓存的字节的数量。第三个参数是要复制的字节的地址。最后,第4个参数提示了缓存在未来的运算中可能将会被怎样使用。GL_STATIC_DRAW提示会告诉上下文,缓存中的内容适合复制到GPU控制的内存,因为很少对其进行修改。这个信息可以帮助OpenGL ES优化内存使用。使用GL_DYNAMIC_DRAW作为提示会告诉上下文,缓存内的数据会频繁改变,同时提示OpenGLES以不同的方式来处理缓存的存储。

2. -glkView:drawInRect:

每当一个GLKView实例需要被重绘时,它都会让保存在视图的上下文属性中的OpenGL ES的上下文成为当前上下文。如果需要的话,GLKView实例会绑定与一个Core Animation层分享的帧缓存,执行其他的标准OpenGL ES配置,并发送一个消息来调用OpenGLES_Ch2_1ViewController-glkView:drawInRect:方法。-glkView:drawInRect:是GLKView类的委托方法。作为GLKViewController的子类,OpenGLES_Ch2_1ViewController会自动成为从storyboard文件加载的关联视图的委托。

下面委托方法的实现告诉baseEffect准备好当前OpenGLES的上下文,以便为使用baseEffect生成的属性和Shading Language程序的绘图做好准备。接着,调用OpenGLES的glClear()丽数来设置当前绑定的帧缓存的像素颜色渲染缓存中的每一个像素的颜色为前面使用glClearColor()函数设定的值。正如2.1节所描述的,帧缓存可能有除了像素颜色渲染缓存之外的其他附加的缓存,并且如果其他的缓存被使用了,它们可以通过在glClear()函数中指定不同的参数来清除。glClear()函数会有效地设置帧缓存中的每一个像素的颜色为背景色。

- (void)glkView: (GLKView *)view drawInRect: (CGRect )rect {

    [self.baseEffect prepareToDraw];
    glClear (GL_COLOR_BUFFER_BIT);
    // Enable use of currently bound vertex buffer
    glEnableVertexAttribArray(      // STEP 4
        GLKVertexAttribPosition);

    glVertexAttribPointer (         //STEP 5
        GLKVertexAttribPosition,
        3,                          //three components per vertex
        GL_FLOAT,                   //data is floating point
        GL_ FALSE,                  //no fixed point scaling
        sizeof(SceneVertex),        // no gaps in data
        NULL);                      // NULL tells GPU to start at
                                    // beginning of bound buffer
    // Draw triangles using the first three vertices in the
    // currently bound vertex buffer
    glDrawArrays (GL_ TRIANGLES , //STEP 6
        0, // Start with first vertex in currently bound buffer
        3); // Use three vertices from currently bound buffer

在帧缓存被清除以后,是时候用存储在当前绑定的OpenGLES的GLARRAY_BUFFER类型的缓存中的顶点数据绘制例子中的三角形了。使用缓存的前三步已经在-viewDidLoad方法中被执行了。正如第1章所描述的,OpenGLES_Ch2_1ViewController的“glkView:drawInRect:”方法会执行剩下的几个步骤:

  1. 启动。
  2. 设置指针。
  3. 绘图。

在第4步中,通过调用glEnableVertexAttrib-Array()来启动顶点缓存渲染操作。OpenGLES所支持的每一个渲染操作都可以单独地使用保存在当前OpenGLES.上下文中的设置来开启或关闭。

在第5步中,glVertextAttribPointer() 函数会告诉OpenGL ES顶点数据在哪里,以及怎么解释为每个顶点保存的数据。在这个例子中,glVertexAttribPointer() 的第一个参数指示当前绑定的缓存包含每个顶点的位置信息。第二个参数指示每个位置有3个部分。第三个参数告诉OpenGL ES每个部分都保存为一个浮点类型的值。第四个参数告诉OpenGLES小数点固定数据是否可以被改变。本书中没有例子会使用小数点固定的数据,因此这个参数值是GL_FALSE。


注意

小数点固定类型是OpenGL ES支持的对于浮点类型的一种替代。小数点固定类型用牺牲精度的方法来节省内存。所有现代GPU都对浮点数的使用做了优化,并且小数点固定数据在使用之前最终都会被转换成浮点数。因此坚持使用浮点数可以减少GPU的运算量并提高精度。


第五个参数叫做“步幅”,它指定了每个顶点的保存需要多少个字节。换句话说,步幅指定了GPU从-一个顶点的内存开始位置转到下一个顶点的内存开始位置需要跳过多少字节。sizeof (GLKVector3)指示在缓存中没有额外的字节,即顶点位置数据是密封的。在一个顶点缓存中保存除了每个顶点位置的X、Y、Z坐标之外的其他数据也是可能的。图2-8中的顶点数据内存模型显示了顶点存储器的一些选项。第一个图显示的是每个顶点的3D顶点位置坐标都紧密地保存在12字节中,就像OpenGLES_Ch2_1 那个例子一样。第二个图显示的是用于每个顶点的存储的额外字节,因此在内存中在一个顶点与下一个顶点的位置坐标之间有缺口。

图 2-8

glVertexAttribPointer()的最后一个参数是NULL,这告诉OpenGL ES可以从当前绑定的顶点缓存的开始位置访问顶点数据。

在第6步中,通过调用glDrawArrays()来执行绘图。glDrawArrays() 的第一个参数会告诉GPU怎么处理在绑定的顶点缓存内的顶点数据。这个例子会指示OpenGLES去渲染三角形。glDrawArrays()的第二个参数和第三个参数分别指定缓存内的需要渲染的第一个顶点的位置和需要渲染的顶点的数量。至此,在图2-3中显示的场景已经被完全地渲染出来或者至少在GPU处理完成后它就会被完全地渲染出来。请记住GPU运算与CPU运算是异步的。在这个例子中的所有代码都是运行在CPU上的,然后在需要进一:步处理的时候向GPU发送命令。GPU可能也会处理发送自iOS的Core Animation的命 令,因此在任何给定的时刻GPU总共要执行多少处理并不一定。

3. -viewDidUnload

OpenGLES_Ch2_1ViewController 实现的最后一个方法是-viewDidUnload方法。和-viewDidLoad在与视图控制器相关的视图被加载时会被自动调用一样, -viewDidUnload方法会在视图最终被卸载时调用。卸载的视图将不再被绘制,因此任何只是在绘制时需要的OpenGLES缓存都可以被安全地删除。

第7步是删除不再需要的顶点缓存和上下文。设置vertexBufferID为0避免了在对应的缓存被删除以后还使用其无效的标识符。设置视图的,上下文属性为nil并设置当前 上下文为nil, 以便让Cocoa Touch收回所有上下文使用的内存和其他资源。

- (void )viewDidUnload {
    [super viewDidUnload];
    //Make the view's context current
    GLKView *view = (GLKView *)self.view;
    [EAGLContext setCur rentContext:view.context];

// Delete buffers that aren't needed when view is unloaded
    if (0 != vertexBufferID) {
        glDeleteBuffers (1, //STEP 7
                        &vertexBufferID);
            vertexBufferID = 0;
        )

//Stop using the context created in -viewDidLoad
    ((GLKView *)self.view).context = nil;
    [EAGLContext setCurrentContext:nil];
}

2.3.5、支持文件

在图2-6中显示的组内的png文件是OpenGLESCh2_1应用的图标。操作系统会根据应用所运行的设备是iPhone、iPodTouch还是iPad而自动地选择正确的图标。 OpenGL_ES_for_iOS_72x72.png 文件包含iPad要用到的图标图像。OpenGL_ES_for_iOS_114x114.pngOpenGL_ES_for_iOS_57x57.png 包含iPod Touch和iPhone要用到的图标图像。扩展名.png代表Portable Network Graphics (PNG)。iOS设备原生支持 PNG文件并且会按照国际标准化组织的标准存储图像。

OpenGLES_Ch2_1-Info.plist文件是在新工程创建的时候由Xcode自动生成的。OpenGLES_Ch2_1-Info.plist 保存诸如应用的版本号、使用的storyboard文件的名字、图标文件的名字等配置信息。添加应用特有的信息到这个文件是可能的,但是OpenGLES_Ch2_1应用不需要任何非标准的信息。配置文件只会在应用每次启动的时候由应用读取 一次。

InfoPlist.strings文件是在新工程创建的时候由Xcode自动生成的。InfoPlist.strings文件包含在OpenGLES_Ch2_1-Info.plist 文件中使用的字符串的本地化版本。本地化是指为不同的语言和文化团体提供专门的文本和图像。InfoPlist.strings 提供了一个根据用户的地区为相同的应用提供不同的图标和用户界面的方式。

1.main函数

main.m文件是工程创建的时候由Xcode自动生成的并且包含应用的main()函数的实现。main()函数被操作系统调用来开启ANSI C、C++ 和Objective-C程序的运行。生成的main.m文件包含如下代码:

//
// main .m
// OpenGLES_ _Ch2_ 1
//

#import <UIKit/UIKit.h>
#import "OpenGLBS_Ch2_1AppDelegate.h"

int main(int argc, char *argv[]) {
    @autoreleasepool {
    return UIApplicationMain(argc, argv,nil,NSStringFromClass([OpenGLES_Ch2_1AppDelegate class]));
}
}

main()函数使用Objective-C的@autoreleasepool关键字来开启自动引用计数日(Automatic Reference Counting)。main() 函数还会调UIApplicationMain()函数,这个函数创建了包含UIApplication实例在内的应用的关键对象,同时开始处理用户事件。NSStringFromClass([OpenGLES_Ch2_AppDelegate class])表达式指定了与新创建的UIApplication实例一起使用的应用委托类的名字。UIApplication 创建了一个充满整个显示屏的UIWindow,同时加载了-一个或者多个storyboard文件来构建应用的用户界面。UIApplicationMain()函数不会干涉UIApplication 的执行,直到应用退出才会返回。

2.预编译头文件

OpenGLES_Ch2_1-Prefix.pch 文件是由Xcode自动生成的并且改善了这个例子中应用的编译速度。扩展名.pch代表预编译头文件(pre-compiled header)。

3. Framework和Product

Frameworks是iOs应用用到的系统库和资源。在Xcode文件列表中的Frameworks文件夹指定了应用使用了哪些框架。OpenGLES_Ch2_1 例子使用了OpenGLES.frameworkQuartzCore.frameworkGLKit.frameworkUIKit.frameworkFoundation.frameworkCoreGraphics.framework

Products文件夹包含编译Xcode工程中指定的应用时产生的结果。

results matching ""

    No results matching ""