OpenGL ES 框架詳細解析(五) —— 使用OpenGL ES和GLKit進行繪制

版本記錄

版本號 時間
V1.0 2017.09.28

前言

OpenGL ES是一個強大的圖形庫,是跨平臺的圖形API,屬于OpenGL的一個簡化版本。iOS系統(tǒng)可以利用OpenGL ES將圖像數(shù)據(jù)直接送入到GPU進行渲染,這樣避免了從CPU進行計算再送到顯卡渲染帶來的性能的高消耗,能帶來來更好的視頻效果和用戶體驗。接下來幾篇就介紹下iOS 系統(tǒng)的 OpenGL ES框架。感興趣的可以看上面幾篇。
1. OpenGL ES 框架詳細解析(一) —— 基本概覽
2. OpenGL ES 框架詳細解析(二) —— 關(guān)于OpenGL ES
3. OpenGL ES 框架詳細解析(三) —— 構(gòu)建用于iOS的OpenGL ES應用程序的清單
4. OpenGL ES 框架詳細解析(四) —— 配置OpenGL ES的上下文

Drawing with OpenGL ES and GLKit - 使用OpenGL ES和GLKit進行繪制

GLKit框架提供了視圖和視圖控制器類,可以消除繪圖和動畫化OpenGL ES內(nèi)容所需的設置和維護代碼。 GLKView類管理OpenGL ES基礎結(jié)構(gòu)為您的繪圖代碼提供了一個地方,GLKViewController類為GLKit視圖中OpenGL ES內(nèi)容的平滑動畫提供了一個渲染循環(huán)。 這些類擴展了用于繪制視圖內(nèi)容和管理視圖呈現(xiàn)的標準UIKit設計模式。 因此,您可以將重點放在OpenGL ES渲染代碼上,并使您的應用快速運行。 GLKit框架還提供了其他功能來簡化OpenGL ES 2.0和3.0開發(fā)。


A GLKit View Draws OpenGL ES Content on Demand - GLKit視圖根據(jù)需要繪制OpenGL ES內(nèi)容

GLKView類提供與標準UIView繪圖周期相當?shù)腛penGL ES。 UIView實例自動配置其圖形上下文,以便您的drawRect:實現(xiàn)只需要執(zhí)行Quartz 2D繪圖命令,并且GLKView實例自動配置,以便您的繪圖方法只需要執(zhí)行OpenGL ES繪圖命令。 GLKView類通過維護保存OpenGL ES繪圖命令結(jié)果的framebuffer對象提供此功能,然后在繪圖方法返回后自動將其顯示給Core Animation。

像標準的UIKit視圖一樣,GLKit視圖根據(jù)需要呈現(xiàn)其內(nèi)容。 當您的視圖第一次顯示時,它會調(diào)用您的繪圖方法 - Core Animation會緩存渲染的輸出,并在顯示視圖時顯示它。 當您想要更改視圖的內(nèi)容時,請調(diào)用其setNeedsDisplay方法,然后視圖再次調(diào)用繪圖方法,緩存生成的圖像,并將其顯示在屏幕上。 當用于渲染圖像的數(shù)據(jù)不經(jīng)常更改或僅響應于用戶操作時,此方法非常有用。 通過僅在需要時才提供新的視圖內(nèi)容,您可以節(jié)省設備上的電池電量,并為設備執(zhí)行其他操作留出更多時間。

Rendering OpenGL ES content with a GLKit view

1. Creating and Configuring a GLKit View - 創(chuàng)建和配置GLKit視圖

您可以以編程方式或使用Interface Builder創(chuàng)建和配置GLKView對象。 在將其用于繪圖之前,必須將其與EAGLContext對象相關(guān)聯(lián)(請參閱Configuring OpenGL ES Contexts)。

  • 以編程方式創(chuàng)建視圖時,首先創(chuàng)建上下文,然后將其傳遞給視圖的initWithFrame:context:method
  • 從sb中加載視圖后,創(chuàng)建一個上下文并將其設置為視圖的上下文屬性的值。

GLKit視圖會自動創(chuàng)建和配置自己的OpenGL ES framebuffer對象和renderbuffers。 您可以使用視圖的可繪制屬性來控制這些對象的屬性,如下面代碼所示。 如果您更改GLKit視圖的大小,比例因子或可繪制屬性,則會在下次繪制內(nèi)容時自動刪除并重新創(chuàng)建相應的framebuffer對象和renderbuffers。

// Configuring a GLKit view

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    // Create an OpenGL ES context and assign it to the view loaded from storyboard
    GLKView *view = (GLKView *)self.view;
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
 
    // Configure renderbuffers created by the view
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
 
    // Enable multisampling
    view.drawableMultisample = GLKViewDrawableMultisample4X;
}

您可以使用其drawableMultisample屬性為GLKView實例啟用多采樣。 多采樣是一種抗鋸齒形式,可以平滑鋸齒狀邊緣,以更多的內(nèi)存和片段處理時間為代價改善大多數(shù)3D應用程序的圖像質(zhì)量 - 如果啟用多采樣,則始終測試應用程序的性能,以確保其仍然可以接受。

