2012年6月25日 星期一

OpenGL ES 2.0 with GLKit Part 2 - 2


since: 2012/06/25
update: 2012/06/25


reference:
1. Beginning OpenGL ES 2.0 with GLKit Part 2 | Ray Wenderlich
2. I touchs: OpenGL ES 2.0 with GLKit Part 2 - 1

GLKBaseEffect 實作

A. 建立一個 GLKBaseEffect
     開啟 HelloGLKitViewController.m 檔案, 修改如下:
....
// step 1:
@interface HelloGLKitViewController ()
{
    //@add
    float _curRed;
    BOOL _increasing;
    GLuint _vertexBuffer;
    GLuint _indexBuffer;
    GLKBaseEffect *effect;
    float _rotation;

}

//@add
@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLKBaseEffect *effect;

@end

....
// step 2:
- (void)setupGL
{
    // Set the current OpenGL context to the current context.
    [EAGLContext setCurrentContext:self.context];
   
    //@add for initialize GLKBaseEffect
    self.effect = [[GLKBaseEffect alloc] init]; 
....
}

....
// step 3:
- (void)tearDownGL
{
    [EAGLContext setCurrentContext:self.context];
   
    glDeleteBuffers(1, &_vertexBuffer);
    glDeleteBuffers(1, &_indexBuffer);
   
    //@add
    self.effect = nil;
}

....

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

B. 設定 GLKBaseEffect 屬性
     1. 說明:
          a. 第一個需要設定的屬性為: 投影矩陣. 投影矩陣是用來告訴 CPU 如何在 2D
              的平面上繪製出 3D 的幾何圖形.

          b. GLKit 提供了一些便利的函式來設定投影矩陣. 我們將使用其中一個來設定:
              (1). 沿著 Y 軸的視野角度 (fovy: field of view along the y-axis),
                     即上下二個平面的.
              (2). 寬與高的比例 (aspect ratio: w / h)
              (3). 近端與遠端平面 (near and far planes)

          c. Field of view 就像是相機的鏡頭. 較小的 field of view (例如: 10), 類似攝遠
              鏡頭 - 藉由將圖片向你拉近而使圖片放大. 較大的 field of view (例如: 100),
              類似廣角鏡頭 - 它讓所有物體看起來較遠. 一般設定 Field of view 的值介於:
              65 - 75 之間.

          d. Aspect ratio 是你想要繪製的方向比例(亦即, view 的方向比例). 它與 field
              of view 結合使用, 來決定沿著 X 軸的視野大小.

          e. 近端與遠端平面, 是場景中用來框住可視空間的容量邊界. 所以, 如果某個
              物體與眼睛(相機)的距離過近於近端平面或過遠於遠端平面, 它就不會被
              繪製出來. 這是一個普遍可能會遇到的問題 - 你在繪製一個物體, 但是它沒有
              在螢幕上顯示出來. 記得要去檢查: 這個物體是否位於 near plane 與 far plane
              之間.

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

     2. 開啟 HelloGLKitViewController.m 檔案, 修改如下:
       // 設定投影矩陣
....
- (void)update
{
    ....

    //@add: set the projection matrix
    //
    // 1. get the aspect ratio of the GLKView.
    float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
   
    // 2. Create a perspective matrix: (with GLKit math library)
    //
    // We set the near plane to 4 units away from the eye, and the far plane
    // to 10 units away.

    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 4.0f, 10.0f);
   
    // 3. Set the projection matrix on the effect's transform property
    self.effect.transform.projectionMatrix = projectionMatrix;
}
....

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

     3. 設定 modelView 矩陣
         a. 說明:
             ModelViewMatrix 是要繪製的幾何物體效果都需要套用的轉換.
             在此, 又藉由 GLKit 數學函式庫的協助, 來執行位移, 旋轉縮放的功能.

         b. 開啟 HelloGLKitViewController.m 檔案, 修改如下: 
....
- (void)update
{
    ....
    //@add: set the projection matrix
    ....
   
    //@add: set the modelViewMatrix
    //
    // 1. Move z-coordinate backwards.
    //    Create a matrix for us that translates 6 units(Z) backwards.
    //
    // Because when we set up the vertices for the square, the z-coordinate
    // for each vertex was 0.

    GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -6.0f); 
   
    // 2. Increment rotate variable
    _rotation += 90 * self.timeSinceLastUpdate;
   
    // 3. Rotating it
    modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(_rotation), 0, 0, 1);
   
    // 4. Set the model view matrix on the effect's transform property.
    self.effect.transform.modelviewMatrix = modelViewMatrix;
}
....

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

