2012年6月23日 星期六

OpenGL ES 2.0 with GLKit Part 1 - 1

since: 2012/06/22
update: 2012/06/23


reference:
1.
Beginning OpenGL ES 2.0 with GLKit Part 1 | Ray Wenderlich

GLKView

A. 說明:
      1. GLKit 是 iOS 5 的新 API, 它可以讓用 OpenGL ES 來開發 App 較以往更為容易.
         
      2. GLKit 包含四個主要的部分:
          a. GLKView/GLKViewController
              這些類別將以往在 OpenGL ES 中, 所使用的一成不變的程式碼作大量的
              抽象化處理, 以方便來設置基本的 OpenGL ES 專案.   

          b. GLKEffects
              這些類別實作了使用在 OpenGL ES 1.0 中普遍的著色行為(shading behaviors),
              使得轉變到 OpenGL ES 2.0 更加容易. 它們也是使用手工的方式來處理一些
              基本的光源與貼圖的工作.

          c. GLMath
              在 iOS 5 的遊戲中, 相當重要的是每個遊戲都需要有各自的數學函式庫,
              而這些數學函式庫都包含共同常規的向量與矩陣運算. 現在使用 GLMath,
              將提供大部份共同常規的數學函式.

          d. GLKTextureLoader
              這個類別使得在 OpenGL ES 中, 載入圖片作為紋理貼圖更加容易. 較以往
              必須寫一個複雜的方法來處理不同圖片的檔案格式; 現在, 只要呼叫單一的
              方法, 就可以載入紋理了.

--------------------------------------------------------------------------------

B. 新增專案
      1. Xcode > File > New > Project... > iOS > Application >
          Empty Application > Next
        Product Name: HelloGLKit
        Company Identifier: com.blogspot
        Device Family: iPhone
        Use Automatic Reference Counting: checked
        > Next > Create

      2. 編譯並執行:
          得到全白的畫面

--------------------------------------------------------------------------------

C. GLKView 介紹
     1. 說明:
         a. 要開始使用 OpenGL ES 2.0 的第一件事, 是幫 window (UIWindow) 新增
             一個 subview, 使它可以利用 OpenGL 來繪製物體. 如果你之前有用一般的
             方式來寫過 OpenGL ES 2.0 的程式, 就會知道需要使用大量一成不變(刻版)
             的程式碼來完成, 例如: 建立 render buffer 與 frame buffer 等等.   

         b. 現在, 可以輕易的使用 GLKit 的新類別: GLKView 來完成. 無論何時, 當你
             想要在一個 view 裡使用 OpenGL 來繪製, 你只需簡單地新增一個 GLKView
             (它是 UIView 的一個標準子類別) 並且設定好一些屬性即可.    

         c. 接著, 你可以設定一個類別作為 GLKView 的委派(delegate), 當需要被繪製時,
             該類別的某個方法就會被呼叫. (你可以在這個方法中, 放入你的 OpenGL
             繪圖指令)

**********************************************

     2. 新增 Framework:
     QuartzCore
     - Add 2D graphics rendering support.

     OpenGLES
     
- Is used for visualizing 2D and 3D data.

     GLKit
     - P
rovides libraries of commonly needed functions and classes to reduce the effort
       needed to create a new OpenGL ES 2.0 application or the effort required to port
       an existing OpenGL ES 1.1 application to OpenGL ES 2.0.


**********************************************

     3. 開啟 AppDelegate.h 檔案, 修改如下:
#import <UIKit/UIKit.h>
//@add
#import <GLKit/GLKit.h>

//@interface AppDelegate : UIResponder <UIApplicationDelegate>
//@update for set as GLKView's delegate
@interface AppDelegate : UIResponder <UIApplicationDelegate, GLKViewDelegate>

@property (strong, nonatomic) UIWindow *window;

@end


**********************************************

     4. 開啟 AppDelegate.m 檔案, 修改如下:
....
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    //@add for GLKView
    //
    // step 1: Create a OpenGL context
    //
    // 1-1: An EAGLContext manages all of the information iOS needs to draw
    //         with OpenGL.

    // 1-2: And specify what version of the API you want to use.(Here, OpenGL ES 2.0)
    EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
   
    // step 2: Create a GLKView
    //
    // This creates a new instance of a GLKView, and makes it as large as the
    // entire window.

    GLKView *view = [[GLKView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   
    // step 3: Set the GLKView's context
    //
    // Tell GLKView to use the OpenGL context.
    view.context = context;
   
    // step 4: Set the GLKView's delegate
    //
    // 4-1: This sets the current class (AppDelegate) as the GLKView's delegate.
    // 4-2: This means whenever the view needs to be redrawn, it will call
    //      a method named glkView:drawInRect on whatever class you specify here.
    //      We will implement this inside the App Delegate shortly to contain
    //      some basic OpenGL commands to paint the screen red.
    view.delegate = self;
   
    // step 5: Add the GLKView as a subview
    [self.window addSubview:view];
   
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
....
#pragma mark - GLKViewDelegate
//@add: for implement delegate method
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    // specify the RGB and alpha (transparency) values to use when clearing the screen.
    glClearColor(1.0, 0.0, 0.0, 1.0);
   
    // actually perform the clearing.
    glClear(GL_COLOR_BUFFER_BIT);
}

@end

**********************************************

     5. 編譯並執行:
        對於完全是 OpenGL 新手的人來說, 可能無法感受到;
        對於以前曾用一般 OpenGL 的方式做過的人而言, 的確是方便許多了.

--------------------------------------------------------------------------------

D. 選擇性的參考資料: GLKView 的其它屬性與方法
      1. 說明:
          目前我們只設定了 GLKView 的一些屬性(context 與 delegate), 在此順便
          介紹 GLKView 的其它屬性與方法, 將來可能會用到.

      2. drawableColorFormat (屬性)
           a. OpenGL context 有一個用來儲存顯示在螢幕上的顏色之 buffer. 你可以
               使用這個屬性來設定在 buffer 中, 每個像素的顏色格式.

           b. 這個屬性的預設值是: GLKViewDrawableColorFormatRGBA8888,
               代表著: 在 buffer 中, 針對 RGBA 各自都使用了 8 bits(因此每個像素
               使用了 4 bytes), 這樣的好處是: 使用了最寬廣的顏色範圍, 可以讓 App
               看起來較美觀.

           c. 但是, 如果你的 App 可以僅使用較低範圍的顏色, 你可能想要將此屬性
               的值設為: GLKViewDrawableColorFormatRGB565, 這會讓你的 App
               花費較少的資源(記憶體與處理時間).

      3. drawableDepthFormat (屬性)
           a. OpenGL context 也可以選擇性地連結另一個 buffer: depth buffer. 它可以
               幫助確認當物體較靠近觀察者前方(相對於較遠的)時, 才顯示其像素.

           b. 這個方式預設為: OpenGL 將最靠近觀察者的物件之每個像素儲存到 buffer 裡. 
               當要開始繪製出像素時, 它會檢查 depth buffer 看看是否已經有畫出較接近
               觀察者的像素, 如果有的話便將目前的像素摒棄掉, 否則就將此像素加入到
               depth buffer 與 color buffer 裡.

           c. 你可以設定這個屬性來選擇 depth buffer 的形式, 預設值為:
               GLKViewDrawableDepthFormatNone, 代表沒有啟用 depth buffer 功能.

           d. 但是如果你想要啓用此功能(通常使用在 3D 遊戲中), 你應當選擇:
               GLKViewDrawableDepthFormat16 或 GLKViewDrawableDepthFormat24.
               二者差別在於: 設為 GLKViewDrawableDepthFormat16 時, 你的 App 會使用
               較少的資源, 但是當二個物體非常靠近時, 就可能會發生渲染的問題.

      4. drawableStencilFormat (屬性)
           a. OpenGL context 可以連結的另一個選擇性 buffer 為: stencil buffer.
               模板緩衝可以協助你限制只在螢幕的特定部分作繪製的工作.
               通常它對於處理某些東西特別有用, 例如: 陰影. 你可以使用 stencil buffer
               來確保陰影投射(cast)到地板上. 

           b. 這個屬性的預設值為: GLKViewDrawableStencilFormatNone, 代表:
               沒有使用 stencil buffer, 但是你可以啟用它, 設定為唯一可選的值:
               GLKViewDrawableStencilFormat8. 

      5. drawableMultisample (屬性)
           a. 最後一個可經由 GLKView 屬性設定的選擇性 buffer 為: multisampling buffer.
               如果你曾經用 OpenGL 來畫線, 並且注意到 "鋸齒狀線條" 情形, 多重採樣緩衝
               可以協助處理這個問題(抗鋸齒化).

           b. 基本上它所處理的是: 取代對每個像素呼叫一次 fragment shader 的作法.
               而將像素劃分為更小的單位, 並且在較小層級的細節處理上多次呼叫
               fragment shader. 其結果通常會使幾何物體的邊緣顯得更加光滑.

           c. 但是, 小心設置這個屬性, 因為它需要花費更多的處理時間與記憶體.
               預設值為: GLKViewDrawableMultisampleNone, 但是你可以啟用它,
               設定為唯一可選的值: GLKViewDrawableMultisample4X.  

      6. drawableHeight/drawableWidth (屬性)
           這些是唯讀的屬性, 用來指出 buffers 的高與寬. 以 view 的 bounds 與
           contentSize 為基準, 當 bounds 與 contentSize 改變時, buffers 就會自動調整大小.

      7. snapshot (方法)
           這是用手動的方式, 從目前 view 的 context 中, 取得一個 UIImage.

      8. bindDrawable (方法)
           a. OpenGL 還有另外一個 buffer: frame buffer, 基本上 frame buffer 是其它
               我們討論過的 buffers (color buffer, depth buffer, stencil buffer 等等) 之集合.

           b. 在呼叫 glkView:drawInRect 方法之前, GLKit 會在場景的背後將設定的屬性
               綁定到 frame buffer 裡. 但是, 如果你的遊戲 App 需要變更不同的 frame buffer
               來執行一些其它種類的渲染(例如, 你要使用不同的紋理貼圖), 你可以使用
               bindDrawable 方法來告訴 GLKit 去重新綁定到你設定的 frame buffer.

      9. deleteDrawable (方法)
           a. GLKView 與 OpenGL 在處理 buffers 時, 會使用到大量的實體記憶體. 假如
               你的 GLKView 無法顯示出來, 你可能會發現先暫時地取消配置記憶體, 直到
               內容再次可被顯示出來, 是有效的方式. 如果你想要這麼做, 只需要使用
               deleteDrawable 方法即可.

           b. 下一次, 當 view 要被繪製出來時, GLKView 會在場景的背後自動重新配置
               記憶體, 相當方便不是嗎?

      10. enableSetNeedsDisplay (屬性) and display (方法)
            a. 預設情況下, GLKView 只會在有需要時才會自行更新, 亦即: 當 views
                第一次被繪製出來, 長寬大小改變, 等等. 然而, 對於遊戲 App 而言,
                你經常需要去重新繪製每個 frame!

            b. 我們可以藉由將 enableSetNeedsDisplay 設為 false 來停用 GLKView 的
                這個預設行為. 然後, 當我們要更新螢幕內容時, 可藉由在 GLKView 上
                呼叫 display 方法, 來控制重新繪製的時機. 

--------------------------------------------------------------------------------

E. 更新 GLKView
      1. 開啓 AppDelegate.h 檔案, 修改如下:
#import <UIKit/UIKit.h>
//@add
#import <GLKit/GLKit.h>
#import <QuartzCore/QuartzCore.h> // used for CADisplayLink

//@interface AppDelegate : UIResponder <UIApplicationDelegate>
//@update for set as GLKView's delegate
@interface AppDelegate : UIResponder <UIApplicationDelegate, GLKViewDelegate>
{
    //@add
    float _curRed;
    BOOL _increasing;
}

@property (strong, nonatomic) UIWindow *window;

//@add
- (void)render:(CADisplayLink *)displayLink;

@end

********************************************

      2. 開啓 AppDelegate.m 檔案, 修改如下:
....
// step 01
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //@add
    _increasing = YES;
    _curRed = 0.0;
....
}