2. Drawing With a GLKit View - 利用GLKit視圖進行繪制

上面的代碼概述了繪制OpenGL ES內(nèi)容的三個步驟:準備OpenGL ES基礎設施,發(fā)布繪圖命令,并將呈現(xiàn)的內(nèi)容呈現(xiàn)給Core Animation進行顯示。 GLKView類實現(xiàn)了第一和第三步。 對于第二步,您將實現(xiàn)一個繪圖方法,如下面的代碼所示。

// Example drawing method for a GLKit view
 
- (void)drawRect:(CGRect)rect
{
    // Clear the framebuffer
    glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
    // Draw using previously configured texture, shader, uniforms, and vertex array
    glBindTexture(GL_TEXTURE_2D, _planetTexture);
    glUseProgram(_diffuseShading);
    glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m);
    glBindVertexArrayOES(_planetMesh);
    glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT);
}

注意:glClear函數(shù)提示OpenGL ES可以丟棄任何現(xiàn)有的幀緩沖區(qū)內(nèi)容,避免了昂貴的內(nèi)存操作將以前的內(nèi)容加載到內(nèi)存中。 為確保最佳性能,您應該在繪制之前始終調(diào)用此函數(shù)。

GLKView類能夠為OpenGL ES繪圖提供一個簡單的界面,因為它可以管理OpenGL ES渲染過程的標準部分:

  • 在調(diào)用繪圖方法之前,該視圖:

    • 使其EAGLContext對象成為當前上下文
    • 根據(jù)當前大小,比例因子和可繪制屬性(如果需要)創(chuàng) 建一個framebuffer對象和renderbuffers
    • 將framebuffer對象綁定為繪制命令的當前目標
    • 設置OpenGL ES視口以匹配幀緩沖區(qū)大小
  • 在您的繪圖方法返回后,視圖:

    • 解決多采樣緩沖區(qū)(如果啟用多重采樣)
    • 丟棄其內(nèi)容不再需要的renderbuffers
    • 向Core Animation呈現(xiàn)renderbuffer內(nèi)容進行緩存和顯示

Rendering Using a Delegate Object - 使用代理對象進行渲染

許多OpenGL ES應用程序在自定義類中實現(xiàn)渲染代碼。 這種方法的一個優(yōu)點是它允許您通過為每個渲染算法定義一個不同的渲染器類來輕松支持多個渲染算法。 共享通用功能的渲染算法可以從超類繼承。 例如,您可以使用不同的渲染器類來支持OpenGL ES 2.0和3.0(請Configuring OpenGL ES Contexts)。 或者您可以使用它們來定制渲染,以獲得更強大硬件的設備上的更好的圖像質(zhì)量。

GLKit非常適合這種方法 - 您可以使您的渲染器對象成為標準GLKView實例的委托。 您的渲染器類不是將GLKView子類化并實現(xiàn)drawRect:方法,而是使用GLKViewDelegate協(xié)議并實現(xiàn)glkView:drawInRect:方法。 下面代碼演示了在應用啟動時基于硬件功能選擇渲染器類。

// Choosing a renderer class based on hardware features

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

// Create a context so we can test for features

EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

[EAGLContext setCurrentContext:context];

// Choose a rendering class based on device features

GLint maxTextureSize;

glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

if (maxTextureSize > 2048)

self.renderer = [[MyBigTextureRenderer alloc] initWithContext:context];

else

self.renderer = [[MyRenderer alloc] initWithContext:context];

// Make the renderer the delegate for the view loaded from the main storyboard

GLKView *view = (GLKView *)self.window.rootViewController.view;

view.delegate = self.renderer;

// Give the OpenGL ES context to the view so it can draw

view.context = context;

return YES;

}

A GLKit View Controller Animates OpenGL ES Content - GLKit視圖控制器動畫化OpenGL ES內(nèi)容

默認情況下,GLKView對象根據(jù)需要呈現(xiàn)其內(nèi)容。 也就是說,使用OpenGL ES繪制的一個關(guān)鍵優(yōu)點是它能夠使用圖形處理硬件來連續(xù)制作復雜的場景 - 諸如游戲和模擬的應用程序很少呈現(xiàn)靜態(tài)圖像。 對于這些情況,GLKit框架提供了一個視圖控制器類,它為其管理的GLKView對象維護一個動畫循環(huán)。 該循環(huán)遵循游戲和模擬中常見的設計模式,分為兩個階段:更新和顯示。 下圖顯示了一個動畫循環(huán)的簡化示例。

The animation loop

1. Understanding the Animation Loop - 了解動畫循環(huán)

對于更新階段,視圖控制器調(diào)用其自己的更新方法(或其委托的glkViewControllerUpdate:方法)。 在這種方法中,你應該準備繪制下一個幀。 例如,游戲可能會使用這種方法根據(jù)自最后一幀以來接收到的輸入事件來確定玩家和敵人角色的位置,科學可視化可能會使用此方法來運行其模擬步驟。 如果您需要時間信息來確定您的應用的下一幀的狀態(tài),請使用其中一個視圖控制器的時序?qū)傩浴?/p>

