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

OpenGL ES2入门02-基本API介绍

概述

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

示例程序

创建工程并引入OpenGL ES

第一种引入方式:

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

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

@import OpenGLES;

创建OpenGLView类继承自UIView

OpenGLView.h:

#import <UIKit/UIKit.h>
@import OpenGLES;

NS_ASSUME_NONNULL_BEGIN

@interface OpenGLView : UIView
{
    CAEAGLLayer     *_eaglLayer;
    EAGLContext     *_context;
    GLuint          _renderBuffer;
    GLuint          _frameBuffer;
}
@end

NS_ASSUME_NONNULL_END

CAEAGLLayer

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

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

+ (Class)layerClass
{
	 return [CAEAGLLayer class];
}

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

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

EAGLContext

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

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

- (void)setupContext
{
    if (!_context) {
        _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    }
    NSAssert(_context && [EAGLContext setCurrentContext:_context],@"Failed to initialize OpenGLES 2.0 context");
}

Renderbuffer

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

创建Renderbuffer:

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

glGenRenderbuffers方法

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

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

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

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

// 释放旧的 renderbuffer
if (_renderbuffer) {
    glDeleteRenderbuffers(1, &_renderbuffer);
    _renderbuffer = 0;
}

glBindRenderbuffer方法

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

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

renderbufferStorage方法

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

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

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

Framebuffer

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

它的创建过程如下:

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

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

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

GL_API void           GL_APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);

通过glCheckFramebufferStatus检查framebuffer的创建情况

- (BOOL)checkFramebuffer:(NSError *__autoreleasing *)error {
    // 检查 framebuffer 是否创建成功
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    NSString *errorMessage = nil;
    BOOL result = NO;
    
    switch (status)
    {
        case GL_FRAMEBUFFER_UNSUPPORTED:
            errorMessage = @"framebuffer不支持该格式";
            result = NO;
            break;
        case GL_FRAMEBUFFER_COMPLETE:
            NSLog(@"framebuffer 创建成功");
            result = YES;
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
            errorMessage = @"Framebuffer不完整 缺失组件";
            result = NO;
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
            errorMessage = @"Framebuffer 不完整, 附加图片必须要指定大小";
            result = NO;
            break;
        default:
            // 一般是超出GL纹理的最大限制
            errorMessage = @"未知错误 error !!!!";
            result = NO;
            break;
    }
    
    NSLog(@"%@",errorMessage ? errorMessage : @"");
    *error = errorMessage ? [NSError errorWithDomain:@"com.error"
                                                code:status
                                            userInfo:@{@"ErrorMessage" : errorMessage}] : nil;
    
    return result;
}

简单渲染-设置背景色

- (void)render
{
    glClearColor(220.0/255.0, 20.0/255.0, 60.0/255.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    // 做完所有绘制操作后,最终呈现到屏幕上
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

glClearColor方法

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

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