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. GLKitiOS 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 bufferframe 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 的一些屬性(contextdelegate), 在此順便
          介紹 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 buffercolor 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 (方法)
           這是用手動的方式, 從目前 viewcontext 中, 取得一個 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. GLKViewOpenGL 在處理 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. 編譯並執行
         你會看到脈動式的 "緊急警報" 效果 (黑 -> 漸紅 -> 紅)



沒有留言:

張貼留言

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