【OpenGL_ES】Working with OpenGL ES Contexts and Framebuffers

概要

任何一个OpenGL的 implementation 都会提供一个针对平台的函数库,用来创建和操作rendering context 。 而 rendering context 就维护着所有OpenGL ES 状态变量的一份拷贝,并且接受和执行 OpenGL 命令。

在iPhone OS 中, EAGL就是这样的函数库。

EAGLContext 是一个 rendering context 类,它负责执行 OpenGL ES 命令、与Core Animation 进行交互并向用户呈现最终结果。

EAGLSharegroup 使得多个 rendering context 能够共用 OpenGL ES 对象。 iPhone OS 中可以用它来节省内存和其他珍贵资源。

生成 EAGLContext 对象

要使用任何 OpenGL ES 命令,应用程序必须首先生成并初始化一个 EAGLContext ,并将它设置成当前的 context 。

EAGLContext *myContext = [[EAGLContext alloc]

initWithAPI:kEAGLRenderingAPIOpenGLES1];

[EAGLContext setCurrentContext: myContext];

应用程序在初始化 context 的同时决定使用哪个版本的 OpenGL ES 。 (1.1 还是 2.0 还是 两者)

程序中的每一个线程都包含一个指向当前context 的指针,并将这个 context 设置成渲染命令的 target 。

大多数情况下没必要生成多个 context , 通常可以用一个 context 和 一个 framebuffer 来完成所有渲染。

生成 Framebuffer 对象

概述

尽管一个 context 接受 OpenGL 命令,但它却不是这些命令的最终目的地。 程序提供了一个渲染像素的场地。 在iPhone OS 中,所有的画面都在 framebuffer 中渲染。

所有 OpenGL ES 2.0 的实现都提供了 framebuffer 对象, 而Apple 在所有 OpenGL ES 1.1 的实现中通过 GL_OES_framebuffer_object 扩展提供了 framebuffer 。

framebuffer 允许程序精确控制 color target , depth target , stencil target 的创建。 这些 target 通常叫做 renderbuffer ,它们只是一系列定义了宽、高、格式的像素构成的 2D 图片 。

生成 framebuffer 的步骤

生成一个 framebuffer 对象

生成一个或若干个 target (也就是renderbuffer 或者 texture), 为它们申请空间,并将它们关联到 framebuffer 对象。

检测 framebuffer 的完整性。

Offscreen Framebuffer 对象

Offscreen framebuffer 用 renderbuffer 来保管渲染的画面。

创建一个完整的 offscreen framebuffer 对象的步骤

生成一个 framebuffer 并将其绑定,这样以后的 framebuffer 命令就会交给它。

生成一个 color renderbuffer , 将其关联到 framebuffer 。

生成一个 depth renderbuffer ,将其关联到 framebuffer 。 (方法同上)

测试 framebuffer 的完整性。

(将 framebuffer)描画到屏幕

offscreen targets 还不能将它们的像素表示到屏幕上。还需要与Core Animation 交互才行。

iPhone OS 中所有的 UIView 对象的后台都有一个 Core Animation 层。 程序想要在屏幕上输出 OpenGL ES 内容,就需要一个UIView 作为 目标(target) , 然后这个 UIView 又进一步需要一个 Core Animation 层,也就是一个CAEAGLLayer 对象。 CAEAGLLayer 对象可以识别 OpenGL ES , 并访问 renderbuffer 。

上述工作已经由 Xcode 的 OpenGL ES 程序模板代为完成了(注①)。 如果要手动实现,可以按照如下步骤:

派生一个 UIView 类 ,并设置好一个 view 用于程序输出。 (和普通 UIView 程序一样)

重写 UIView 类的 layerClass 方法, 将普通的 CALayer 对象换成支持 OpenGL ES 的 CAEAGLLayer 对象。

通过调用UIView 的 layer 方法获取 CAEAGLLayer 对象。

设置 CAEAGLLayer 层对象的属性。

设置CAEAGLLayer 对象的 drawableProperties 属性来调整渲染面。(可选)

像之前一样创建 framebuffer 。

创建 color renderbuffer , 调用 rendering context 对象来分派 Core Animation layer 上的存储空间 。 (什么意思??) 代码: [myContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:myEAGLLayer];

