在上一篇 OpenGL ES 编程攻略(上)咱们了解了OpenGL ES 的基本概念和 装备 OpenGL ES 上下文 的相关内容,今日持续完结 OpenGL ES编程攻略的接下来的内容。
运用OpenGL ES和GLKit绘图
GLKit结构供给了视图和视图操控器类,消除了制造OpenGL ES内容和动画所需的设置和保护代码。GLKView
类办理OpenGL ES基础设施,为您的绘图代码供给方位,GLKViewController
类供给了一个烘托循环,用于在GLKit视图中流畅地动画OpenGL ES内容。这些类扩展了用于制造视图内容和办理视图出现的规范UIKit规划模式。因而,您能够主要专心于OpenGL ES烘托代码,并快速发动和运转运用程序。GLKit结构还供给了其他功用,以简化OpenGL ES 2.0和3.0的开发。
GLKit视图按需制造OpenGL ES内容
GLKView
类供给了基于OpenGL ES的规范UIView
绘图周期的等价物。UIView
实例主动装备其图形上下文,以便您的drawRect:
完结只需求履行Quartz 2D绘图指令,GLKView
实例会主动装备自己,以便您的绘图办法只需求履行OpenGL ES绘图指令。GLKView
类经过保护一个帧缓冲方针来供给此功用,该方针保存OpenGL ES绘图指令的成果,然后在绘图办法返回后主动将其出现给Core Animation。
与规范UIKit视图相同,GLKit视图按需出现其内容。当您的视图初次显现时,它会调用您的绘图办法——Core Animation缓存烘托的输出,并在显现视图时显现它。当您想更改视图的内容时,请调用其setNeedsDisplay
办法,该视图再次调用您的绘图办法,缓存生成的图画并将其显现在屏幕上。当用于烘托图画的数据不频频或仅依据用户操作更改时,这种办法十分有用。经过仅在需求时烘托新视图内容,您能够节省设备的电池电量,并为设备留出更多时刻履行其他操作。
图3-1运用GLKit视图烘托OpenGL ES内容
创立和装备GLKit视图
您能够以编程办法或运用Interface Builder创立和装备GLKView
方针。在运用它进行绘图之前,您有必要将其与EAGLContext
方针相相关。
- 以编程办法创立视图时,首要创立一个上下文,然后将其传递给视图的
initWithFrame:context:
办法。 - 从故事板加载视图后,创立一个上下文,并将其设置为视图
context
特点的值。
GLKit视图会主动创立和装备自己的OpenGL ES帧缓冲方针和烘托缓冲区。您能够运用视图的可制造特点操控这些方针的特点,如清单3-1所示。假如您更改GLKit视图的巨细、缩放因子或可制造特点,它会主动删去并从头创立恰当的帧缓冲方针和烘托缓冲区,下次制造其内容时。
清单3-1装备GLKit视图
- (void)viewDidLoad
{
[super viewDidLoad];
// 创立一个OpenGL ES上下文,并将其分配给从故事板加载的视图
GLKView *view = (GLKView *)self.view;
view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
// 装备视图创立的烘托缓冲区
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
// 启用多采样
view.drawableMultisample = GLKViewDrawableMultisample4X;
}
您能够运用GLKView
实例的drawableMultisample
特点为多采样。多采样是一种抗锯齿形式,能够滑润锯齿状边际,以运用更多内存和片段处理时刻为代价来改善大多数3D运用程序的图画质量——假如您启用多采样,请一直测验运用程序的功用,以保证它依然能够承受。
运用GLKit视图绘图
图3-1概述了制造OpenGL ES内容的三个进程:预备OpenGL ES基础设施,发出绘图指令,以及将烘托的内容出现给Core Animation以供显现。GLKView
类完结了第一步和第3步。在第二步中,您将完结一种绘图办法,如清单3-2中的示例。
列出3-2 GLKit视图的示例绘图办法
- (void)drawRect:(CGRect)rect
{
// 铲除帧缓冲区
glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 运用从前装备的纹理,着⾊器和极点数组制造
glBindTexture(GL_TEXTURE_2D, _planetTexture);
glUseProgram(_diffuseShading);
glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m);
glBindVertexArrayOES(_planetMesh);
glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT);
}
留意: glClear
功用向OpenGL ES提示,任何现有的帧缓冲区内容都能够丢掉,然后防止了将之前的内容加载到内存中的贵重内存操作。为了保证最佳功用,您应该一直在绘图前调用此函数。
GLKView
类能够为OpenGL ES绘图供给一个简略的界面,由于它办理OpenGL ES烘托进程的规范部分:
-
在调用绘图办法之前,视图:
- 使其
EAGLContext
方针成为当前上下文 - 依据当前巨细、份额因子和可制造特点(假如需求)创立帧缓冲区方针和烘托缓冲区
- 将帧缓冲区方针绑定为制造指令的当前方针
- 将OpenGL ES视口设置为匹配帧缓冲区巨细
- 使其
-
绘图办法返回后,视图:
- 处理多采样缓冲区(假如启用了多采样)
- 丢掉不再需求内容的烘托缓冲区
- 向 Core Animation 出现烘托缓冲区内容,以进行缓存和显现
运用托付方针进行烘托
许多OpenGL ES运用程序在自定义类中完结烘托代码。这种办法的一个长处是,它答应您经过为每个烘托算法定义不同的烘托器类来轻松支撑多个烘托算法。同享一起功用的烘托算法能够从超类继承它。或许,您能够运用它们自定义烘托,以便在具有更强壮硬件的设备上取得更好的图画质量。
GLKit十分适合这种办法——您能够将烘托器方针作为规范GLKView
实例的托付。您的烘托器类不是子类GLKView
和完结drawRect:
办法,而是选用GLKViewDelegate
协议并完结glkView:drawInRect:
办法。清单3-3演示了在运用程序发动时依据硬件功用挑选烘托器类。
清单3-3依据硬件功用挑选烘托器类
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 创立一个上下文,以便咱们能够测验功用
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:context];
// 依据设备功用挑选烘托类
GLint maxTextureSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if (maxTextureSize > 2048)
self.renderer = [[MyBigTextureRenderer alloc] initWithContext:context];
else
self.renderer = [[MyRenderer alloc] initWithContext:context];
// 使烘托器成为从主故事板加载的视图的托付
GLKView *view = (GLKView *)self.window.rootViewController.view;
view.delegate = self.renderer;
// 为视图供给OpenGL ES上下文,以便它能够制造
view.context = context;
return YES;
}
GLKit视图操控器为OpenGL ES内容添加动画
默许情况下,GLKView
方针按需出现其内容。尽管如此,运用OpenGL ES绘图的一个要害优势是它能够运用图形处理硬件对杂乱场景进行连续动画——游戏和模仿等运用程序很少显现静态图画。关于这些情况,GLKit结构供给了一个视图操控器类,为其办理的GLKView
方针保护动画循环。这个循环遵从游戏和模仿中常见的规划模式,分为两个阶段:更新和显现。图3-2显现了动画循环的简化示例。
图3-2动画循环
了解动画循环
关于更新阶段,视图操控器调用自己的update
办法(或其托付的glkViewControllerUpdate:
办法)。在这个办法中,您应该为制造下一帧做好预备。例如,游戏或许会运用此办法依据自上一帧以来收到的输入事件来确认玩家和敌方角色的方位,而科学可视化或许会运用此办法运转其模仿进程。假如您需求计时信息来确认运用程序下一帧的状况,请运用视图操控器的计时特点之一,例如timeSinceLastUpdate
特点。在图3-2中,更新相位会增加一个angle
变量,并用它来核算转化矩阵。
关于显现阶段,视图操控器调用其视图的display
办法,进而调用您的绘图办法。在绘图办法中,您将OpenGL ES绘图指令提交到GPU以出现您的内容。为了取得最佳功用,您的运用程序应在烘托新帧开始时修正OpenGL ES方针,然后提交绘图指令。在图3-2中,显现阶段在上色器程序中将统一变量设置为更新阶段核算的矩阵,然后提交绘图指令来出现新内容。
动画循环以视图操控器的framesPerSecond
特点指示的速度在这两个阶段之间替换。您能够运用preferredFramesPerSecond
特点来设置所需的帧速率——为了优化当前显现硬件的功用,视图操控器会主动挑选挨近您首选值的最佳帧速率。
重要信息: 为了取得最佳效果,请挑选您的运用程序能够持续完结的帧速率。平稳、共同的帧速率比不规则改变的帧速率发生更愉快的用户体验。
运用GLKit视图操控器
清单3-4演示了运用GLKViewController
子类和GLKView
实例烘托动画OpenGL ES内容的典型策略。
清单3-4运用GLKit视图和视图操控器制造OpenGL ES内容并为其制造动画
@implementation PlanetViewController // subclass of GLKViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 创立一个OpenGL ES上下文,并将其分配给从故事板加载的视图
GLKView *view = (GLKView *)self.view;
view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
// 设置动画帧速率
self.preferredFramesPerSecond = 60;
// 未显现:加载上色器、纹路和极点数组,设置投影矩阵
[self setupGL];
}
- (void)update
{
_rotation += self.timeSinceLastUpdate * M_PI_2; // 每秒四分之一轮换
// 为旋转行星设置改换矩阵
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(_rotation, 0.0f, 1.0f, 0.0f);
_normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL);
_modelViewProjectionMatrix = GLKMatrix4Multiply(_projectionMatrix, modelViewMatrix);
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
// 铲除帧缓冲区
glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 将上色器制服设置为在-update中核算的值
glUseProgram(_diffuseShading);
glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m);
glUniformMatrix3fv(_uniformNormalMatrix, 1, 0, _normalMatrix.m);
// 运用之前装备的纹路和极点数组制造
glBindTexture(GL_TEXTURE_2D, _planetTexture);
glBindVertexArrayOES(_planetMesh);
glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT, 0);
}
@end
在本例中,从故事板加载PlanetViewController
类(自定义GLKViewController
子类)的实例,以及规范GLKView
实例及其可制造特点。viewDidLoad
办法创立一个OpenGL ES上下文并将其供给给视图,并设置动画循环的帧速率。
视图操控器主动是其视图的托付,因而它完结了动画循环的更新和显现阶段。在update
办法中,它核算了显现旋转行星所需的改换矩阵。在glkView:drawInRect:
办法中,它将这些矩阵供给给上色器程序,并提交绘图指令来烘托行星几许形状。
运用GLKit开发您的烘托器
除了检查和检查操控器基础设施外,GLKit结构还供给了其他几项功用,以方便在iOS上开发OpenGL ES。
处理矢量和矩阵数学
OpenGL ES 2.0及更高版别不供给用于创立或指定转化矩阵的内置功用。相反,可编程上色器供给极点转化,并运用通用均匀变量指定上色器输入。GLKit 结构包括一个全面的矢量和矩阵类型和功用库,针对 iOS 硬件的高功用进行了优化。(请参阅*GLKit结构参阅*。)
从OpenGL ES 1.1固定功用管道迁移
OpenGL ES 2.0及更高版别删去了与OpenGL ES 1.1固定功用图形管道相关的一切功用。GLKBaseEffect
类为OpenGL ES 1.1管道的转化、照明和上色阶段供给了Objective-C模仿,GLKSkyboxEffect
和GLKReflectionMapEffect
类增加了对常见视觉效果的支撑。有关具体信息,请参阅这些课程的参阅文档。
加载纹路数据
GLKTextureLoader
类供给了一种简略的办法,能够同步或异步地将iOS支撑的任何图画格局的纹路数据加载到OpenGL ES上下文中。
制造到其他烘托目的地
Framebuffer方针是烘托指令的目的地。当您创立帧缓冲方针时,您能够准确操控其对色彩、深度和模板数据的存储。您经过将图画附加到帧缓冲区来供给此存储,如图4-1所示。最常见的图画附件是烘托缓冲方针。您还能够将OpenGL ES纹路附加到帧缓冲区的色彩衔接点,这意味着任何绘图指令都会出现到纹路中。稍后,纹路能够作为未来烘托指令的输入。您还能够在单个烘托上下文中创立多个帧缓冲方针。您能够这样做,以便在多个帧缓冲区之间同享相同的烘托管道和OpenGL ES资源。
图4-1带色彩和深度烘托缓冲区的结构缓冲区
一切这些办法都需求手动创立帧缓冲区和烘托缓冲区方针来存储OpenGL ES上下文的烘托成果,以及编写额定的代码将其内容出现到屏幕上,并在需求时运转动画循环。
创立帧缓冲方针
依据您的运用程序打算履行的使命,您的运用程序会装备不同的方针以附加到帧缓冲区方针。在大多数情况下,装备帧缓冲区的区别在于将什么方针附加到帧缓冲区方针的色彩附加点:
- 要运用帧缓冲区进行屏幕外图画处理,请附加一个烘托缓冲区。请参阅创立屏幕外帧缓冲方针。
- 要运用帧缓冲区图画作为后续烘托进程的输入,请附加纹路。请参阅运用帧缓冲方针烘托到纹路。
- 要在Core Animation层组合中运用帧缓冲区,请运用特别的Core Animation感知烘托缓冲区。请参阅烘托到中心动画层。
创立屏幕外帧缓冲区方针
用于屏幕外烘托的帧缓冲区将其一切附件分配为OpenGL ES烘托缓冲区。以下代码分配一个带有色彩和深度附件的帧缓冲方针。
- 创立结构缓冲区并将其绑定。
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- 创立一个色彩烘托缓冲区,为其分配存储空间,并将其附加到结构缓冲区的色彩衔接点。
GLuint colorRenderbuffer;
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
- 创立一个深度或深度/模板烘托缓冲区,为其分配存储空间,并将其附加到帧缓冲区的深度衔接点。
GLuint depthRenderbuffer;
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
- 测验帧缓冲区的完整性。只有当帧缓冲区的装备发生改变时,才需求履行此测验。
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
if(status != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"failed to make complete framebuffer object %x", status);
}
制造到屏幕外烘托缓冲区后,您能够运用glReadPixels
函数将其内容返回CPU进行进一步处理。
运用帧缓冲方针烘托到纹路
创立此帧缓冲区的代码几乎与屏幕外示例相同,但现在分配了一个纹路并将其附加到色彩附件点。
- 创立帧缓冲方针(运用与 创立屏幕外帧缓冲方针 相同的进程)。
- 创立方针纹路,并将其附加到帧缓冲区的色彩衔接点。
// 创立纹路
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
- 分配并附加深度缓冲区(和曾经相同)。
- 测验帧缓冲区的完整性(和曾经相同)。
尽管此示例假设您正在烘托到色彩纹路,但其他选项是或许的。例如,运用OES_depth_texture
扩展,您能够将纹路附加到深度附件点,以将场景的深度信息存储到纹路中。您能够运用此深度信息来核算终究烘托场景中的阴影。
烘托到中心动画层
Core Animation 是 iOS 上图形烘托和动画的中心基础设施。您能够运用承载运用不同 iOS 子体系(例如 UIKit、Quartz 2D 和 OpenGL ES)出现的内容的层来组成运用程序的用户界面或其他视觉显现。OpenGL ES 经过CAEAGLLayer
类衔接到 Core Animation,这是一种特别类型的 Core Animation 层,其内容来自 OpenGL ES 烘托缓冲区。Core Animation 将烘托缓冲区的内容与其他层组成并在屏幕上显现生成的图画。
图4-2 Core Animation与OpenGL ES同享烘托缓冲区
CAEAGLLayer
经过供给两个要害功用为OpenGL ES供给支撑。首要,它为烘托缓冲区分配同享存储空间。其次,它向Core Animation显现烘托缓冲区,用烘托缓冲区的数据替换图层之前的内容。该模型的一个长处是,只有当烘托的图画发生改变时,才需求在每个帧中制造中心动画层的内容。
留意: GLKView
类会主动履行以下进程,因而当您想在视图的内容层中运用OpenGL ES绘图时,您应该运用它。
要运用中心动画层进行OpenGL ES烘托:
-
创立一个
CAEAGLLayer
方针并装备其特点。为了取得最佳功用,请将图层
opaque
特点的值设置为YES
。请参阅“了解中心动画组成功用”。或许,经过为
CAEAGLLayer
方针的drawableProperties
特点分配新的值字典来装备烘托外表的外表特点。您能够指定烘托缓冲区的像素格局,并指定烘托缓冲区的内容在发送到Core Animation后是否被丢掉。有关答应密钥的列表,请参阅*EAGLDrawable协议参阅*。 -
分配OpenGL ES上下文,使其成为当前上下文。请参阅装备OpenGL ES上下文。
-
创立帧缓冲方针(如上面的创立屏幕外帧缓冲方针)。
-
创立一个色彩烘托缓冲区,经过调用上下文的
renderbufferStorage:fromDrawable:
办法分配其存储空间,并将图层方针作为参数传递。宽度、高度和像素格局取自图层,用于为烘托缓冲区分配存储空间。
GLuint colorRenderbuffer;
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:myEAGLLayer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
留意: 当Core Animation层的鸿沟或特点发生改变时,您的运用程序应该从头分配烘托缓冲区的存储空间。假如您不从头分配烘托缓冲区,烘托缓冲区巨细将与图层的巨细不匹配;在这种情况下,Core Animation或许会缩放图画的内容以适合图层。
- 检索色彩烘托缓冲区的高度和宽度。
GLint width;
GLint height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
在前期的比如中,显式供给了烘托缓冲区的宽度和高度,以分配缓冲区的存储空间。在这里,代码在分配存储后从色彩烘托缓冲区检索宽度和高度。您的运用程序这样做是由于色彩烘托缓冲区的实践尺度是依据图层的鸿沟和缩放因子核算的。附加到结构缓冲区的其他烘托缓冲区有必要具有相同的尺度。除了运用高度和宽度来分配深度缓冲区外,还运用它们来分配OpenGL ES视口,并帮助确认运用程序纹路和模型所需的细节水平。请参阅支撑高分辨率显现器。
-
分配并附加深度缓冲区(和曾经相同)。
-
测验帧缓冲区的完整性(和曾经相同)。
-
经过将
CAEAGLLayer
方针传递给可见层的addSublayer:
办法将其添加到中心动画层层次结构中。
制造帧缓冲区方针
现在您有一个帧缓冲区方针,您需求填充它。本节介绍烘托新帧并将其出现给用户所需的进程。烘托到纹路或屏幕外帧缓冲区的效果相似,仅在运用程序运用终究帧的办法上有所不同。
按需烘托或运用动画循环
在烘托到 Core Animation 图层时,您有必要挑选何时制造 OpenGL ES 内容,就像运用 GLKit 视图和视图操控器绘图时相同。假如烘托到屏幕外帧缓冲区或纹路,请依据运用这些类型的帧缓冲区的情况进行绘图。
关于按需绘图,请完结您自己的办法来制造和出现烘托缓冲区,并在您想要显现新内容时调用它。
要运用动画循环绘图,请运用CADisplayLink
方针。显现链接是 Core Animation 供给的一种计时器,可让您将绘图同步到屏幕的刷新率。清单4-1 展现了怎么检索显现视图的屏幕,运用该屏幕创立新的显现链接方针,并将显现链接方针添加到运转循环中。
留意: GLKViewController
类主动运用CADisplayLink
方针为GLKView
内容制造动画。仅当您需求超出GLKit结构范围的行为时,才直接运用CADisplayLink
类。
清单4-1创立和发动显现链接
displayLink = [myView.window.screen displayLinkWithTarget:self selector:@selector(drawFrame)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
在drawFrame
办法的完结中,读取显现链接timestamp
特点,以获取下一个烘托帧的时刻戳。它能够运用该值来核算方针鄙人一帧中的方位。
一般,每次屏幕刷新时都会触发显现链接方针;该值一般为60 Hz,但在不同的设备上或许会有所不同。大多数运用程序不需求每秒更新屏幕60次。您能够将显现链接的frameInterval
特点设置为调用办法之前经过的实践帧数。例如,假如帧距离设置为3,则每第三帧调用一次您的运用程序,或大约每秒20帧。
重要信息: 为了取得最佳效果,请挑选您的运用程序能够持续完结的帧速率。平稳、共同的帧速率比不规则改变的帧速率发生更愉快的用户体验。
烘托帧
图4-3显现了OpenGL ES运用程序在iOS上烘托和出现结构时应采纳的进程。这些进程包括许多进步运用程序功用的提示。
图4-3 iOS OpenGL烘托进程
铲除缓冲区
在每个帧的开头,抹掉一切帧缓冲区附件的内容,这些附件不需求曾经帧的内容来制造下一帧。调用glClear
函数,传递一个带有一切缓冲区的位掩码,如清单4-2所示。
列出4-2个通明结构缓冲器附件
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
运用glClear
“提示”OpenGL ES,能够丢掉烘托缓冲区或纹路的现有内容,然后防止将之前的内容加载到内存中的贵重操作。
预备资源并履行绘图指令
这两个进程包括您在规划运用程序架构时做出的大部分要害决议方案。首要,您决议要向用户显现什么,并装备相应的OpenGL ES方针,如极点缓冲方针、纹路、上色器程序及其输入变量,以便上传到GPU。接下来,您将提交绘图逗号,告诉GPU怎么运用这些资源烘托帧。
OpenGL ES规划攻略更具体地介绍了烘托器规划。现在,最重要的功用优化是,假如您的运用程序仅在烘托新帧开始时修正OpenGL ES方针,则运转速度会更快。尽管您的运用程序能够在修正方针和提交绘图指令之间替换运用(如图4-3中的虚线所示),但假如每帧只履行每个进程一次,它运转速度会更快。
履行绘图指令
此进程将您上一步预备的方针提交绘图指令以运用它们。OpenGL ES规划攻略具体介绍了规划烘托代码的这一部分以高效运转。现在,需求留意的最重要的功用优化是,假如您的运用程序仅在烘托新帧时修正OpenGL ES方针,则运转速度会更快。尽管您的运用程序能够在修正方针和提交绘图指令之间替换运用(如虚线所示),但假如只履行每个进程一次,它运转速度会更快。
处理多采样问题
假如您的运用程序运用多采样来进步图画质量,则您的运用程序有必要在向用户显现像素之前解析像素。运用多采样进步图画质量具体介绍了多采样。
丢掉不必要的烘托缓冲器
丢掉操作是一种功用提示,告诉OpenGL ES不再需求一个或多个烘托缓冲区的内容。经过向OpenGL ES暗示您不需求烘托缓冲区的内容,能够丢掉缓冲区中的数据,并防止更新这些缓冲区内容的贵重使命。
在烘托循环的这个阶段,您的运用程序已经提交了结构的一切绘图指令。尽管您的运用程序需求色彩烘托缓冲区显现到屏幕上,但它或许不需求深度缓冲区的内容。清单4-3丢掉了深度缓冲区的内容。
清单4-3丢掉深度帧缓冲器
const GLenum discards[] = {GL_DEPTH_ATTACHMENT};
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glDiscardFramebufferEXT(GL_FRAMEBUFFER,1,discards);
留意: glDiscardFramebufferEXT
功用由OpenGL ES 1.1和2.0的EXT_discard_framebuffer
扩展供给。在OpenGL ES 3.0上下文中,请运用glInvalidateFramebuffer
函数。
向 Core Animation 展现成果
在此进程中,色彩烘托缓冲区将保存完结的帧,因而您只需将其出现给用户。清单4-4将烘托缓冲区绑定到上下文并出现它。这会导致完结的帧交给Core Animation。
清单4-4出现成品结构
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
默许情况下,您有必要假设烘托缓冲区的内容在运用程序显现烘托缓冲区后被丢掉。这意味着,每次您的运用程序显现帧时,它都有必要在烘托新帧时彻底从头创立帧的内容。因而,上面的代码总是会擦除色彩缓冲区。
假如您的运用程序期望在帧之间保存色彩烘托缓冲区的内容,请将kEAGLDrawablePropertyRetainedBacking
键添加到存储在CAEAGLLayer
方针的drawableProperties
特点中的字典中,并从前期的glClear
函数调用中删去GL_COLOR_BUFFER_BIT
常量。保存备份或许需求iOS分配额定的内存来保存缓冲区的内容,这或许会降低运用程序的功用。
运用多采样来进步图画质量
多采样是一种抗锯齿形式,能够滑润锯齿状边际,并进步大多数3D运用程序的图画质量。OpenGL ES 3.0包括多采样作为中心规范的一部分,iOS经过APPLE_framebuffer_multisample
扩展在OpenGL ES 1.1和2.0中供给。多采样运用更多的内存和片段处理时刻来烘托图画,但它或许会以低于其他办法的功用成本进步图画质量。
图4-4显现了多采样的作业原理。您的运用程序不会创立一个帧缓冲区方针,而是创立两个。多采样缓冲区包括烘托内容所需的一切附件(一般是色彩和深度缓冲区)。解析缓冲区仅包括运用创立帧缓冲区方针中的恰当进程向用户显现烘托图画所需的附件(一般是色彩烘托缓冲区,但或许是纹路)。多样本烘托缓冲区运用与解析帧缓冲区相同的维度分配,但每个参数都包括一个额定的参数,该参数指定要存储每个像素的样本数量。您的运用程序履行对多采样缓冲区的一切烘托,然后经过将这些样本解析到解析缓冲区来生成终究的抗锯齿图画。
图4-4多采样的作业原理
清单4-5显现了创立多采样缓冲区的代码。此代码运用之前创立的缓冲区的宽度和高度。它调用glRenderbufferStorageMultisampleAPPLE
函数,为烘托缓冲区创立多采样存储。
清单4-5创立多样本缓冲区
glGenFramebuffers(1, &sampleFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer);
glGenRenderbuffers(1, &sampleColorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, sampleColorRenderbuffer);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_RGBA8_OES, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sampleColorRenderbuffer);
glGenRenderbuffers(1, &sampleDepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, sampleDepthRenderbuffer);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sampleDepthRenderbuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
以下是修正烘托代码以支撑多采样的进程:
-
在铲除缓冲区进程中,您能够铲除多采样帧缓冲区的内容。
glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer); glViewport(0, 0, framebufferWidth, framebufferHeight); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
提交绘图指令后,您将内容从多采样缓冲区解析为解析缓冲区。为每个像素存储的样本在解析缓冲区中合并为单个样本。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, resolveFrameBuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, sampleFramebuffer); glResolveMultisampleFramebufferAPPLE();
-
在丢掉进程中,您能够丢掉附加到多样本帧缓冲区的两个烘托缓冲区。这是由于您方案出现的内容存储在解析帧缓冲区中。
const GLenum discards[] = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT}; glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards);
-
在“当前成果”进程中,您将显现附加到解析帧缓冲区的色彩烘托缓冲区。
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER];
多采样不是免费的;存储其他样本需求额定的内存,将样本解析到解析帧缓冲区需求时刻。假如您向运用程序添加多采样,请有必要测验运用程序的功用,以保证它依然能够承受。
多使命处理、高分辨率和其他 iOS 功用
运用 OpenGL ES 的许多方面与渠道无关,但在 iOS 上运用 OpenGL ES 的一些细节需求特别考虑。特别是,运用 OpenGL ES 的 iOS 运用程序有必要正确处理多使命处理,否则在移动到后台时或许会被停止。在为 iOS 设备开发 OpenGL ES 内容时,您还应该考虑显现分辨率和其他设备功用。
完结多使命感知 OpenGL ES 运用程序
当用户切换到另一个运用程序时,您的运用程序能够持续运转。
OpenGL ES 运用程序在移动到后台时有必要履行额定的作业。假如运用程序不正确地处理这些使命,它或许会被 iOS 停止。此外,运用程序或许期望释放 OpenGL ES 资源,以便这些资源可供前台运用程序运用。
后台运用程序或许无法在图形硬件上履行指令
假如 OpenGL ES 运用程序试图在图形硬件上履行 OpenGL ES 指令,它就会被停止。iOS 会阻挠后台运用程序拜访图形处理器,以便最前面的运用程序一直能够为用户供给超卓的体验。您的运用不仅能够在后台调用 OpenGL ES 时停止,还能够在之前提交的指令在后台刷新到 GPU 时停止。您的运用程序有必要保证一切从前提交的指令都已完结履行,然后才能进入后台。
假如您运用 GLKit 视图和视图操控器,而且仅在您的绘图办法期间提交 OpenGL ES 指令,则您的运用程序在移动到后台时会主动正确运转。默许情况下,当您的运用程序处于非活动状况时,GLKViewController
该类会暂停其动画计时器,以保证不会调用您的绘图办法。
假如您不运用 GLKit 视图或视图操控器,或许您在GLKView
绘图办法之外提交 OpenGL ES 指令,则有必要采纳以下进程来保证您的运用程序不会在后台停止:
- 在您的运用托付的
applicationWillResignActive:
办法中,您的运用应停止其动画计时器(假如有),将自身置于已知的杰出状况,然后调用该glFinish
函数。 - 在您的运用程序托付的
applicationDidEnterBackground:
办法中,您的运用程序或许期望删去它的一些 OpenGL ES 方针,以便为前台运用程序供给内存和资源。调用该glFinish
函数以保证当即删去资源。 - 在您的运用退出其
applicationDidEnterBackground:
办法后,它不得进行任何新的 OpenGL ES 调用。假如它进行 OpenGL ES 调用,它会被 iOS 停止。 - 在您运用的
applicationWillEnterForeground:
办法中,从头创立任何方针并从头发动动画计时器。
总而言之,您的运用程序需求调用该glFinish
函数以保证一切从前提交的指令都从指令缓冲区中排出并由 OpenGL ES 履行。在它移入后台后,您有必要防止运用 OpenGL ES,直到它移回前台。
在移动到后台之前轻松删去从头创立的资源
当您的运用移入后台时,永远不需求释放 OpenGL ES 方针。一般,您的运用应防止处置其内容。考虑两种情况:
- 用户正在玩您的游戏并短暂退出以检查他们的日历。当玩家回到你的游戏时,游戏的资源还在内存中,游戏能够当即恢复。
- 当用户发动另一个 OpenGL ES 运用程序时,您的 OpenGL ES 运用程序处于后台。假如该运用程序需求的内存比设备上的可用内存多,体系会静默主动停止您的运用程序,而不需求它履行任何额定的作业。
您的方针应该是将您的运用程序规划为一个好公民:这意味着尽或许短地移动到前台所需的时刻,同时还要减少其在后台时的内存占用。
以下是您应该怎么处理这两种情况:
- 你的运用应该在内存中保存纹路、模型和其他资源;当您的运用程序进入后台时,永远不应丢掉需求很长时刻才能从头创立的资源。
- 您的运用程序应该处理能够快速轻松地从头创立的方针。寻找耗费大量内存的方针。
简略方针是您的运用程序分配用于保存烘托成果的帧缓冲区。当您的运用程序在后台时,它对用户不行见,而且或许不会运用 OpenGL ES 出现任何新内容。这意味着您的运用程序的帧缓冲区耗费的内存已分配,但没有用处。此外,帧缓冲区的内容是暂时的;大多数运用程序在每次烘托新帧时都会从头创立帧缓冲区的内容。这使得烘托缓冲区成为能够轻松从头创立的内存密集型资源,成为移动到后台时能够处理的方针的杰出候选者。
假如您运用 GLKit 视图和视图操控器,GLKViewController
当您的运用程序移入后台时,该类会主动处理其相关视图的帧缓冲区。假如您为其他用处手动创立帧缓冲区,则应在您的运用程序移至后台时处理它们。不管哪种情况,您还应该考虑您的运用程序其时能够处理哪些其他临时资源。
支撑高分辨率显现器
默许情况下,GLKit 视图的contentScaleFactor
特点值与包括它的屏幕的份额相匹配,因而其相关的帧缓冲区被装备为以显现器的全分辨率出现。
假如您运用中心动画层出现 OpenGL ES 内容,则其份额因子1.0
默许设置为。要以 Retina 显现器的全分辨率绘图,您应该更改CAEAGLLayer
方针的份额因子以匹配屏幕的份额因子。
当支撑具有高分辨率显现器的设备时,您应该相应地调整运用程序的模型和纹路资源。在高分辨率设备上运转时,您或许期望挑选更具体的模型和纹路来烘托更好的图画。相反,在规范分辨率设备上,您能够运用更小的模型和纹路。
重要提示:许多 OpenGL ES API 调用以屏幕像素表明尺度。假如运用大于 的份额因子1.0
,则应在运用glScissor
、glBlitFramebuffer
、glLineWidth
或glPointSize
函数或gl_PointSize
上色器变量时相应地调整尺度。
决议怎么支撑高分辨率显现器的一个重要因素是功用。Retina 显现屏上的份额因子翻倍,像素数量翻了两番,导致 GPU 处理的片段数量增加了四倍。假如您的运用程序对每个片段履行许多核算,则像素的增加或许会降低帧速率。假如您发现您的运用程序在更高的份额因子下运转速度明显变慢,请考虑以下选项之一:
-
运用本文档中的功用调整攻略优化片段上色器的功用。
-
在片段上色器中完结更简略的算法。经过这样做,您正在降低单个像素的质量,然后以更高的分辨率烘托整个图画。
-
运用介于 1.0 和 和屏幕的份额因子之间的小数份额因子。份额因子 1.5 比份额因子 1.0 供给更好的质量,但需求填充的像素比缩放到 2.0 的图画要少。
-
GLKView
为您的方针drawableColorFormat
和drawableDepthFormat
特点运用较低精度的格局。经过这样做,您能够减少对底层烘托缓冲区进行操作所需的内存带宽。 -
运用较低的份额因子并启用多重采样。另一个长处是多重采样还能够在不支撑高分辨率显现的设备上供给更高的质量。
要为
GLKView
方针启用多重采样,请更改其drawableMultisample
特点的值。假如您不烘托到 GLKit 视图,则有必要手动设置多重采样缓冲区并在出现终究图画之前解析它们。多重采样不是免费的;需求额定的内存来存储额定的样本,而且将样本解析到解析帧缓冲区需求时刻。假如您将多重采样添加到您的运用程序,请一直测验您的运用程序的功用以保证它依然能够承受。
支撑多种接口方向
与任何运用程序相同,OpenGL ES 运用程序应该支撑适合其内容的用户界面方向。您能够在其信息特点列表中为您的运用声明支撑的界面方向,或许为运用其supportedInterfaceOrientations
办法托管您的 OpenGL ES 内容的视图操控器声明。
默许情况下,GLKViewController
和GLKView
类会主动处理方向改变:当用户将设备旋转到支撑的方向时,体系会为方向改变设置动画并更改视图操控器视图的巨细。当它的巨细发生改变时,GLKView
方针会相应地调整其帧缓冲区和视口的巨细。假如您需求响应此更改,请在您的子类中完结viewWillLayoutSubviews
orviewDidLayoutSubviews
办法,或许假如您正在运用自定义子类,请完结该办法。GLKViewController``layoutSubviews``GLKView
假如您运用 Core Animation 层制造 OpenGL ES 内容,您的运用程序仍应包括一个视图操控器来办理用户界面方向。
在外部显现器上出现 OpenGL ES 内容
iOS 设备能够衔接到外部显现器。外接显现器的分辨率及其内容份额因子或许与主屏幕的分辨率和份额因子不同;您出现帧的代码应调整以匹配。
在外部显现器上绘图的进程几乎与在主屏幕上运转的进程相同。
-
在外部显现器上创立一个窗口。
-
为您的烘托策略添加恰当的视图或视图操控器方针到窗口。
- 假如运用 GLKit 进行烘托,请设置
GLKViewController
和(或您的自定义子类)的实例并运用其特点GLKView
将它们添加到窗口中。rootViewController
- 假如烘托到中心动画层,请将包括您的层的视图添加为窗口的子视图。要运用动画循环进行烘托,请经过检索
screen
窗口的特点并调用其displayLinkWithTarget:selector:
办法来创立针对外部显现器优化的显现链接方针。
- 假如运用 GLKit 进行烘托,请设置