2012年6月8日 星期五

OpenGL ES 2.0 Tutorial Part 1 - 4

since: 2012/06/08
update: 2012/06/08


reference:
1. OpenGL ES 2.0 for iPhone Tutorial | Ray Wenderlich
2. I touchs: OpenGL ES 2.0 Tutorial Part 1 - 1
3. I touchs: OpenGL ES 2.0 Tutorial Part 1 - 2
4. I touchs: OpenGL ES 2.0 Tutorial Part 1 - 3
5. glVertexAttribPointer
6. glDrawElements

Creating Vertex Data

A. 為簡單的正方形建立頂點資料

    1. 說明:
        a. 正方形的頂點將被設置如下:
           V0: 座標(1, -1), 顏色:
           V1: 座標(1, 1), 顏色:
           V2: 座標(-1, 1), 顏色:
           V3: 座標(-1, -1), 顏色:

        b. 在 OpenGL ES 中, 只能使用三角形來繪製幾何圖形, 所以我們可以利用
            二個三角形來產生正方形, 如上圖所示: 一個三角形使用頂點 V0, V1, V2;
            另一個三角形使用頂點 V2, V3, V0.
            (說明: 你可以將頂點資料組成任何產生三角形的方式)

    2. 開啟 OpenGLView.m 檔案, 修改如下:
#import "OpenGLView.h"

/******************************/
/* start: add for vertex data */

// 用來儲存每個頂點的資訊: 位置(x,y,z), 顏色(r,g,b,a)
typedef struct {
    float Position[3];
    float Color[4];
} Vertex;

// 所有個別頂點的資訊:
// {位置1, 顏色1}, {位置2, 顏色2}, {位置3, 顏色3}, {位置4, 顏色4}
const Vertex Vertices[] = {
    {{1, -1, 0}, {1, 0, 0, 1}},
    {{1, 1, 0}, {0, 1, 0, 1}},
    {{-1, 1, 0}, {0, 0, 1, 1}},
    {{-1, -1, 0}, {0, 0, 0, 1}}
};

// 產生三角形的頂點索引:
//
// 每三個頂點組成一個三角形:
// 第一個三角形: 由頂點索引為 0, 1 與 2 所組成
// 第二個三角形: 由頂點索引為 2, 3 與 0 所組成
const GLubyte Indices[] = {
    0, 1, 2,
    2, 3, 0
};

/* end: add for vertex data */
/****************************/

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

B. 建立 Vertex Buffer Objects
     1. 說明:
         a. 要將頂點資料送至 OpenGL, 最佳方式是經由頂點緩衝區物件
              (Vertex Buffer Objects).

         b. 根本上來說, Vertex Buffer Objects 是一種 OpenGL 物件, 用來將頂點資料
              儲存到緩衝區裡.

         c. 有二種類型的 vertex buffer objects, 一種用來保存(追蹤)每個頂點的資料
             (像之前的 Vertices 陣列); 另一種用來保存(追蹤)建立三角形的頂點索引
             (像之前的 Indices 陣列).

     2. 開啟 OpenGLView.h 檔案, 修改如下:
....
//@add
- (GLuint)compileShader:(NSString *)shaderName withType:(GLenum)shaderType;
- (void)compileShaders;
- (void)setupVBOs; // Creating Vertex Buffer Objects

@end

     3. 開啟 OpenGLView.m 檔案, 修改如下:
....
//@add: Creating Vertex Buffer Objects
- (void)setupVBOs
{
    GLuint vertexBuffer;
    // Create a new Vertex Buffer object
    glGenBuffers(1, &vertexBuffer);
   
    // Bind "vertexBuffer" to GL_ARRAY_BUFFER
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
   
    // Send the data over to OpenGL-land
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
   
   
    GLuint indexBuffer;
    // Create a new Index Buffer object
    glGenBuffers(1, &indexBuffer);
   
    // Bind "indexBuffer" to GL_ELEMENT_ARRAY_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
   
    // Send the data over to OpenGL-land
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
....
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        //@add
        [self setupLayer]; // Set layer to opaque(不透明)      
        [self setupContext]; // Create OpenGL context               
        [self setupRenderBuffer]; // Create a render buffer       
        [self setupFrameBuffer]; // Create a frame buffer   
       
        //@add
        [self compileShaders];
        [self setupVBOs]; // Creating Vertex Buffer Objects
       
        [self render]; // render something  
    }
    return self;
}
....

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

