OpenGL ES 框架详细解析(五) —— 使用OpenGL ES和GLKit进行绘制

版本记录

版本号 时间
V1.0 2017.09.28

前言

OpenGL ES是一个强大的图形库,是跨平台的图形API,属于OpenGL的一个简化版本。iOS系统可以利用OpenGL ES将图像数据直接送入到GPU进行渲染,这样避免了从CPU进行计算再送到显卡渲染带来的性能的高消耗,能带来来更好的视频效果和用户体验。接下来几篇就介绍下iOS 系统的 OpenGL ES框架。感兴趣的可以看上面几篇。
1. OpenGL ES 框架详细解析(一) —— 基本概览
2. OpenGL ES 框架详细解析(二) —— 关于OpenGL ES
3. OpenGL ES 框架详细解析(三) —— 构建用于iOS的OpenGL ES应用程序的清单
4. OpenGL ES 框架详细解析(四) —— 配置OpenGL ES的上下文

Drawing with OpenGL ES and GLKit - 使用OpenGL ES和GLKit进行绘制

GLKit框架提供了视图和视图控制器类,可以消除绘图和动画化OpenGL ES内容所需的设置和维护代码。 GLKView类管理OpenGL ES基础结构为您的绘图代码提供了一个地方,GLKViewController类为GLKit视图中OpenGL ES内容的平滑动画提供了一个渲染循环。 这些类扩展了用于绘制视图内容和管理视图呈现的标准UIKit设计模式。 因此,您可以将重点放在OpenGL ES渲染代码上,并使您的应用快速运行。 GLKit框架还提供了其他功能来简化OpenGL ES 2.0和3.0开发。


A GLKit View Draws OpenGL ES Content on Demand - 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方法,然后视图再次调用绘图方法,缓存生成的图像,并将其显示在屏幕上。 当用于渲染图像的数据不经常更改或仅响应于用户操作时,此方法非常有用。 通过仅在需要时才提供新的视图内容,您可以节省设备上的电池电量,并为设备执行其他操作留出更多时间。

Rendering OpenGL ES content with a GLKit view

1. Creating and Configuring a GLKit View - 创建和配置GLKit视图

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

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

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

// 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应用程序的图像质量 - 如果启用多采样,则始终测试应用程序的性能,以确保其仍然可以接受。

2. Drawing With a GLKit View - 利用GLKit视图进行绘制

上面的代码概述了绘制OpenGL ES内容的三个步骤:准备OpenGL ES基础设施,发布绘图命令,并将呈现的内容呈现给Core Animation进行显示。 GLKView类实现了第一和第三步。 对于第二步,您将实现一个绘图方法,如下面的代码所示。

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

  • 在调用绘图方法之前,该视图:

    • 使其EAGLContext对象成为当前上下文
    • 根据当前大小,比例因子和可绘制属性(如果需要)创 建一个framebuffer对象和renderbuffers
    • 将framebuffer对象绑定为绘制命令的当前目标
    • 设置OpenGL ES视口以匹配帧缓冲区大小
  • 在您的绘图方法返回后,视图:

    • 解决多采样缓冲区(如果启用多重采样)
    • 丢弃其内容不再需要的renderbuffers
    • 向Core Animation呈现renderbuffer内容进行缓存和显示

Rendering Using a Delegate Object - 使用代理对象进行渲染

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

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

// 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;

}

A GLKit View Controller Animates OpenGL ES Content - GLKit视图控制器动画化OpenGL ES内容

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

The animation loop

1. Understanding the Animation Loop - 了解动画循环

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

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

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

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

2. Using a GLKit View Controller - 使用GLKit控制器

下面代码演示了使用GLKViewController子类和GLKView实例渲染动画OpenGL ES内容的典型策略。

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

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


Using GLKit to Develop Your Renderer - 使用GLKit开发您的渲染

除了查看和查看控制器基础外,GLKit框架还提供了几个其他功能来简化iOS上的OpenGL ES开发。

1. Handling Vector and Matrix Math - 处理向量和矩阵数学

OpenGL ES 2.0及更高版本不提供用于创建或指定变换矩阵的内置函数。 相反,可编程着色器提供顶点变换,并使用通用的均匀变量指定着色器输入。 GLKit框架包括矢量和矩阵类型和功能的综合库,针对iOS硬件上的高性能进行了优化。 (见GLKit Framework Reference。)

2. Migrating from the OpenGL ES 1.1 Fixed-Function Pipeline - 从OpenGL ES 1.1固定功能管道迁移

OpenGL ES 2.0及更高版本删除与OpenGL ES 1.1固定功能图形管道相关联的所有功能。GLKBaseEffect类为OpenGL ES 1.1管道的转换,照明和阴影阶段提供了Objective-C模拟,GLKSkyboxEffectGLKReflectionMapEffect类增加了对常见视觉效果的支持。 有关详细信息,请参阅这些类的参考文档。

3. Loading Texture Data - 加载纹理数据

GLKTextureLoader类提供了一种简单的方法,可将纹理数据从iOS支持的任何图像格式加载到OpenGL ES上下文中,同步或异步。 (请参阅Use the GLKit Framework to Load Texture Data。)

后记

未完,待续~~~

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

推荐阅读更多精彩内容