002-GLKView是如何工作的

这篇文章是对OpenGL ES之绘制三角形(一)的补充解释, 在这篇文章里会解释苹果封装的GLKView帮我们做了哪些工作? GLKView是如何工作的?

CAEAGLLayer和GLKView之间的关系就如同UIView和CALayer之间的关系, GLKView是对CAEAGLLayer的封装, 简化了我们使用Core Animation去渲染OpenGL ES的步骤.

Core Animation 是iOS上图形渲染和动画的核心基础, OpenGL ES通过CAEAGLLayer该类连接到Core Animation ,这是一种特殊类型的Core Animation层,其内容来自OpenGL ES renderbuffer。Core Animation将renderbuffer的内容与其他图层复合,并在屏幕上显示生成的图像。

002-GLKView是如何工作的_第1张图片
Core Animation与OpenGL ES共享renderbuffer

CAEAGLLayer提供的两项主要功能,首先,它为renderbuffer分配共享存储。其次,它将渲染缓冲区呈现给Core Animation,用renderbuffer中的数据替换了以前的内容。该模型的一个优点是,只有当渲染的图像更改时,Core Animation图层的内容不需要在每个帧中绘制。

使用Core Animation 渲染OpenGL ES步骤

1. 创建CAEAGLLayer对象并配置其属性

CAEAGLLayer *eaglLayer = [[CAEAGLLayer alloc] init];
      
 //指定绘图时需要的信息
 eaglLayer.drawableProperties = 
         [NSDictionary dictionaryWithObjectsAndKeys:
             [NSNumber numberWithBool:NO], 
             kEAGLDrawablePropertyRetainedBacking,  //是否使用保留背景, 设置NO, 不保留, 这段代码是告诉Core Animation 层不要保留以前绘制的任何图像, 需要绘图时重新绘制整个层的内容
             kEAGLColorFormatRGBA8, 
             kEAGLDrawablePropertyColorFormat,  //设置颜色值保存的位数, 8位
             nil];
      
  1. 为获得最佳性能,请将图层opaque属性的值设置为YES,
  2. 可选,通过drawableProperties为CAEAGLLayer对象的属性分配新的值字典来配置渲染表面的表面属性。您可以指定renderbuffer的像素格式,并指定在将它们发送到Core Animation之后,renderbuffer的内容是否被丢弃。
    有关允许密钥的列表, 请参阅EAGLDrawable Protocol Reference

2. 分配OpenGL ES上下文并使其成为当前上下文

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

3. 创建与绑定framebuffer对象

GLuint framebuffer;
glGenFramebuffers(1,&framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER,framebuffer);

glGenFramebuffers()需要2个参数; 第一个是要创建的帧缓冲区的数量,第二个参数是指向GLuint变量或数组以存储单个ID或多个ID的指针。
当不再使用glDeleteFramebuffers()时,FBO可能会被删除。

创建FBO后,必须先绑定FBO。
glBindFramebuffer()第一个参数target为GL_FRAMEBUFFER,第二个参数为framebuffer对象的ID。一旦绑定FBO,所有OpenGL操作都会影响当前绑定的帧缓冲区对象。对象ID 0保留给默认的窗口系统提供的帧缓冲区。因此,为了取消绑定当前帧缓冲区(FBO),请在glBindFramebuffer()中使用ID 0。

4. 创建一个彩色渲染缓冲区

通过调用上下文的renderbufferStorage:fromDrawable:方法并传递层对象作为参数来分配其存储空间。宽度,高度和像素格式取自层,用于为renderbuffer分配存储空间。

GLuint colorRenderbuffer;
//创建
glGenRenderbuffers(1,&colorRenderbuffer);
//绑定
glBindRenderbuffer(GL_RENDERBUFFER,colorRenderbuffer);

[myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:myEAGLLayer];

glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER,colorRenderbuffer);

renderbuffer对象是新引入的用于屏幕外渲染。它允许将场景直接渲染到renderbuffer对象,而不是渲染到纹理对象。Renderbuffer只是一个包含可渲染内部格式的单个映像的数据存储对象。它用于存储没有相应纹理格式的OpenGL逻辑缓冲区,例如模板或深度缓冲区。

与FBO一样, 需要先创建glGenRenderbuffers,然后绑定glBindRenderbuffer

存储
//这个是OpenGL ES里面的方法, 上面的那个是EAGLContext对象的方法
void glRenderbufferStorage(GLenum  target,
                           GLenum  internalFormat,
                           GLsizei width,
                           GLsizei height)

当创建一个renderbuffer对象时,它没有任何数据存储,所以我们必须为它分配一个内存空间。这可以通过使用glRenderbufferStorage()来完成。第一个参数必须是GL_RENDERBUFFER。第二个参数是彩色渲染(GL_RGB,GL_RGBA等),深度可渲染(GL_DEPTH_COMPONENT)或模板可渲染格式(GL_STENCIL_INDEX)。width和height是以像素为单位的renderbuffer图像的尺寸。