如果Core Animation 层的属性改变了,程序就要重新调用renderbufferStorage 来分派 renderbuffer 。 如果不这样做就会导致已经渲染的画面被拉伸或变形后显示,继而导致严重的性能下降。  比如在模板程序中,每次 CAEAGLLayer 对象的绑定改变时 framebuffer 和 renderbuffer 对象都会被废弃并重新创建。

获取 renderbuffer 的宽和高

分派并关联 depth buffer 。

测试 framebuffer 对象完整性。

总结起来,三种情况下创建 framebuffer 对象的步骤都是差不多的 , 区别只在于你怎样分派关联到 framebuffer 的颜色关联点( color attachment point )上的那个对象。

offscreen renderbuffer : glRenderbufferStorageOES

Drawable renderbuffer : renderbufferStorage

Texture : glFramebufferTexture2DOES

描画一个 framebuffer 对象

一旦分派了 framebuffer 对象,你就可以对它进行渲染。所有的渲染的目标都是当前绑定的 framebuffer 。

glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);

显示结果

假如你分派了一个指向 Core Animation Layer 的 color renderbuffer ,要显示它的内容就需要将它设置成为当前的 renderbuffer ,然后调用 rendering context 的 presentRenderbuffer 方法。

默认情况下 renderbuffer 的内容在显示之后是不再进行验证的,所以每次在描画一帧的时候你必须完全重建 renderbuffer 的内容。

如果要保存帧与帧之间 renderbuffer 的内容,你需要设置 CAEAGLLayer 对象的 drawableProberties (字典类型)属性 , 给它添加一个叫做 kEAGLDrawablePropertyRetainedBacking 的键值 。 但这样做会增加内存消耗,降低程序性能。

只要 renderbuffer 被显示到屏幕, 它就一定被 animated 并且和其他屏幕上可见的 Core Animation layer 混合, 不管这些 Core Animation Layer 是由 OpenGL ES 、Quartz 、还是其他什么图形库来描画的。  将 OpenGL ES 描画的内容与其他内容混合会造成性能损失,所以你的应用程序最好只依赖 OpenGL ES 来渲染内容。 具体的做法是,创建一个和屏幕同样尺寸的 CAEAGLLayer 对象,将它的不透明(opaque)属性设置为 YES ,并确保没有其他的 Core Animation Layer 或者 view 是可见的。

关于 OpenGL ES 内容和其他内容混合的论述 (省略) 。

Sharegroups

EAGLSharegroup 对象管理关联到一个或更多 EAGLContext 对象上的资源。 它经常在创建一个EAGLContext 对象时同时被创建,然后在最后一个 EAGLContext 被销毁后一起消亡。 它是一个“不透明”对象,所以没有什么提供给开发者用的API。

要创建共用同一个 sharegroup 的若干个 context , 首先要创建一个 context , 然后使用 initWithAPI:sharegroup: 方法来创建其他的 context 。 例子:

EAGLContext* firstContext = [[EAGLContext alloc]

initWithAPI:kEAGLRenderingAPIOpenGLES1];

EAGLContext* secondContext = [[EAGLContext alloc]

initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup: [firstContext sharegroup]];

sharegroup 保管着 texture 、buffer 、framebuffer 、renderbuffer 等资源。 在从多个 context 对象中同时访问这些资源时,你的程序就有责任管理这些共有对象的状态变化。 一旦改变了一个正在其他 context 中被渲染的共有对象的状态,结果将不可预测。为了得到确切的结果,程序必须确保在修改一个共有对象时没有其他 context 在使用它。 进一步说, 无法保证其他的 context 对象在重新绑定共享对象之前注意到它的状态变化。

为了保证在修改通过 sharegroup 共享的对象的状态后能够得到正确的结果,你的程序必须依次执行以下步骤:

改变一个对象的状态

调用发出状态修改动作的那个 context 的 glFlush 方法

每一个 context 必须重新绑定该对象以发现它的状态改变。

当 sharegroup 中所有 context 都绑定了新对象后,原来的对象即被销毁。

注释

注①  : 如果在 Xcode 中创建新项目时指定了OpenGL ES 模板,

那么该项目会用一个EAGLView 类来派生 UIView ,

它的 layerClass  方法返回一个支持 OpenGL ES 的 CAEAGLLayer 对象。

你可能感兴趣的:(【OpenGL_ES】Working with OpenGL ES Contexts and Framebuffers)