(Apple Documentation) OpenGL ES Programming Guide - Drawing with OpenGL ES and 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 方法,然后视图会再次调用绘图方法,缓存生成的图像,并将其显示在屏幕上。 当用于渲染图像的数据不经常更改或仅仅只是响应用户的操作时,这个方法很有用。 通过这种仅在需要时呈现新的视图内容的方式,可以节省设备上的电池电量并为设备留出更多时间来执行其他操作。

Figure 3-1 Rendering OpenGL ES content with a GLKit view

(Apple Documentation) OpenGL ES Programming Guide - Drawing with OpenGL ES and GLKit(译)_第1张图片

GLKit View的创建与配置

你可以通过编程或nterface Builder的方式创建和配置GLKView对象。 在将其用于绘图之前,必须将其与 EAGLContext 对象关联(请参阅 Configuring OpenGL ES Contexts)。

  • 以编程方式创建视图时,首先创建一个上下文,然后将其传递给视图的 initWithFrame:context: 方法。
  • 从故事板加载视图后,创建上下文并将其设置为视图 context 属性的值。

GLKit视图会自动创建和配置自己的OpenGL ES帧缓冲对象和渲染缓冲区。 您可以使用视图的drawable属性控制这些对象的属性,如3-1所示。 如果更改了GLKit视图的大小、缩放比例或其他drawable属性,它会在下次绘制其内容时自动删除并重新创建相应的帧缓冲区对象和渲染缓冲区。

Listing 3-1 Configuring a GLKit view

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    // Create an OpenGL ES context and assign it to the view loaded from storyboard
    GLKView *view = (GLKView *)self.view;
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
 
    // Configure renderbuffers created by the view
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
 
    // Enable multisampling
    view.drawableMultisample = GLKViewDrawableMultisample4X;
}

您可以使用 drawableMultisample 属性为 GLKView 实例启用多重采样。 多重采样是一种抗锯齿形式,可以使锯齿状边缘平滑,能够提高大多数3D应用程序的图像质量,但代价是这将会使用更多的内存和片段处理时间,请始终测试应用程序的性能以确保其可接受。

使用 GLKit 视图进行绘制

Figure 3-1中概述了绘制OpenGL ES内容的三个步骤:准备OpenGL ES基础结构,发布绘图命令,以及将已渲染的内容呈递给Core Animation以供显示。 GLKView 类实现第一步和第三步。 对于第二步,您需要实现一个类似于Listing 3-2中的绘图方法。

Listing 3-2 Example drawing method for a GLKit view
- (void)drawRect:(CGRect)rect
{
    // Clear the framebuffer
    glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
    // Draw using previously configured texture, shader, uniforms, and vertex array
    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渲染过程那些标准化的部分:

  • 在回调绘制方法前,GLKView 完成了下面这些操作:
    - 将 EAGLContext 对象设置为当前上下文
    - 根据当前尺寸、缩放比例以及drawable属性创建帧缓冲对象和渲染缓冲区。
    - 将帧缓冲对象绑定为当前绘图命令的目标
    - 将 OpenGL ES 视口设置为帧缓冲的大小

    • 绘制方法执行完成后:
      • 处理多重采样缓冲区(如果启用了多重采样)
      • 清理那些不再需要其内容的渲染缓冲区
      • 将渲染缓冲区的内容递交给Core Animation进行缓存和显示

使用代理对象进行渲染

许多OpenGL ES应用程序在自定义的类中实现渲染代码。 这种方法的优点是它允许你通过为每个算法定义不同的渲染器类来轻松的支持多种渲染算法。 共享通用功能的渲染算法可以从超类继承。 例如,你可以使用不同的渲染器类来支持OpenGL ES 2.0和3.0(参阅:Configuring OpenGL ES Contexts), 你也可以使用不同的渲染器类为更强大硬件设备获得更好的图像质量。

GLKit非常适合这种方式: 您可以使渲染器对象成为 GLKView 实例的代理。 这样,您的渲染器类只要实现 GLKViewDelegate 协议的 glkView:drawInRect: 方法,而不需要继承 GLKView 并实现 drawRect:。 Listing 3-3 演示了如何在应用程序启动时根据硬件功能选择渲染器类。

Listing 3-3 Choosing a renderer class based on hardware features
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Create a context so we can test for features
    EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    [EAGLContext setCurrentContext:context];
 
    // Choose a rendering class based on device features
    GLint maxTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
    if (maxTextureSize > 2048)
        self.renderer = [[MyBigTextureRenderer alloc] initWithContext:context];
    else
        self.renderer = [[MyRenderer alloc] initWithContext:context];
 
    // Make the renderer the delegate for the view loaded from the main storyboard
    GLKView *view = (GLKView *)self.window.rootViewController.view;
    view.delegate = self.renderer;
 
    // Give the OpenGL ES context to the view so it can draw
    view.context = context;
 
    return YES;
}