C. 在 GLKBaseEffect 上呼叫 prepareToDraw
     1. 說明:
         在將物體繪製到螢幕之前, 任何時候你在 GLKBaseEffect 上更改了屬性設定,
         都需要先呼叫此方法.

     2. 開啟 HelloGLKitViewController.m 檔案, 修改如下: 
....
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(_curRed, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
   
    //@add
    [self.effect prepareToDraw];
}
....

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

D. 啟用 "預先定義好的" 的屬性 
     1. 開啟 HelloGLKitViewController.m 檔案, 修改如下: 
....
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(_curRed, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
   
    //@add
    [self.effect prepareToDraw];
   
    //@add: Enable pre-defined attributes
    //
    // 1. bind the vertex and index buffers
    //
    // Every time before you draw, you have to tell OpenGL which vertex buffer objects
    // you should use.
So here we bind the vertex and index buffers we created earlier.
    //
    // Strictly, we didn't have to do this for this app (because they're already still bound
    // from before)
but usually you have to do this because in most games you use
    // many different vertex buffer objects.

    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
   
    // 2. Enable the pre-defined vertex attributes (position)
    //    and feed the correct values to input variables for the vertex shader.
    glEnableVertexAttribArray(GLKVertexAttribPosition);       

    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *) offsetof(Vertex, Position));
   
    // 3. Enable the pre-defined vertex attributes (color)
    //    and feed the correct values to input variables for the vertex shader.
    glEnableVertexAttribArray(GLKVertexAttribColor);

    glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *) offsetof(Vertex, Color));
}
....

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

     2. glVertexAttribPointer 參數說明:

        void glVertexAttribPointer(GLuint  index
                                   GLint  size
                                   GLenum  type
                                   GLboolean  normalized
                                   GLsizei  stride
                                   const GLvoid *pointer);

       a. index:
           即將被設定的 vertex shaderattribute 之索引位置,
           在此, 我們使用 GLKit 提供的 "預先定義好的" 常數值.
            (若不使用 GLKit 的話, 需呼叫 glGetAttribLocation 來取得)

       b. size:
           每個頂點屬性元素數量, 在此為:位置 3 個(x,y,z); 顏色 4 個(r,g,b,a).

       c. type:
           每個元素的資料型別, 在此, 位置顏色皆為 GL_FLOAT.

       d. normalized:
           是否要對頂點進行正規化處理, 在此為否: GL_FALSE

       e. stride:
           跨度: 二個連續頂點屬性之間的位元偏移量
           亦即, 包含個別頂點資料的資料結構之大小, 在此皆為: sizeof(Vertex)

       f. pointer:
           在儲存該屬性的資料結構中, 此屬性資料的起始偏移量
           在此, 可以使用 offsetof 運算子, 來找出在一個結構體中, 特定資料欄位
           起始偏移量.

           備註: 不然的話, 就要依照 struct 的內容來計算:  
            typedef struct {
            float Position[3];
            float Color[4];
            } Vertex;

            => Position 資料是位於 struct 的起始處, 因此偏移量為: 0.
            => Color 資料是位於 Position 資料之後, 因此偏移量為: sizeof(float) * 3.

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

E. 繪製幾何圖形
     1. 開啟 HelloGLKitViewController.m 檔案, 修改如下: 
....
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(_curRed, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
   
    //@add
    [self.effect prepareToDraw];
   
    //@add: Enable pre-defined attributes
    ....
   
    //@add: Draw geometry
    glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
}
....

     2. glDrawElements 參數說明:

        void glDrawElements(GLenum  mode
                           GLsizei  count
                           GLenum  type
                           const GLvoid *indices);

        a. mode:
            要繪製的基本圖形種類.

        b. count:
            要繪製的頂點數量.

        c. type:
            索引陣列中, 每個個別索引資料型別(GLubyte)

        d. indices:
            指向索引陣列的指標.

            備註:
            在此為特例, 由於使用了 Vertex Buffer Objects, 因此它會使用
            在 glkView:drawInRect: 函式中與 GL_ELEMENT_ARRAY_BUFFER 綁定,
            儲存在 Index Buffer object 裡的 Indices 陣列.

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

F. 編譯並執行:
     可以在螢幕上看到一個漂亮的旋轉方塊.
     (背景仍然是: 脈動式的 "緊急警報" 效果: 黑 -> 漸紅 -> 紅)

沒有留言:

張貼留言

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