OpenGL ES 3.0 数据可视化 0:Hello world

本文档的任务是介绍最简单的OpenGL ES 3.0程序在iOS上的开发步骤,功能是打印一些OpenGL ES相关信息并设置屏幕颜色。不使用GLKit的原因是方便理解程度的实现逻辑并简化代码移植至Android平台的难度,开发环境为Xcode 7、运行环境为iOS 9。编写于2016年3月,修订于10月且将原系列文档合并入《OpenGL ES 3.0 数据可视化》系列文档。代码托管在GitHub: ES3_0_ClearColor。

欢迎加入GPUImage、OpenGL ES、Vulkan、Metal交流群536987698,一起学习。

1、打印OpenGL ES平台相关实现信息

1、新建一个Single View Application工程。
2、创建UIView的子类MyGLView。
3、引入OpenGL ES头文件。

#import 

4、配置OpenGL ES上下文。OpenGL ES系统与本地窗口(UIKit)桥接由EGL上下文系统实现,iOS平台的EGL具体实现称为EAGL,可认为是“Embedded Apple Graphics Library”。在UIView的初始化方法initWithFrame:中输入如下代码

EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
[EAGLContext setCurrentContext:context]; // 1

注释// 1设置当前上下文环境并告知EAGLContext,即该线程中的后续OpenGL ES调用将与该上下文环境绑定。若不绑定,则下面的GL调用都返回无效值。

5、打印厂商信息。一般是查看当前设备支持的OpenGL ES版本及拓展功能。OpenGL ES只是标准接口,每个平台的具体实现细节各不相同,有些平台提供了一些纹理的拓展方便开发或提高性能,如苹果提供了PowerVR压缩纹理。

printf("厂家 = %s\n", glGetString(GL_VENDOR));
printf("渲染器 = %s\n", glGetString(GL_RENDERER));
printf("ES版本 = %s\n", glGetString(GL_VERSION));
printf("拓展功能 =>\n%s\n", glGetString(GL_EXTENSIONS));

模拟器的运行结果:

厂家 = Apple Inc.
渲染器 = Apple Software Renderer
ES版本 = OpenGL ES 3.0 APPLE-12.0.38
拓展功能 =>
GL_OES_standard_derivatives 
GL_EXT_color_buffer_half_float 
GL_EXT_debug_label 
GL_EXT_debug_marker 
GL_EXT_pvrtc_sRGB 
GL_EXT_read_format_bgra 
GL_EXT_separate_shader_objects
GL_EXT_shader_framebuffer_fetch 
GL_EXT_shader_texture_lod 
GL_EXT_shadow_samplers 
GL_EXT_texture_filter_anisotropic 
GL_APPLE_clip_distance 
GL_APPLE_color_buffer_packed_float
GL_APPLE_copy_texture_levels 
GL_APPLE_rgb_422 
GL_APPLE_texture_format_BGRA8888 
GL_IMG_read_format 
GL_IMG_texture_compression_pvrtc 

iPad Air 2的运行结果:

厂家 = Apple Inc.
渲染器 = Apple A8X GPU
ES版本 = OpenGL ES 3.0 Apple A8X GPU - 77.14
拓展功能 =>
GL_OES_standard_derivatives
GL_KHR_texture_compression_astc_ldr
GL_EXT_color_buffer_half_float
GL_EXT_debug_label
GL_EXT_debug_marker
GL_EXT_pvrtc_sRGB
GL_EXT_read_format_bgra
GL_EXT_separate_shader_objects
GL_EXT_shader_framebuffer_fetch
GL_EXT_shader_texture_lod
GL_EXT_shadow_samplers
GL_EXT_texture_filter_anisotropic
GL_APPLE_clip_distance
GL_APPLE_color_buffer_packed_float
GL_APPLE_copy_texture_levels
GL_APPLE_rgb_422
GL_APPLE_texture_format_BGRA8888
GL_IMG_read_format
GL_IMG_texture_compression_pvrtc 

可见,模拟器与真机的拓展功能几乎一致,真机只多了一项GL_KHR_texture_compression_astc_ldr。下面简单介绍拓展的命名规则,后续文档再详细描述它们的用途:

  • GL_EXT_开头的拓展在其他平台基本也是可用的,如Android,很可能在下一个GL版本成为新标准的核心功能。
  • GL_APPLE这类以厂家开头(如APPLE)的拓展只能在其设备上使用。
  • GL_OES特定于当前平台。
  • GL_IMG为图像相关的拓展,比如支持更多纹理格式。

逐项获取拓展名可使用glGetStringi,示例如下。

int max = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &max);
NSMutableSet *extensions = [NSMutableSet set];
for (int i = 0; i < max; i++) {
    [extensions addObject: @( (char *)glGetStringi(GL_EXTENSIONS, i) )];
}
NSLog(@"%@", extensions);

现在,让我们实现一个设置屏幕颜色的功能。开始真正的绘制操作,需要配置图层类、渲染缓冲区、帧缓冲区等。下面逐步详细描述。

2、设置屏幕颜色

6、修改MyGLView的+ (Class)layerClass方法,使用CAEAGLLayer作为我们的图层类。

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

CAEAGLLayer是苹果专门为OpenGL ES准备的一个图层类,它用于分配渲染缓冲区的存储空间,相关文档如下:

