OpenGL ES for iOS - 3

绘制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类提供与标准UIView绘图周期相当的OpenGL ES。 UIView实例自动配置其图形上下文,以便您的drawRect:实现只需要执行Quartz 2D绘图命令,并且GLKView实例自动配置,以便您的绘图方法只需执行OpenGL ES绘图命令。 GLKView类通过维护保存OpenGL ES绘图命令结果的framebuffer对象来提供此功能,然后在绘图方法返回后自动将其显示给Core Animation。

像标准的UIKit视图一样,GLKit视图根据需要呈现其内容。当您的视图第一次显示时,它会调用您的绘图方法 - Core Animation缓存渲染的输出,并在显示视图时显示它。当您想要更改视图的内容时,请调用其setNeedsDisplay方法,再次调用绘图方法,缓存生成的图像,并将其显示在屏幕上。当用于渲染图像的数据不经常更改或仅响应于用户操作时,此方法非常有用。通过仅在需要时才提供新的视图内容,您可以节省设备上的电池电量,并为设备执行其他操作留出更多时间


3-1.png

创建和配置GLKit视图

您可以以编程方式或使用Interface Builder创建和配置GLKView对象。在使用它绘制之前,必须将其与EAGLContext对象相关联(请参阅配置OpenGL ES上下文)。

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

GLKit视图会自动创建和配置自己的OpenGL ES framebuffer对象和renderbuffers。您可以使用视图的可绘制属性来控制这些对象的属性,如清单3-1所示。如果更改GLKit视图的大小,比例因子或可绘制属性,则会在下次绘制内容时自动删除并重新创建相应的framebuffer对象和renderbuffers。
Listing3-1

- (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视图

图3-1概述了绘制OpenGL ES内容的三个步骤:准备OpenGL ES基础设施,发布绘图命令,并将呈现的内容呈现给Core Animation进行显示。 GLKView类实现了第一和第三步。对于第二步,您将实现一个绘图方法,如清单3-2中的示例所示。
Listing3-2

- (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渲染过程的标准部分

  • 在调用绘图方法之前,该视图:
  • 使其EAGLContext对象成为当前上下文
  • 根据当前大小,比例因子和可绘制属性(如果需要)创建一个framebuffer对象和renderbuffers
  • 将framebuffer对象绑定为绘制命令的当前目标
  • 设置OpenGL ES视口以匹配帧缓冲区大小
  • 在您的绘图方法返回后,视图:
  • 解决多采样缓冲区(如果启用了多次采样)
  • 丢弃其内容不再需要的renderbuffers
  • 向Core Animation呈现renderbuffer内容以进行缓存和显示

使用委托对象呈现

许多OpenGL ES应用程序在自定义类中实现渲染代码。这种方法的优点在于它允许您通过为每个渲染算法定义不同的渲染器类来轻松支持多种渲染算法。共享公共功能的渲染算法可以从超类继承。例如,您可以使用不同的渲染器类来支持OpenGL ES 2.0和3.0(请参阅配置OpenGL ES上下文)。或者您可以使用它们来定制渲染,从而在具有更强大硬件的设备上获得更好的图像质量

GLKit非常适合这种方法 - 您可以使您的渲染器对象成为标准GLKView实例的委托。您的渲染器类不是将GLKView子类化并实现drawRect:方法,而是使用GLKViewDelegate协议并实现glkView:drawInRect:方法。程序清单3-3演示了在应用程序启动时基于硬件功能选择渲染器类
Listing3-3

- (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;
}

GLKit视图控制器动画化OpenGL ES内容

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

3-2.png

了解动画循环
对于更新阶段,视图控制器调用其自己的更新方法(或其委托的glkViewControllerUpdate:方法)。在这种方法中,你应该准备绘制下一帧。例如,游戏可能会使用这种方法根据自最后一帧以来接收到的输入事件来确定玩家和敌人角色的位置,科学可视化可能会使用此方法来运行其模拟步骤。如果您需要时间信息来确定应用的下一帧的状态,请使用其中一个视图控制器的时间属性,如timeSinceLastUpdate属性。在图3-2中,更新阶段增加一个角度变量,并使用它来计算变换矩阵。

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

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

重要提示:为获得最佳效果,请选择应用程序可以始终如一地实现的帧率平滑,一致的帧速率产生比不规则变化的帧速率更愉快的用户体验。

使用GLKit视图控制器

清单3-4演示了使用GLKViewController子类和GLKView实例渲染动画OpenGL ES内容的典型策略。

@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子类)的实例从故事板加载,以及标准GLKView实例及其可绘制属性。 viewDidLoad方法创建一个OpenGL ES上下文并将其提供给视图,并且还设置动画循环的帧速率。

视图控制器自动地代表其视图,因此它实现了动画循环的更新和显示阶段。在更新方法中,它计算显示旋转行星所需的变换矩阵。在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上下文中,同步或异步。 (请参阅使用GLKit框架加载纹理数据。)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容