宽度和高度应小于GL_MAX_RENDERBUFFER_SIZE,否则会生成GL_INVALID_VALUE错误。

将图像附加到FBO

FBO本身没有任何图像存储(缓冲区)。相反,我们必须在FBO上附加framebuffer可附加的图像(纹理或renderbuffer对象)。该机制允许FBO快速切换(分离和附加)FBO中的可帧缓冲附件的图像。切换帧缓冲附加图像比在FBO之间切换要快得多。并且,它可以节省不必要的数据副本和内存消耗。例如,纹理可以附加到多个FBO,并且其图像存储可以被多个FBO共享。

将一个Renderbuffer图像附加到FBO

void glFramebufferRenderbuffer(GLenum target,
                               GLenum attachmentPoint,
                               GLenum renderbufferTarget,
                               GLuint renderbufferId)

可以通过调用glFramebufferRenderbuffer()来附加renderbuffer映像。第一个参数必须是GL_FRAMEBUFFER,第二个参数是连接纹理图像的连接点。FBO有多个颜色附加点(GL_COLOR_ATTACHMENT0,...,GL_COLOR_ATTACHMENT n),GL_DEPTH_ATTACHMENT和GL_STENCIL_ATTACHMENT。。第三个参数必须是GL_RENDERBUFFER,最后一个参数是renderbuffer对象的ID。

连接点

有两种类型的帧缓冲可附加图像; 纹理图像和renderbuffer图像。如果纹理对象的图像附加到帧缓冲区,则OpenGL会执行“渲染到纹理”。如果renderbuffer对象的图像附加到帧缓冲区,则OpenGL将执行“屏幕外渲染”。

下图显示了framebuffer对象,纹理对象和renderbuffer对象之间的连接。多个纹理对象或renderbuffer对象可以通过附件点附加到framebuffer对象。

002-GLKView是如何工作的_第2张图片
gl_fbo01.png

帧缓冲对象中 有多个颜色附加点(GL_COLOR_ATTACHMENT0,...,GL_COLOR_ATTACHMENT n),一个深度附加点(GL_DEPTH_ATTACHMENT)和一个模板附加点(GL_STENCIL_ATTACHMENT)。颜色连接点的数量取决于实现,但每个FBO必须至少具有一个颜色附加点。您可以使用GL_MAX_COLOR_ATTACHMENTS查询最大数量的颜色附加点,这是由显卡支持的。FBO具有多个颜色附加点的原因是允许在同一时间将颜色缓冲区渲染到多个目的地。这个“多个渲染目标”(MRT)可以通过GL_ARB_draw_buffers扩展来完成。

注意: 当核心动画层的边界或属性更改时,您的应用程序应重新分配renderbuffer的存储。如果不重新分配renderbuffers,renderbuffer大小将不匹配图层的大小; 在这种情况下,Core Animation可以缩放图像的内容以适应图层。

5. 检索颜色renderbuffer的高度和宽度。

GLint width;
GLint height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,GL_RENDERBUFFER_WIDTH,&width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER,GL_RENDERBUFFER_HEIGHT,&height);

6.分配并附加深度缓冲区

创建一个深度或深度/模板的渲染缓冲区,为其分配存储空间,并将其附加到framebuffer的深度附件点。

GLuint depthRenderbuffer;
glGenRenderbuffers(1,&depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER,depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT16,width,height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,depthRenderbuffer);

7.测试帧缓冲区的完整性(检查FBO状态)

一旦可连接的图像(纹理和渲染缓冲区)附加到FBO并且在执行FBO操作之前,必须使用glCheckFramebufferStatus()验证FBO状态是完整还是不完整。如果FBO不完整,则任何绘图和读取命令(glBegin(),glCopyTexImage2D()等)将失败。

GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status!= GL_FRAMEBUFFER_COMPLETE){
        NSLog(@“无法使完整的framebuffer对象%x”,状态);
}

glCheckFramebufferStatus()验证当前绑定的FBO上的所有附加图像和帧缓冲区参数。而且,这个函数不能在glBegin()/ glEnd()对中调用。目标参数应为GL_FRAMEBUFFER。检查FBO后返回非零值。如果满足所有要求和规则,则返回GL_FRAMEBUFFER_COMPLETE。否则,它返回一个相关的错误值,它告诉什么规则被违反。

8. 显示

通过将CAEAGLLayer对象传递给addSublayer:可见层的方法,将对象添加到Core Animation层次结构中。

至此, 就实现了一个自定义的GLKView.
当你想在一个视图的内容层的OpenGL ES绘制是应该使用GLKView, 而不是CAEAGLLayer

你可能感兴趣的:(002-GLKView是如何工作的)