OpenGL ES GLKit初探

配置OpenGL ES上下文

OpenGL ES的每种实现都提供一种创建渲染上下文的方法,以管理OpenGL ES规范要求的状态。通过将此状态置于上下文中,多个应用程序可以轻松共享图形硬件,而不会干扰其他状态。

在你的应用可以调用任何OpenGL ES函数之前,它必须初始化一个EAGLContext对象。本EAGLContext类还提供用于OpenGL ES的内容与核心动画结合的方法。

设置上下文

在iOS每个线程都有一个上下文,要将当前线程的上下文设置给OpenGL ES,需调用以下函数进行设置:

[EAGLContext setCurrentContext: myContext];

调用EAGLContext类中的方法currentContext以检索线程的当前上下文。

注意: 如果您的应用程序在同一线程上主动在两个或多个上下文之间切换,请glFlush在将新上下文设置为当前上下文之前调用该函数。这样可以确保将先前提交的命令及时交付给图形硬件。

创建上下文

一个EAGLContext对象仅支持OpenGL ES的一个版本。OpenGL ES 3.0可兼容 OpenGL ES 2.0和OpenGL ES1.0。但是反过来却不行。

创建一个OpenGL ES的上下文对象。

EAGLContext* CreateBestEAGLContext()
{
   EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
   if (context == nil) {
      context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
   }
   return context;
}

EAGLSharegroup

上下文保留了OpenGL ES的状态,但是并没有直接管理OpenGL ES对象。而是通过EAGLSharegroup来创建和维护OpenGL ES对象。

当多个上下文连接到一个公共共享组时,由任何上下文创建的OpenGL ES对象在所有上下文上均可用。如果您在创建该上下文的另一个上下文上绑定了相同的对象标识符,则将引用相同的 OpenGL ES对象。移动设备上的资源通常很稀缺。在多个上下文中创建相同内容的多个副本是浪费的。共享公共资源可以更好地利用设备上的可用图形资源。

共享OpenGL ES对象的两个上下文


-w590

共享组在两种特定情况下最有用:

  • 当上下文之间共享的大多数资源不变时。
  • 当您希望您的应用程序能够在渲染器主线程以外的其他线程上创建新的OpenGL ES对象时。在这种情况下,第二个上下文在单独的线程上运行,并专门用于获取数据和创建资源。加载资源后,第一个上下文可以绑定到该对象并立即使用它。

要创建引用同一共享组的多个上下文,可以通过调用来初始化第一个上下文initWithAPI:。将自动为上下文创建一个共享组。通过调用initWithAPI:sharegroup:方法,将第二个及更高版本的上下文初始化为使用第一个上下文的共享组。

注意:与同一共享组关联的所有上下文必须使用与初始上下文相同的OpenGL ES API版本。

EAGLContext* firstContext = CreateBestEAGLContext();
EAGLContext* secondContext = [[EAGLContext alloc] initWithAPI:[firstContext API] sharegroup: [firstContext sharegroup]];

更新OpenGL ES对象

  1. 调用glFlush可能正在使用该对象的每个上下文。
  2. 在要修改对象的上下文中,调用一个或多个OpenGL ES函数来更改对象。
  3. 调用glFlush接收到状态修改命令的上下文。
  4. 在其他所有上下文中,重新绑定对象标识符。

注意:共享对象的另一种方法是使用单个渲染上下文,但使用多个目标帧缓冲区。在渲染时,您的应用将绑定适当的帧缓冲区并根据需要渲染其帧。因为所有OpenGL ES对象都是从单个上下文引用的,所以它们看到的是相同的OpenGL ES数据。此模式使用较少的资源,但仅对可以仔细控制上下文状态的单线程应用程序有用。

使用OpenGL ES和GLKit绘图

GLKit提供了GLViewGLViewController从而消除了绘制和设置OpenGL ES内容动画所需的设置和维护代码。GLView类管理OpenGL ES的基础设施,为您的绘制代码提供一个场所。GLKViewController类提供了在GLKit视图的OpenGL ES内容的流畅的动画渲染循环。

像标准的UIKit视图一样,GLKit视图可按需呈现其内容。首次显示视图时,它会调用您的绘制方法-Core Animation会缓存渲染的输出,并在显示视图时将其显示。

当您想要更改视图的内容时,请调用其setNeedsDisplay方法,然后该视图再次调用您的绘图方法,将生成的图像缓存并显示在屏幕上。当用于渲染图像的数据很少或仅响应用户操作而变化时,此方法很有用。通过仅在需要时渲染新的视图内容,可以节省设备的电池电量,并为设备留出更多时间执行其他操作。

使用GLKit视图渲染OpenGL ES内容

-w717

创建和配置GLKit视图

您可以GLKView以编程方式或使用Interface Builder 创建和配置对象。在将其用于绘图之前,必须将其与EAGLContext对象关联。

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

GLKit视图会自动创建并配置自己的OpenGL ES帧缓冲区对象和渲染缓冲区。您可以使用视图的drawable属性控制这些对象的属性。如果更改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视图进行绘图

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进行缓存和显示。

使用GLKit ViewController

下面的代码演示了使用GLKViewController子类和GLKView实例渲染动画OpenGL ES内容的典型策略。
使用GLKit视图和视图控制器绘制和设置OpenGL ES内容动画。

@implementation PlanetViewController // subclass of GLKViewController
 
- (void)viewDidLoad
{
    [super viewDidLoad];
 
    //创建一个OpenGL ES上下文,并将其分配给从storyboard加载的视图
    GLKView *view = (GLKView *)self.view;
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
 
    //设置动画帧频
    self.preferredFramesPerSecond = 60;
 
    // 加载着色器,纹理和顶点数组,设置投影矩阵
    [self setupGL];
}
 
- (void)update
{
    _rotation += self.timeSinceLastUpdate * M_PI_2; //每秒旋转四分之一
    //为旋转的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
{
    // 清除帧缓冲区
    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:方法中,它将那些矩阵提供给着色器程序,并提交绘制命令以渲染行星几何体。

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