C. 更新繪圖的程式碼

      1. 開啟 OpenGLView.m 檔案, 修改如下:
....
//@add: render something
- (void)render
{
    // Specify the RGB and alpha (transparency) values to use when clearing the screen.
    //
    //glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0); // 除法會降低效能
    glClearColor(0.0, 0.4078, 0.2157, 1.0);
   
    // Actually perform the clearing
    glClear(GL_COLOR_BUFFER_BIT);
   
    //@add: draw our new vertex data to the screen
    //
    // 1. 設定要繪製在 UIView 上的範圍, 此處為畫到整個視窗上
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
   
    // 2. 定義頂點的屬性資料(之後便可設定 vertex shader 的 attributes 變數之值:
    //     Position, SourceColor)

    //
    // a. 當在繪製時, OpenGL 會從儲存在緩衝區物件裡的陣列取得頂點資料.
    // b. 我們需要告訴 OpenGL 儲存在緩衝區物件裡的頂點陣列資料是什麼格式.
    // c. 亦即, 我們需要告訴 OpenGL 如何去理解儲存在緩衝區裡的陣列資料.
    // d. 當 glVertexAttribPointer 函式被呼叫時, 它會參照到此時任何綁定到
    //    GL_ARRAY_BUFFER 的緩衝區,
在此處為 setupVBOs 函式中的 vertexBuffer.
    //
    // void glVertexAttribPointer(GLuint  index
    //                            GLint  size
    //                            GLenum  type
    //                            GLboolean  normalized
    //                            GLsizei  stride
    //                            const GLvoid *  pointer);
    //
    // 參數說明
    // index: 即將被設定的 vertex shader 的 attribute 之索引位置,
    //            之前已在 compileShaders 函式中呼叫 glGetAttribLocation 取得.
    //
    // size: 每個頂點屬性的元素數量, 在此為:位置 3 個(x,y,z); 顏色 4 個(r,g,b,a). 
    //
    // type: 每個元素的資料型別, 在此, 位置與顏色皆為 GL_FLOAT.
    //
    // normalized: 是否要對頂點進行正規化處理, 在此為否: GL_FALSE
    //
    // stride(跨度): 二個連續頂點屬性之間的位元偏移量
    //              亦即, 包含個別頂點資料的資料結構之大小, 在此皆為: sizeof(Vertex)  
    //
    // pointer: 在儲存該屬性的資料結構中, 此屬性資料的起始偏移量
    //
    // typedef struct {
    //     float Position[3];
    //     float Color[4];
    // } Vertex;
    //
    // 說明:
    // a. Position 資料是位於 struct 的起始處, 因此偏移量為: 0.
    // b. Color 資料是位於 Position 資料之後, 因此偏移量為: sizeof(float) * 3.
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), 0);

    glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), (GLvoid *) (sizeof(float) * 3));
   
    // 3. 對每個傳入的頂點呼叫 vertex shader, 然後對每個像素呼叫 fragment shader,
    //    最後顯示在螢幕上.
    //
    // void glDrawElements(GLenum  mode
    //                     GLsizei  count
    //                     GLenum  type
    //                     const GLvoid *  indices);
    //
    // 參數說明
    // mode: 要繪製的基本圖形種類
    //
    // count: 要繪製的頂點數量
    //
    // type: 索引陣列中, 每個個別索引的資料型別(GLubyte)
    //
    // indices: 指向索引陣列的指標.
    //               在此為特例, 由於使用了 Vertex Buffer Objects, 因此它會使用
    //               在 setupVBOs 函式中與 GL_ELEMENT_ARRAY_BUFFER 綁定,
    //               儲存在 Index Buffer object 裡的 Indices 陣列.
    glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),
                   GL_UNSIGNED_BYTE, 0);
   
    // Present the render/color buffer to the UIView's layer!
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}
....

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

D. 編譯並執行:
        說明:
        預設情況下, OpenGL 的 "攝影機" 是位於 (0, 0, 0), 並朝著 Z 軸方向望去.
        左下角的座標為(-1, -1), 右上角的座標為(1, 1), "攝影機"的可視範圍剛好
        涵蓋住我們所建立的正方形. 所以整個螢幕範圍都是繪製的區域.        

沒有留言:

張貼留言

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