默认情况下,GLKView 对象会按需要渲染内容。 也就是说,使用OpenGL ES绘制的一个关键优势是它能够使用图形处理硬件来处理连续复杂场景的动画 - 比如游戏和模拟类的应用,它们很少只渲染静态图像。 对于这些情况,GLKit框架提供了一个视图控制器类,它为它管理的 GLKView 对象的维护着一个动画循环。 这个循环遵循了游戏和模拟中常见的设计模式,有两个阶段:更新和显示。 图3-2显示了动画循环的简化示例。

Figure 3-2 The animation loop

(Apple Documentation) OpenGL ES Programming Guide - Drawing with OpenGL ES and GLKit(译)_第2张图片

理解动画循环

在更新阶段,视图控制器会调用自己的更新方法(或代理对象的 glkViewControllerUpdate: 方法)。 在此方法中,您应该为绘制下一帧做准备。 例如,游戏中可以使用该方法通过获取自上一帧以来接收的输入事件来确定玩家和敌人角色的位置,and a scientific visualization might use this method to run a step of its simulation(译者注:不会翻了)。 如果需要时序信息来确定应用程序的下一帧状态,请使用视图控制器的时序属性,例如 timeSinceLastUpdate 属性。 在图3-2中,更新阶段递增角度并使用它来计算变换矩阵。

在显示阶段,视图控制器调用其视图的显示方法,该方法接着会调用绘图 display 方法。 在绘图方法中,您向GPU提交OpenGL ES绘图命令以渲染您的内容。 为获得最佳性能,您的应用应该在新的一帧开始渲染的时修改OpenGL ES对象,然后提交绘图命令。 在图3-2中,显示阶段将着色器程序中的统一变量设置为在更新阶段计算的矩阵,然后提交绘图命令以渲染新内容。

动画循环会按视图控制器的 framesPerSecond 属性指示的速率在这两个阶段之间交替。 您可以使用 preferredFramesPerSecond 属性设置期望的帧速率 - 以优化当前显示硬件的性能,视图控制器会自动选择接近您的设置的最佳帧速率。

重要提示:为获得最佳效果,请为应用设置一个始终不变帧速率。 平滑、一致的帧速率比不同的帧速率有更好的用户体验。

使用GLKit View Controller

Listing 3-4 演示了使用 GLKViewController 子类和 GLKView 实例渲染OpenGL ES内容的典型方案。

Listing 3-4 Using a GLKit view and view controller to draw and animate OpenGL ES content
@implementation PlanetViewController // subclass of GLKViewController
 
- (void)viewDidLoad
{
    [super viewDidLoad];
 
    // Create an OpenGL ES context and assign it to the view loaded from storyboard
    GLKView *view = (GLKView *)self.view;
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
 
    // Set animation frame rate
    self.preferredFramesPerSecond = 60;
 
    // Not shown: load shaders, textures and vertex arrays, set up projection matrix
    [self setupGL];
}
 
- (void)update
{
    _rotation += self.timeSinceLastUpdate * M_PI_2; // one quarter rotation per second
 
    // Set up transform matrices for the rotating planet
    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
{
    // Clear the framebuffer
    glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
    // Set shader uniforms to values calculated in -update
    glUseProgram(_diffuseShading);
    glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m);
    glUniformMatrix3fv(_uniformNormalMatrix, 1, 0, _normalMatrix.m);
 
    // Draw using previously configured texture and vertex array
    glBindTexture(GL_TEXTURE_2D, _planetTexture);
    glBindVertexArrayOES(_planetMesh);
    glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT, 0);
}
 
@end

在这个示例中,PlanetViewController类(自定义 GLKViewController 子类)的实例从storyboard加载,同时会创建标准 GLKView 的实例及其可绘制属性。 viewDidLoad方法中创建了OpenGL ES上下文并将其提供给视图,同时还设置可动画循环的帧率。

视图控制器自动成为其视图的委托,因此它实现了动画循环的更新和显示阶段。 在更新方法中,它计算显示旋转行星所需的变换矩阵。 在glkView:drawInRect:方法中,它将这些矩阵提供给着色器程序并提交绘图命令以渲染行星几何体。

使用GLKit开发你自己的渲染器

除了视图和视图控制器这些基础功能外,GLKit框架还提供了一些其他可以简化iOS上OpenGL ES开发的功能。

处理矢量和数学矩阵

OpenGL ES 2.0及更高版本不提供用于创建或指定矩阵变换的内置函数。 相反,可编程着色器提供了顶点变换,您可以使用通用统一变量指定着色器输入。 GLKit框架包含了一个全面的矢量和矩阵类型和函数库,它针对iOS硬件进行了优化以获得更高的性能。 (参见 GLKit Framework Reference )

从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上下文中。(请参阅 Use the GLKit Framework to Load Texture Data)

你可能感兴趣的:(文章翻译)