OpenGL ES2入门02-基本API介绍
2019-08-28
概述
接下来会结合着示例程序来首先介绍一些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);
- n: 表示申请生成 renderbuffer 的个数。
- renderbuffers: 回分配给 renderbuffer 的 id。
返回的 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);
- target: 表示当前 renderbuffer,必须是 GL_RENDERBUFFER。
- renderbuffer: 某个 renderbuffer 对应的 id(比如使用 glGenRenderbuffers 生成的 id)。
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);
- target: 表示当前 framebuffer,必须是 GL_FRAMEBUFFER。
- attachment:指定 renderbuffer 被装配到那个装配点上,其值是 GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT,GL_STENCIL_ATTACHMENT 中的一个,分别对应 color,depth 和 stencil 三大 buffer。
- renderbuffertarget:表示当前 renderbuffer,必须是 GL_RENDERBUFFER。
- renderbuffer:某个 renderbuffer 对应的 id,表示需要装配的 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。