首页 关于 微信公众号
欢迎关注我的微信公众号

OpenGL ES2入门02-基本API介绍

接下来会结合着示例程序来首先介绍一些OpenGL ES的基本API。

第一种引入方式:

  1. #import <OpenGLES/ES2/gl.h>
  2. #import <OpenGLES/ES2/glext.h>
  3. #import <QuartzCore/QuartzCore.h>

第二种引入方式(iOS7之后新增):

  1. @import OpenGLES;

OpenGLView.h:

  1. #import <UIKit/UIKit.h>
  2. @import OpenGLES;
  3. NS_ASSUME_NONNULL_BEGIN
  4. @interface OpenGLView : UIView
  5. {
  6. CAEAGLLayer *_eaglLayer;
  7. EAGLContext *_context;
  8. GLuint _renderBuffer;
  9. GLuint _frameBuffer;
  10. }
  11. @end
  12. NS_ASSUME_NONNULL_END

CAEAGLLayer 实现了 EAGLDrawable 协议,它是 Apple 专门为 OpenGL ES 准备的一个图层类。

所以想要显示 OpenGL ES 的内容,需要把它默认的 layer 设置为一个特殊的 layer(CAEAGLLayer),我们简单的重写 layerClass 即可:

  1. + (Class)layerClass
  2. {
  3. return [CAEAGLLayer class];
  4. }

另外,为了方便起见,我们使 _eaglLayer 这个成员变量指代 self.layer,这样除了调用上方便外,可读性也更好。

  1. - (void)setupLayer {
  2. // 用于显示的layer
  3. _eaglLayer = (CAEAGLLayer *)self.layer;
  4. // CALayer 默认是透明的(opaque = NO),而透明的层对性能负荷很大。所以将其关闭。
  5. _eaglLayer.opaque = YES;
  6. }

EAGLContext 对象,这个 context 管理所有使用 OpenGL ES 进行渲染的状态,命令以及资源信息。

通过 initWithAPI 创建完 context,然后需要使用 setCurrentContext 将它设置为当前 context,因为我们之前提过,context 可以同时存在多个,需要指定当前环境对应的 context。

  1. - (void)setupContext
  2. {
  3. if (!_context) {
  4. _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
  5. }
  6. NSAssert(_context && [EAGLContext setCurrentContext:_context],@"Failed to initialize OpenGLES 2.0 context");
  7. }

有了上下文,OpenGL ES 还需要在一块 buffer 上进行渲染,这块 buffer 就是 Renderbuffer(OpenGL ES 总共有三大不同用途的 buffer,分别是 color buffer,depth buffer 和 stencil buffer,这里是最基本的 color buffer)。可以简单的把 renderbuffer 理解成用于展示的窗口。

创建Renderbuffer:

  1. - (void)setupRenderBuffer
  2. {
  3. // 生成 renderbuffer ( renderbuffer = 用于展示的窗口 )
  4. glGenRenderbuffers(1, &_renderBuffer);
  5. // 绑定 renderbuffer
  6. glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
  7. // GL_RENDERBUFFER 的内容存储到实现 EAGLDrawable 协议的 CAEAGLLayer
  8. [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
  9. }

glGenRenderbuffers方法

glGenRenderbuffers 用于生成 renderbuffer,并分配 id。它的原型为:

  1. GL_API void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers);

返回的 id 不会为 0,0 是OpenGL ES 保留的,0 则表示这个 buffer 这个不存在或者创建失败。

所以,一般会通过 id 来判断某个 buffer 是否存在,执行对应的操作。比如在 gen 之前,释放旧的 renderbuffer,确保之后的操作无误。

  1. // 释放旧的 renderbuffer
  2. if (_renderbuffer) {
  3. glDeleteRenderbuffers(1, &_renderbuffer);
  4. _renderbuffer = 0;
  5. }

glBindRenderbuffer方法

glBindRenderbuffer 用于绑定 renderbuffer,将指定 id 的 renderbuffer 设置为当前 renderbuffer。它的原型为:

  1. GL_API void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer);

renderbufferStorage方法

