本文檔的任務(wù)是介紹最簡單的OpenGL ES 3.0程序在iOS上的開發(fā)步驟窿冯,功能是打印一些OpenGL ES相關(guān)信息并設(shè)置屏幕顏色扼倘。不使用GLKit的原因是方便理解程度的實(shí)現(xiàn)邏輯并簡化代碼移植至Android平臺(tái)的難度,開發(fā)環(huán)境為Xcode 7赁还、運(yùn)行環(huán)境為iOS 9旨枯。編寫于2016年3月趟佃,修訂于10月且將原系列文檔合并入《OpenGL ES 3.0 數(shù)據(jù)可視化》系列文檔耸袜。代碼托管在GitHub: ES3_0_ClearColor友多。
歡迎加入GPUImage、OpenGL ES堤框、Vulkan域滥、Metal交流群536987698,一起學(xué)習(xí)胰锌。
1、打印OpenGL ES平臺(tái)相關(guān)實(shí)現(xiàn)信息
1藐窄、新建一個(gè)Single View Application工程资昧。
2、創(chuàng)建UIView的子類MyGLView荆忍。
3格带、引入OpenGL ES頭文件撤缴。
#import <OpenGLES/ES3/gl.h>
4、配置OpenGL ES上下文叽唱。OpenGL ES系統(tǒng)與本地窗口(UIKit)橋接由EGL上下文系統(tǒng)實(shí)現(xiàn)屈呕,iOS平臺(tái)的EGL具體實(shí)現(xiàn)稱為EAGL,可認(rèn)為是“Embedded Apple Graphics Library”棺亭。在UIView的初始化方法initWithFrame:中輸入如下代碼
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
[EAGLContext setCurrentContext:context]; // 1
注釋// 1
設(shè)置當(dāng)前上下文環(huán)境并告知EAGLContext虎眨,即該線程中的后續(xù)OpenGL ES調(diào)用將與該上下文環(huán)境綁定。若不綁定镶摘,則下面的GL調(diào)用都返回?zé)o效值嗽桩。
5、打印廠商信息凄敢。一般是查看當(dāng)前設(shè)備支持的OpenGL ES版本及拓展功能碌冶。OpenGL ES只是標(biāo)準(zhǔn)接口,每個(gè)平臺(tái)的具體實(shí)現(xiàn)細(xì)節(jié)各不相同涝缝,有些平臺(tái)提供了一些紋理的拓展方便開發(fā)或提高性能扑庞,如蘋果提供了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));
模擬器的運(yùn)行結(jié)果:
廠家 = 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的運(yùn)行結(jié)果:
廠家 = 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
可見拒逮,模擬器與真機(jī)的拓展功能幾乎一致罐氨,真機(jī)只多了一項(xiàng)GL_KHR_texture_compression_astc_ldr。下面簡單介紹拓展的命名規(guī)則消恍,后續(xù)文檔再詳細(xì)描述它們的用途:
- GL_EXT_開頭的拓展在其他平臺(tái)基本也是可用的岂昭,如Android,很可能在下一個(gè)GL版本成為新標(biāo)準(zhǔn)的核心功能狠怨。
- GL_APPLE這類以廠家開頭(如APPLE)的拓展只能在其設(shè)備上使用约啊。
- GL_OES特定于當(dāng)前平臺(tái)。
- GL_IMG為圖像相關(guān)的拓展佣赖,比如支持更多紋理格式恰矩。
逐項(xiàng)獲取拓展名可使用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);
現(xiàn)在外傅,讓我們實(shí)現(xiàn)一個(gè)設(shè)置屏幕顏色的功能。開始真正的繪制操作俩檬,需要配置圖層類萎胰、渲染緩沖區(qū)、幀緩沖區(qū)等棚辽。下面逐步詳細(xì)描述技竟。
2、設(shè)置屏幕顏色
6屈藐、修改MyGLView的+ (Class)layerClass
方法榔组,使用CAEAGLLayer作為我們的圖層類熙尉。
+ (Class)layerClass {
return [CAEAGLLayer class];
}
CAEAGLLayer是蘋果專門為OpenGL ES準(zhǔn)備的一個(gè)圖層類,它用于分配渲染緩沖區(qū)的存儲(chǔ)空間搓扯,相關(guān)文檔如下:
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检痰、配置渲染緩沖區(qū)(Render Buffer)
渲染緩沖區(qū)類似一個(gè)平面,用于保存繪制內(nèi)容锨推,并使用某種數(shù)據(jù)類型加以填充铅歼,比如顏色值。我們?cè)诖藙?chuàng)建的是顏色緩沖區(qū)爱态,用以保存所繪制的顏色信息谭贪,緩沖區(qū)大小由CAEAGLLayer的bounds中size指定,這在處理屏幕旋轉(zhuǎn)時(shí)是個(gè)非常重要的條件锦担。通常俭识,屏幕發(fā)生旋轉(zhuǎn)時(shí),屏幕的寬高值互換洞渔,故需要重新創(chuàng)建幀緩沖區(qū)等內(nèi)容套媚,后續(xù)文檔將詳細(xì)討論此問題。OpenGL ES有Frame buffer磁椒、Render buffer堤瘤、Data buffer等類型的緩沖區(qū),它們的作用各不相同浆熔。不過本辐,它們的創(chuàng)建與綁定等操作流程是類似的,后續(xù)文檔再作詳細(xì)介紹医增。
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
在綁定好渲染緩沖區(qū)后慎皱,通知EAGLContext讓CAEAGLLayer實(shí)例中分配存儲(chǔ)空間,用以保存后續(xù)繪制的內(nèi)容叶骨。對(duì)于離屏表面茫多,用戶應(yīng)使用glRenderbufferStorage()
進(jìn)行存儲(chǔ)空間的分配操作,這是高級(jí)話題忽刽,后續(xù)文檔再介紹天揖。
8、配置幀緩沖區(qū)(Frame Buffer)
幀緩沖區(qū)由多個(gè)render buffer組成跪帝,在此只綁定一個(gè)渲染緩沖區(qū)今膊,即是把顏色緩沖區(qū)附著到幀緩沖區(qū)中。幀緩沖區(qū)與渲染緩沖區(qū)的關(guān)系如下圖所示伞剑。
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
幀緩沖區(qū)的創(chuàng)建斑唬、綁定操作類似渲染緩沖區(qū),第三步略有區(qū)別:將渲染緩沖區(qū)配置成幀緩沖區(qū)的顏色附著(Attachment)。在此可理解成將顏色數(shù)據(jù)放入幀緩沖區(qū)這個(gè)書柜里指定的位置讓別人看赖钞。
9、配置緩沖區(qū)清除顏色
glClearColor(1.0, 0, 1.0, 1.0);
設(shè)置顏色緩沖區(qū)渲染聘裁。
10雪营、設(shè)置屏幕顏色(清除渲染緩沖區(qū))
glClear(GL_COLOR_BUFFER_BIT);
讓OpenGL ES系統(tǒng)使用前面glClearColor
指定的顏色刷一遍指定的緩沖區(qū),這里是顏色緩沖區(qū)衡便。而顏色緩沖區(qū)的內(nèi)容保存在渲染緩沖區(qū)中献起,且最后呈現(xiàn)給用戶的是渲染緩沖區(qū),因此镣陕,這里就是設(shè)置屏幕顏色的具體實(shí)現(xiàn)了谴餐。
11、交換前后端幀緩沖區(qū)
iOS系統(tǒng)維護(hù)著兩個(gè)重要的幀緩沖區(qū)呆抑,當(dāng)前屏幕使用的是前端幀緩沖區(qū)岂嗓。然而,剛才我們的操作都在后端幀緩沖區(qū)執(zhí)行鹊碍,若直接寫在前端幀緩沖區(qū)厌殉,那么沒完成的繪制也會(huì)顯示在屏幕上,而屏幕是逐行掃描刷新的侈咕,顯然這個(gè)行為會(huì)給用戶造成錯(cuò)覺公罕,比如逐行繪制圖片。所以耀销,在后端幀緩沖區(qū)操作完成后楼眷,我們需要通知系統(tǒng),讓其交換前后端幀緩沖區(qū)熊尉,用戶才能看到前面的操作罐柳。所以,這是最后一步操作:[context presentRenderbuffer:GL_RENDERBUFFER];
帽揪,現(xiàn)在你應(yīng)該能看到紫色的屏幕硝清。
12、清理操作
在結(jié)束OpenGL ES操作后转晰,應(yīng)該在dealloc
或適當(dāng)?shù)牡胤阶銮謇聿僮髀茫唇Y(jié)束當(dāng)前上下文的使用,具體表現(xiàn)為:
if ([EAGLContext currentContext] == yourCurrentContext) {
[EAGLContext setCurrentContext: nil];
}
當(dāng)然查邢,在本文這么簡單的使用場合中不解除也能正常運(yùn)行蔗崎,因?yàn)閕OS幫我們做了這個(gè)處理。然而扰藕,之后隨著我們的應(yīng)用越來越復(fù)雜時(shí)缓苛,需要自行處理進(jìn)入前后臺(tái)情況下的EGL上下文的保存情況。
3、總結(jié)
從上述內(nèi)容可知未桥,在iOS上通過繼承UIView進(jìn)行OpenGL ES 3.0開發(fā)的最簡單步驟為:
- 若繼承UIView子類笔刹,則需覆蓋
+layerClass
- 配置EAGLContext。若使用GLKViewController冬耿,應(yīng)配置GLKView的context屬性為新生成的Context舌菜。
- 配置渲染緩沖區(qū)Render Buffer
- 創(chuàng)建幀緩沖區(qū)Frame Buffer并配置渲染、深度等緩沖區(qū)
- 設(shè)置視口glViewport
- 清空緩沖區(qū)glClear(指定緩沖區(qū))
- 繪制操作
- 通知EAGLContext將渲染緩沖區(qū)內(nèi)容發(fā)送至屏幕
其中亦镶,步驟5日月、7在本文檔沒使用,因?yàn)槟J(rèn)情況缤骨,我們使用了全屏顯示且沒繪制幾何圖元爱咬,比如點(diǎn)、直線和三角形绊起。