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 <OpenGLES/ES3/gl.h>

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組成,在此只綁定一個渲染緩沖區,即是把顏色緩沖區附著到幀緩沖區中。幀緩沖區與渲染緩沖區的關系如下圖所示。

幀緩沖區對象的內容
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];,現在你應該能看到紫色的屏幕。

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:繪制圓點。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容