renderbufferStorage 用于将 GL_RENDERBUFFER 的内容存储到实现 EAGLDrawable 协议的 CAEAGLLayer。它的原型为:

  1. /* Attaches an EAGLDrawable as storage for the OpenGL ES renderbuffer object bound to <target> */
  2. - (BOOL)renderbufferStorage:(NSUInteger)target fromDrawable:(nullable id<EAGLDrawable>)drawable;

这个函数内部,会使用 drawable(_eaglLayer)的相关信息(设置存储在 drawableProperties 属性中)作为参数,调用 glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); glRenderbufferStorage 指定存储在 renderbuffer 中图像的宽高以及颜色格式,并按照此规格为之分配存储空间。

接下去我们将会创建 framebuffer object,它通常也被称之为 FBO。我们之前提到过了,它相当于 buffer(color, depth, stencil)的管理者,三大 buffer 可以附加到一个 FBO 上。

它的创建过程如下:

  1. - (void)setupFrameBuffer
  2. {
  3. // 生成 framebuffer ( framebuffer = 画布 )
  4. glGenFramebuffers(1, &_frameBuffer);
  5. // 绑定 fraembuffer
  6. glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
  7. // framebuffer 不对渲染的内容做存储, 所以这一步是将 framebuffer 绑定到 renderbuffer ( 渲染的结果就存在 renderbuffer )
  8. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  9. GL_RENDERBUFFER, _renderBuffer);
  10. }

之前的 gen,bin 操作和 renderbuffer 中对应的都是一致的,只是做相应的替换,比如 renderbuffer 改成 framebuffer 即可,这里就不细说,重点看一下 glFramebufferRenderbuffer。

之前说过,framebuffer 不对渲染的内容做存储,而 glFramebufferRenderbuffer 的作用正是将相关的 buffer(三大 buffer 之一)装配到 framebuffer 上,使得 framebuffer 能索引到对应的渲染内容。它的原型为:

  1. GL_API void GL_APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
  1. - (BOOL)checkFramebuffer:(NSError *__autoreleasing *)error {
  2. // 检查 framebuffer 是否创建成功
  3. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  4. NSString *errorMessage = nil;
  5. BOOL result = NO;
  6. switch (status)
  7. {
  8. case GL_FRAMEBUFFER_UNSUPPORTED:
  9. errorMessage = @"framebuffer不支持该格式";
  10. result = NO;
  11. break;
  12. case GL_FRAMEBUFFER_COMPLETE:
  13. NSLog(@"framebuffer 创建成功");
  14. result = YES;
  15. break;
  16. case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
  17. errorMessage = @"Framebuffer不完整 缺失组件";
  18. result = NO;
  19. break;
  20. case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
  21. errorMessage = @"Framebuffer 不完整, 附加图片必须要指定大小";
  22. result = NO;
  23. break;
  24. default:
  25. // 一般是超出GL纹理的最大限制
  26. errorMessage = @"未知错误 error !!!!";
  27. result = NO;
  28. break;
  29. }
  30. NSLog(@"%@",errorMessage ? errorMessage : @"");
  31. *error = errorMessage ? [NSError errorWithDomain:@"com.error"
  32. code:status
  33. userInfo:@{@"ErrorMessage" : errorMessage}] : nil;
  34. return result;
  35. }
  1. - (void)render
  2. {
  3. glClearColor(220.0/255.0, 20.0/255.0, 60.0/255.0, 1.0);
  4. glClear(GL_COLOR_BUFFER_BIT);
  5. // 做完所有绘制操作后,最终呈现到屏幕上
  6. [_context presentRenderbuffer:GL_RENDERBUFFER];
  7. }

glClearColor方法

glClearColor 用来设置清屏颜色,它的函数原型:

  1. GL_API void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

glClear方法

glClear (GLbitfield mask) 用来指定要用清屏颜色来清除由 mask 指定的 buffer,mask 可以是 GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT 和 GL_STENCIL_BUFFER_BIT 的自由组合。

在这里我们只使用到 color buffer,所以清除的就是 clolor buffer。

Blog

Opinion

Project