對于顯示階段,視圖控制器調(diào)用其視圖的顯示方法,該方法又調(diào)用您的繪圖方法。 在繪圖方法中,您可以向GPU提交OpenGL ES繪圖命令以呈現(xiàn)內(nèi)容。 為了獲得最佳性能,應用程序應在開始渲染新框架時修改OpenGL ES對象,然后提交繪圖命令。 在上圖中,顯示階段將著色器程序中的統(tǒng)一變量設置為在更新階段計算的矩陣,然后提交繪圖命令以呈現(xiàn)新內(nèi)容。

動畫循環(huán)按照視圖控制器的framePerSecond屬性指示的速率在這兩個階段之間進行交替。 您可以使用preferredFramesPerSecond屬性設置所需的幀速率,以優(yōu)化當前顯示硬件的性能,視圖控制器會自動選擇接近您的首選值的最佳幀速率。

重要提示:為獲得最佳效果,請選擇應用程序可以始終如一地實現(xiàn)的幀率 平滑,一致的幀率產(chǎn)生比不定期變化的幀速率更愉快的用戶體驗。

2. Using a GLKit View Controller - 使用GLKit控制器

下面代碼演示了使用GLKViewController子類和GLKView實例渲染動畫OpenGL ES內(nèi)容的典型策略。

// Using a GLKit view and view controller to draw and animate OpenGL ES content

@implementation PlanetViewController // subclass of GLKViewController
 
- (void)viewDidLoad
{
    [super viewDidLoad];
 
    // Create an OpenGL ES context and assign it to the view loaded from storyboard
    GLKView *view = (GLKView *)self.view;
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
 
    // Set animation frame rate
    self.preferredFramesPerSecond = 60;
 
    // Not shown: load shaders, textures and vertex arrays, set up projection matrix
    [self setupGL];
}
 
- (void)update
{
    _rotation += self.timeSinceLastUpdate * M_PI_2; // one quarter rotation per second
 
    // Set up transform matrices for the rotating planet
    GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(_rotation, 0.0f, 1.0f, 0.0f);
    _normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL);
    _modelViewProjectionMatrix = GLKMatrix4Multiply(_projectionMatrix, modelViewMatrix);
}
 
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    // Clear the framebuffer
    glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
    // Set shader uniforms to values calculated in -update
    glUseProgram(_diffuseShading);
    glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m);
    glUniformMatrix3fv(_uniformNormalMatrix, 1, 0, _normalMatrix.m);
 
    // Draw using previously configured texture and vertex array
    glBindTexture(GL_TEXTURE_2D, _planetTexture);
    glBindVertexArrayOES(_planetMesh);
    glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT, 0);
}
 
@end

在此示例中,PlanetViewController類(自定義GLKViewController子類)的實例從故事板加載,以及標準GLKView實例及其可繪制屬性。viewDidLoad方法創(chuàng)建一個OpenGL ES上下文并將其提供給視圖,并且還設置動畫循環(huán)的幀速率。

視圖控制器自動為其視圖的委托,因此它同時實現(xiàn)動畫循環(huán)的更新和顯示階段。 在更新方法中,它計算顯示旋轉(zhuǎn)平面所需的變換矩陣。 在glkView:drawInRect:方法中,它將這些矩陣提供給著色器程序,并提交繪制命令以渲染平面幾何。


Using GLKit to Develop Your Renderer - 使用GLKit開發(fā)您的渲染

除了查看和查看控制器基礎外,GLKit框架還提供了幾個其他功能來簡化iOS上的OpenGL ES開發(fā)。

1. Handling Vector and Matrix Math - 處理向量和矩陣數(shù)學

OpenGL ES 2.0及更高版本不提供用于創(chuàng)建或指定變換矩陣的內(nèi)置函數(shù)。 相反,可編程著色器提供頂點變換,并使用通用的均勻變量指定著色器輸入。 GLKit框架包括矢量和矩陣類型和功能的綜合庫,針對iOS硬件上的高性能進行了優(yōu)化。 (見GLKit Framework Reference。)

2. Migrating from the OpenGL ES 1.1 Fixed-Function Pipeline - 從OpenGL ES 1.1固定功能管道遷移

OpenGL ES 2.0及更高版本刪除與OpenGL ES 1.1固定功能圖形管道相關(guān)聯(lián)的所有功能。GLKBaseEffect類為OpenGL ES 1.1管道的轉(zhuǎn)換,照明和陰影階段提供了Objective-C模擬,GLKSkyboxEffectGLKReflectionMapEffect類增加了對常見視覺效果的支持。 有關(guān)詳細信息,請參閱這些類的參考文檔。

3. Loading Texture Data - 加載紋理數(shù)據(jù)

GLKTextureLoader類提供了一種簡單的方法,可將紋理數(shù)據(jù)從iOS支持的任何圖像格式加載到OpenGL ES上下文中,同步或異步。 (請參閱Use the GLKit Framework to Load Texture Data。)

后記

未完,待續(xù)~~~

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

推薦閱讀更多精彩內(nèi)容