The CAEAGLLayer class supports drawing OpenGL content in iPhone applications. If you plan to use OpenGL for your rendering, use this class as the backing layer for your views by returning it from your view’s layerClass class method. The returned CAEAGLLayer object is a wrapper for a Core Animation surface that is fully compatible with OpenGL ES function calls.

Prior to designating the layer’s associated view as the render target for a graphics context, you can change the rendering attributes you want using the drawableProperties property. This property lets you configure the color format for the rendering surface and whether the surface retains its contents.

Because an OpenGL ES rendering surface is presented to the user using Core Animation, any effects and animations you apply to the layer affect the 3D content you render. However, for best performance, do the following:

  • Set the layer’s opaque attribute to TRUE.
  • Set the layer bounds to match the dimensions of the display.
  • Make sure the layer is not transformed.
  • Avoid drawing other layers on top of the CAEAGLLayer object. If you must draw other, non OpenGL content, you might find the performance cost acceptable if you place transparent 2D content on top of the GL content and also make sure that the OpenGL content is opaque and not transformed.
  • When drawing landscape content on a portrait display, you should rotate the content yourself rather than using the CAEAGLLayer transform to rotate it.

7、配置渲染缓冲区(Render Buffer)

渲染缓冲区类似一个平面,用于保存绘制内容,并使用某种数据类型加以填充,比如颜色值。我们在此创建的是颜色缓冲区,用以保存所绘制的颜色信息,缓冲区大小由CAEAGLLayer的bounds中size指定,这在处理屏幕旋转时是个非常重要的条件。通常,屏幕发生旋转时,屏幕的宽高值互换,故需要重新创建帧缓冲区等内容,后续文档将详细讨论此问题。OpenGL ES有Frame buffer、Render buffer、Data buffer等类型的缓冲区,它们的作用各不相同。不过,它们的创建与绑定等操作流程是类似的,后续文档再作详细介绍。

GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];

在绑定好渲染缓冲区后,通知EAGLContext让CAEAGLLayer实例中分配存储空间,用以保存后续绘制的内容。对于离屏表面,用户应使用glRenderbufferStorage()进行存储空间的分配操作,这是高级话题,后续文档再介绍。

8、配置帧缓冲区(Frame Buffer)

帧缓冲区由多个render buffer组成,在此只绑定一个渲染缓冲区,即是把颜色缓冲区附着到帧缓冲区中。帧缓冲区与渲染缓冲区的关系如下图所示。

OpenGL ES 3.0 数据可视化 0:Hello world_第1张图片
帧缓冲区对象的内容
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);

帧缓冲区的创建、绑定操作类似渲染缓冲区,第三步略有区别:将渲染缓冲区配置成帧缓冲区的颜色附着(Attachment)。在此可理解成将颜色数据放入帧缓冲区这个书柜里指定的位置让别人看。

9、配置缓冲区清除颜色

glClearColor(1.0, 0, 1.0, 1.0);设置颜色缓冲区渲染。

10、设置屏幕颜色(清除渲染缓冲区)

glClear(GL_COLOR_BUFFER_BIT);让OpenGL ES系统使用前面glClearColor指定的颜色刷一遍指定的缓冲区,这里是颜色缓冲区。而颜色缓冲区的内容保存在渲染缓冲区中,且最后呈现给用户的是渲染缓冲区,因此,这里就是设置屏幕颜色的具体实现了

11、交换前后端帧缓冲区

iOS系统维护着两个重要的帧缓冲区,当前屏幕使用的是前端帧缓冲区。然而,刚才我们的操作都在后端帧缓冲区执行,若直接写在前端帧缓冲区,那么没完成的绘制也会显示在屏幕上,而屏幕是逐行扫描刷新的,显然这个行为会给用户造成错觉,比如逐行绘制图片。所以,在后端帧缓冲区操作完成后,我们需要通知系统,让其交换前后端帧缓冲区,用户才能看到前面的操作。所以,这是最后一步操作:[context presentRenderbuffer:GL_RENDERBUFFER];,现在你应该能看到紫色的屏幕。

OpenGL ES 3.0 数据可视化 0:Hello world_第2张图片
iPhone 7运行截图

12、清理操作

在结束OpenGL ES操作后,应该在dealloc或适当的地方做清理操作,即结束当前上下文的使用,具体表现为:

if ([EAGLContext currentContext] == yourCurrentContext) {
    [EAGLContext setCurrentContext: nil];
}

当然,在本文这么简单的使用场合中不解除也能正常运行,因为iOS帮我们做了这个处理。然而,之后随着我们的应用越来越复杂时,需要自行处理进入前后台情况下的EGL上下文的保存情况。

3、总结

从上述内容可知,在iOS上通过继承UIView进行OpenGL ES 3.0开发的最简单步骤为:

  1. 若继承UIView子类,则需覆盖+layerClass
  2. 配置EAGLContext。若使用GLKViewController,应配置GLKView的context属性为新生成的Context。
  3. 配置渲染缓冲区Render Buffer
  4. 创建帧缓冲区Frame Buffer并配置渲染、深度等缓冲区
  5. 设置视口glViewport
  6. 清空缓冲区glClear(指定缓冲区)
  7. 绘制操作
  8. 通知EAGLContext将渲染缓冲区内容发送至屏幕

其中,步骤5、7在本文档没使用,因为默认情况,我们使用了全屏显示且没绘制几何图元,比如点、直线和三角形。

下一篇文档:OpenGL ES 3.0 数据可视化 1:绘制圆点。

你可能感兴趣的:(OpenGL ES 3.0 数据可视化 0:Hello world)