....
// step 02
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    //@add
    if (_increasing) {
        _curRed += 0.01;
    } else {
        _curRed -= 0.01;
    }
    if (_curRed >= 1.0) {
        _curRed = 1.0;
        _increasing = NO;
    }
    if (_curRed <= 0.0) {
        _curRed = 0.0;
        _increasing = YES;
    }
   
    // specify the RGB and alpha (transparency) values to use when clearing the screen.
    //glClearColor(1.0, 0.0, 0.0, 1.0);
    //@update
    glClearColor(_curRed, 0.0, 0.0, 1.0);
   
    // actually perform the clearing.
    glClear(GL_COLOR_BUFFER_BIT);
}

....
// step03
//@add: for synchronize the time we render with OpenGL
//             to the rate at which the screen refreshes.

- (void)render:(CADisplayLink *)displayLink
{
    GLKView *view = [self.window.subviews objectAtIndex:0];
    [view display];
}

....
// step04
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
....
    view.delegate = self;
   
    //@add: for synchronize
    view.enableSetNeedsDisplay = NO;

    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];

    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
   
    // step 5: Add the GLKView as a subview
    [self.window addSubview:view];
   
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
....

********************************************

      3. 編譯並執行
         你會看到脈動式的 "緊急警報" 效果 (黑 -> 漸紅 -> 紅)



沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。