OpenGL ES for iOS - 3

繪制OpenGL ES和GLKit

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

GLKit視圖根據需要繪制OpenGL ES內容

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

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


3-1.png

創建和配置GLKit視圖

您可以以編程方式或使用Interface Builder創建和配置GLKView對象。在使用它繪制之前,必須將其與EAGLContext對象相關聯(請參閱配置OpenGL ES上下文)。

  • 以編程方式創建視圖時,首先創建上下文,然后將其傳遞給視圖的initWithFrame:context:方法。
  • 從故事板加載視圖后,創建上下文并將其設置為視圖的上下文屬性的值。

GLKit視圖會自動創建和配置自己的OpenGL ES framebuffer對象和renderbuffers。您可以使用視圖的可繪制屬性來控制這些對象的屬性,如清單3-1所示。如果更改GLKit視圖的大小,比例因子或可繪制屬性,則會在下次繪制內容時自動刪除并重新創建相應的framebuffer對象和renderbuffers。
Listing3-1

- (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實例啟用多采樣。多采樣是一種抗鋸齒形式,可以平滑鋸齒狀邊緣,以更多的內存和片段處理時間為代價,以大多數3D應用程序的圖像質量提升,如果啟用多采樣,則始終測試應用程序的性能,以確保其仍然可以接受。

繪制GLKit視圖

圖3-1概述了繪制OpenGL ES內容的三個步驟:準備OpenGL ES基礎設施,發布繪圖命令,并將呈現的內容呈現給Core Animation進行顯示。 GLKView類實現了第一和第三步。對于第二步,您將實現一個繪圖方法,如清單3-2中的示例所示。
Listing3-2

- (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函數提示OpenGL ES可以丟棄任何現有的幀緩沖區內容,避免了昂貴的內存操作將以前的內容加載到內存中。為了確保最佳性能,您應該在繪制之前始終調用此函數。

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

  • 在調用繪圖方法之前,該視圖:
  • 使其EAGLContext對象成為當前上下文
  • 根據當前大小,比例因子和可繪制屬性(如果需要)創建一個framebuffer對象和renderbuffers
  • 將framebuffer對象綁定為繪制命令的當前目標
  • 設置OpenGL ES視口以匹配幀緩沖區大小
  • 在您的繪圖方法返回后,視圖:
  • 解決多采樣緩沖區(如果啟用了多次采樣)
  • 丟棄其內容不再需要的renderbuffers
  • 向Core Animation呈現renderbuffer內容以進行緩存和顯示

使用委托對象呈現

許多OpenGL ES應用程序在自定義類中實現渲染代碼。這種方法的優點在于它允許您通過為每個渲染算法定義不同的渲染器類來輕松支持多種渲染算法。共享公共功能的渲染算法可以從超類繼承。例如,您可以使用不同的渲染器類來支持OpenGL ES 2.0和3.0(請參閱配置OpenGL ES上下文)。或者您可以使用它們來定制渲染,從而在具有更強大硬件的設備上獲得更好的圖像質量

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

- (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;
}

GLKit視圖控制器動畫化OpenGL ES內容

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

3-2.png

了解動畫循環
對于更新階段,視圖控制器調用其自己的更新方法(或其委托的glkViewControllerUpdate:方法)。在這種方法中,你應該準備繪制下一幀。例如,游戲可能會使用這種方法根據自最后一幀以來接收到的輸入事件來確定玩家和敵人角色的位置,科學可視化可能會使用此方法來運行其模擬步驟。如果您需要時間信息來確定應用的下一幀的狀態,請使用其中一個視圖控制器的時間屬性,如timeSinceLastUpdate屬性。在圖3-2中,更新階段增加一個角度變量,并使用它來計算變換矩陣。

對于顯示階段,視圖控制器調用其視圖的顯示方法,該方法又調用您的繪圖方法。在繪圖方法中,您可以向GPU提交OpenGL ES繪圖命令以呈現內容。為了獲得最佳性能,您的應用程序應在渲染新幀時開始修改OpenGL ES對象,之后提交繪圖命令。在圖3-2中,顯示階段將著色器程序中的均勻變量設置為在更新階段計算的矩陣,然后提交繪圖命令以呈現新內容。

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

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

使用GLKit視圖控制器

清單3-4演示了使用GLKViewController子類和GLKView實例渲染動畫OpenGL ES內容的典型策略。

@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方法創建一個OpenGL ES上下文并將其提供給視圖,并且還設置動畫循環的幀速率。

視圖控制器自動地代表其視圖,因此它實現了動畫循環的更新和顯示階段。在更新方法中,它計算顯示旋轉行星所需的變換矩陣。在glkView:drawInRect:方法中,它將這些矩陣提供給著色器程序,并提交繪圖命令來渲染行星幾何。

使用GLKit開發您的渲染器

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

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

從OpenGL ES 1.1固定功能管道遷移
OpenGL ES 2.0及更高版本刪除與OpenGL ES 1.1固定功能圖形管道相關聯的所有功能。 GLKBaseEffect類為OpenGL ES 1.1流水線的轉換,照明和陰影階段提供了Objective-C模擬,GLKSkyboxEffect和GLKReflectionMapEffect類增加了對常見視覺效果的支持。有關詳細信息,請參閱這些類的參考文檔。

加載紋理數據
GLKTextureLoader類提供了一種簡單的方法來將紋理數據從iOS支持的任何圖像格式加載到OpenGL ES上下文中,同步或異步。 (請參閱使用GLKit框架加載紋理數據。)

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

推薦閱讀更多精彩內容