配置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对象的两个上下文
共享组在两种特定情况下最有用:
- 当上下文之间共享的大多数资源不变时。
- 当您希望您的应用程序能够在渲染器主线程以外的其他线程上创建新的OpenGL ES对象时。在这种情况下,第二个上下文在单独的线程上运行,并专门用于获取数据和创建资源。加载资源后,第一个上下文可以绑定到该对象并立即使用它。
要创建引用同一共享组的多个上下文,可以通过调用来初始化第一个上下文initWithAPI:
。将自动为上下文创建一个共享组。通过调用initWithAPI:sharegroup:
方法,将第二个及更高版本的上下文初始化为使用第一个上下文的共享组。
注意:与同一共享组关联的所有上下文必须使用与初始上下文相同的OpenGL ES API版本。
EAGLContext* firstContext = CreateBestEAGLContext();
EAGLContext* secondContext = [[EAGLContext alloc] initWithAPI:[firstContext API] sharegroup: [firstContext sharegroup]];
更新OpenGL ES对象
- 调用
glFlush
可能正在使用该对象的每个上下文。 - 在要修改对象的上下文中,调用一个或多个OpenGL ES函数来更改对象。
- 调用
glFlush
接收到状态修改命令的上下文。 - 在其他所有上下文中,重新绑定对象标识符。
注意:共享对象的另一种方法是使用单个渲染上下文,但使用多个目标帧缓冲区。在渲染时,您的应用将绑定适当的帧缓冲区并根据需要渲染其帧。因为所有OpenGL ES对象都是从单个上下文引用的,所以它们看到的是相同的OpenGL ES数据。此模式使用较少的资源,但仅对可以仔细控制上下文状态的单线程应用程序有用。
使用OpenGL ES和GLKit绘图
GLKit提供了GLView
和GLViewController
从而消除了绘制和设置OpenGL ES内容动画所需的设置和维护代码。GLView
类管理OpenGL ES的基础设施,为您的绘制代码提供一个场所。GLKViewController
类提供了在GLKit视图的OpenGL ES内容的流畅的动画渲染循环。
像标准的UIKit视图一样,GLKit视图可按需呈现其内容。首次显示视图时,它会调用您的绘制方法-Core Animation
会缓存渲染的输出,并在显示视图时将其显示。
当您想要更改视图的内容时,请调用其setNeedsDisplay
方法,然后该视图再次调用您的绘图方法,将生成的图像缓存并显示在屏幕上。当用于渲染图像的数据很少或仅响应用户操作而变化时,此方法很有用。通过仅在需要时渲染新的视图内容,可以节省设备的电池电量,并为设备留出更多时间执行其他操作。
使用GLKit视图渲染OpenGL ES内容
创建和配置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:方法中,它将那些矩阵提供给着色器程序,并提交绘制命令以渲染行星几何体。