2012年4月3日 星期二

OpenGL ES 入門: 二. 簡單繪圖之三

since: 2012/04/03
update: 2012/04/03

reference:
1. 原文:
iPhone Development: OpenGL ES From the Ground Up, Part 2: A Look at Simple Drawing

2. 翻譯:
從零開始學習OpenGL ES之二 – 簡單繪圖概述


簡單繪圖之三: 畫正方形

A. 架構微調
      1. 說明: 為了讓每次練習的繪圖程式碼能獨自分開, 而不是在同一個方法中
                     (ViewControllerdrawView:), 利用註解來區分, 因此做了些調整:

                     a. 在 GLView.h 裡, 針對不同的繪圖, 而在 <GLViewDelegate> protocol
                         宣告中新增不同的繪圖方法.

                     b. 在 ViewController.m 裡, 實作 <GLViewDelegate> protocol 所宣告的
                          不同繪圖方法.

                     c. 最後在 GLView.mdrawView 裡, 將原本呼叫
                         [self.delegate drawView:self];
                         改成呼叫: [self.delegate 個別的繪圖方法];

      2. 開啓 GLView.h 檔案, 修改如下:
....
//@add for protocol
@protocol GLViewDelegate

@required
- (void)setupView:(GLView *)view;

@optional
//@update for drawing
- (void)drawView:(GLView *)view;
- (void)drawTriangle3D; // 畫三角形

@end

      3. 開啓 ViewController.m 檔案, 修改如下:
....
- (void)drawView:(GLView *)view
{
/*
....
*/
}

// 畫三角形
- (void)drawTriangle3D
{
    NSLog(@"ViewController => drawTriangle3D");
   
    //@add for Rendering Frequency
    static GLfloat rotation = 0.0;
   
    // 建立三個頂點
    //
    // 三個頂點的 z 值是一樣的,其值(-3.0)是處於原點 "之後" 的
    Vertex3D    vertex1 = Vertex3DMake(0.0, 1.0, -3.0);
    Vertex3D    vertex2 = Vertex3DMake(1.0, 0.0, -3.0);
    Vertex3D    vertex3 = Vertex3DMake(-1.0, 0.0, -3.0);
   
    // 建立三角形
    Triangle3D  triangle = Triangle3DMake(vertex1, vertex2, vertex3);
   
    // 將 OpenGL 中的矩陣設為單位矩陣.
    // 所謂的單位矩陣是矩陣乘法下的單位元素,
    // 任何矩陣乘上單位矩陣都還是等於自己
    //
    // 所以 glLoadIdentity() 的作用是不希望之前的矩陣資料,
    // 殘留到現在的運算.
    glLoadIdentity();
   
    //@add for Rendering Frequency
    glRotatef(rotation, 0.0, 0.0, 1.0);
   
    // 告訴 OpenGL 所有的繪製工作是在一個灰色背景上進行.
    //
    // 設定: R, G, B, alpha(1.0, 代表完全不透明)
    // 白色: (1.0, 1.0, 1.0, 1.0)
    // 黑色: (0.0, 0.0, 0.0, 1.0)
    glClearColor(0.7, 0.7, 0.7, 1.0);
   
    // 通知 OpenGL 清除以前的一切圖形並將其設為 clear 顏色.
    //
    // color buffer - 顏色緩存, 保存當前 frame 各像素的顏色, 基本上就是你在屏幕上
    //                          看到的.

    //
    // depth buffer - 深度緩存(z-buffer), 保存每個潛在像素離觀察者距離的信息,
    //                          使用此信息可以確定一個像素是否需要被繪製出來
    //
    // 記住: 在繪製一個 frame 之前, 必須清除這兩個緩存以保證不會和以前的內容混雜.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
    // 啟動 OpenGL 的 vertex arrays(頂點陣列)的特性.
    // 作為基本準則, 我們可以先啟動最後再關閉我們使用的功能
    //
    glEnableClientState(GL_VERTEX_ARRAY);
   
    // 設置繪圖時所需的顏色
    // 此行代碼將繪圖顏色設為鮮艷的紅色
    //
    // 現在, 直到下次調用 glColor4f() 前所有的圖形都是以紅色繪製
    // 有一些例外的情況, 例如繪製紋理形狀時, 但基本上, 這樣設定顏色可以使顏色保持.
    glColor4f(1.0, 0.0, 0.0, 1.0);
   
    // 由於我們使用頂點陣列, 我們必須通知 OpenGL 頂點的陣列在什麼地方.
    //
    // 頂點陣列只是一個 GLfloat 的 C 陣列, 每三個值代表一個頂點.
    // 我們創建了 Triangle3D 物體, 但在內存中, 它完全等同於 9 個連續的 GLfloat,
    // 所以我們可以傳遞此三角形物體的位址.
    //
    // glVertexPointer()
    // 第一個參數, 指示了多少個 GLfloat 代表一個頂點
    // 第二個參數, 告訴 OpenGL 頂點是由 GLfloat 構成
    glVertexPointer(3, GL_FLOAT, 0, &triangle);
   
    // 通知 OpenGL 通過剛才提交的頂點陣列來繪製三角形
    //
    // 第一個參數, 告訴 OpenGL 繪製什麼.
    // 儘管 OpenGL ES 不支持繪製三角形之外的四邊形或其他多邊形, 但它仍然支持
    // 一些其他繪圖模式,
如繪製點, 線, 線迴路, 三角形條和三角形扇.
    glDrawArrays(GL_TRIANGLES, 0, 9);
   
    // 最後, 我們要禁止先前啟動了的特性以保證不會被其他地方的代碼弄混
    glDisableClientState(GL_VERTEX_ARRAY);
   
    //@add for Rendering Frequency
    rotation += 0.5;
}
....

      4. 開啓 GLView.m 檔案, 修改如下:
....
- (void)drawView
{
    NSLog(@"step 06. GLView => drawView");
       
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFrameBuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderBuffer);
   
    // GO: step 07. ViewController => drawView:
    //[self.delegate drawView:self];
    //@update for drawing
    [self.delegate drawTriangle3D]; // 畫三角形
   
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER_OES];
   
    //@add for Rendering Frequency
    int intCount = [self.currentRunCount intValue] + 1;
    self.currentRunCount = [NSNumber numberWithInt:intCount];
   
    if (intCount >= self.totalRunTime * kRenderingFrequency) {

        [self stopAnimation];
    }
}
....

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

B. 畫正方形之前置作業
       1. 開啓 GLView.h 檔案, 修改如下:
....
//@add for protocol
@protocol GLViewDelegate

@required
- (void)setupView:(GLView *)view;

@optional
//@update for drawing
- (void)drawView:(GLView *)view;
- (void)drawTriangle3D; // 畫三角形
- (void)drawSquare; // 畫正方形

@end

       2. 開啓 ViewController.m 檔案, 修改如下:
....
// 畫正方形
- (void)drawSquare
{
    NSLog(@"ViewController => drawSquare");
    // Draw code here
}
....

       3. 開啓 GLView.m 檔案, 修改如下:
....
- (void)drawView
{
....
    //@update for drawing
    //[self.delegate drawTriangle3D]; // 畫三角形
    [self.delegate drawSquare]; // 畫正方形
....
}
...
----------------------------------------------------------------------------------

C. 畫正方形
方法一
     1. 開啓 ViewController.m 檔案, 修改如下:
....
// 畫正方形
- (void)drawSquare
{
    NSLog(@"ViewController => drawSquare");
    // Draw code here
    //
    // 定義一個包含兩個 Triangle3D 物體的陣列
    Triangle3D  triangle[2];
   
    triangle[0].v1 = Vertex3DMake(0.0, 1.0, -3.0);
    triangle[0].v2 = Vertex3DMake(1.0, 0.0, -3.0);
    triangle[0].v3 = Vertex3DMake(-1.0, 0.0, -3.0);
    triangle[1].v1 = Vertex3DMake(-1.0, 0.0, -3.0);
    triangle[1].v2 = Vertex3DMake(1.0, 0.0, -3.0);
    triangle[1].v3 = Vertex3DMake(0.0, -1.0, -3.0);
   
    glLoadIdentity();
    glClearColor(0.7, 0.7, 0.7, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnableClientState(GL_VERTEX_ARRAY);
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glVertexPointer(3, GL_FLOAT, 0, &triangle);
    glDrawArrays(GL_TRIANGLES, 0, 18);
    glDisableClientState(GL_VERTEX_ARRAY);
}
....

     2. 編譯並執行

     3. 說明
         由於 Vertex3DMake() 方法在堆疊中創建新的 Vertex3D,  然後複製其值到
         陣列中而造成額外的記憶體被佔用,因此上述代碼並不理想。

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

D. 畫正方形
方法二
      1. 開啓 OpenGLESCommon.h 檔案, 修改如下:
....
#pragma mark -
#pragma mark Vertex3D
#pragma mark -
....
// 設定現存頂點的值
static inline void Vertex3DSet(Vertex3D *vertex, CGFloat inX, CGFloat inY, CGFloat inZ)
{
    vertex->x = inX;
    vertex->y = inY;
    vertex->z = inZ;
}
....

      2. 開啓 ViewController.m 檔案, 修改如下:
....
// 畫正方形
- (void)drawSquare
{
    NSLog(@"ViewController => drawSquare");
    // Draw code here
    //
    /*
    // 定義一個包含兩個 Triangle3D 物體的陣列
    Triangle3D  triangle[2];
   
    triangle[0].v1 = Vertex3DMake(0.0, 1.0, -3.0);
    triangle[0].v2 = Vertex3DMake(1.0, 0.0, -3.0);
    triangle[0].v3 = Vertex3DMake(-1.0, 0.0, -3.0);
    triangle[1].v1 = Vertex3DMake(-1.0, 0.0, -3.0);
    triangle[1].v2 = Vertex3DMake(1.0, 0.0, -3.0);
    triangle[1].v3 = Vertex3DMake(0.0, -1.0, -3.0);
    */
   
    //@update
    Triangle3D  *triangles = malloc(sizeof(Triangle3D) * 2);
   
    Vertex3DSet(&triangles[0].v1, 0.0, 1.0, -3.0);
    Vertex3DSet(&triangles[0].v2, 1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[0].v3, -1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[1].v1, -1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[1].v2, 1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[1].v3, 0.0, -1.0, -3.0);
   
    glLoadIdentity();
    glClearColor(0.7, 0.7, 0.7, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnableClientState(GL_VERTEX_ARRAY);
    glColor4f(1.0, 0.0, 0.0, 1.0);
    //glVertexPointer(3, GL_FLOAT, 0, &triangle);
    //@update
    glVertexPointer(3, GL_FLOAT, 0, triangles);
    glDrawArrays(GL_TRIANGLES, 0, 18);
    glDisableClientState(GL_VERTEX_ARRAY);
   
    if (triangles != NULL)
        free(triangles);
}
....

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

E. 畫正方形
方法三
      1. 說明: 使用 triangle strips (GL_TRIANGLE_STRIP)方法通過四個頂點
                     (12個 GLfloat)來繪製正方形
.

      2. 開啓 ViewController.m 檔案, 修改如下:
....
// 畫正方形
- (void)drawSquare
{
    NSLog(@"ViewController => drawSquare");
    // Draw code here
    //
    /*
    Triangle3D  *triangles = malloc(sizeof(Triangle3D) * 2);
   
    Vertex3DSet(&triangles[0].v1, 0.0, 1.0, -3.0);
    Vertex3DSet(&triangles[0].v2, 1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[0].v3, -1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[1].v1, -1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[1].v2, 1.0, 0.0, -3.0);
    Vertex3DSet(&triangles[1].v3, 0.0, -1.0, -3.0);
    */
   
    //@update
    Vertex3D  *vertices = malloc(sizeof(Vertex3D) * 4);
   
    Vertex3DSet(&vertices[0], 0.0, 1.0, -3.0);
    Vertex3DSet(&vertices[1], 1.0, 0.0, -3.0);
    Vertex3DSet(&vertices[2], -1.0, 0.0, -3.0);
    Vertex3DSet(&vertices[3], 0.0, -1.0, -3.0);
   
    glLoadIdentity();
    glClearColor(0.7, 0.7, 0.7, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnableClientState(GL_VERTEX_ARRAY);
    glColor4f(1.0, 0.0, 0.0, 1.0);
    //glVertexPointer(3, GL_FLOAT, 0, triangles);
    //@update
    glVertexPointer(3, GL_FLOAT, 0, vertices);
   
    //glDrawArrays(GL_TRIANGLES, 0, 18);
    //@update
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 12);
   
    glDisableClientState(GL_VERTEX_ARRAY);
   
    //if (triangles != NULL)
    //    free(triangles);
    //@update
    if (vertices != NULL)
        free(vertices);
}
....

      3. 開啓 GLView.m 檔案, 修改如下:
....
//@add
- (void)drawView
{
    NSLog(@"step 06. GLView => drawView");
       
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFrameBuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderBuffer);
   
    // GO: step 07. ViewController => drawView:
    //[self.delegate drawView:self];
    //@update for drawing
    //[self.delegate drawTriangle3D]; // 畫三角形
    [self.delegate drawSquare]; // 畫正方形
   
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER_OES];
   
    //@add for Rendering Frequency
    /*
    int intCount = [self.currentRunCount intValue] + 1;
    self.currentRunCount = [NSNumber numberWithInt:intCount];
   
    if (intCount >= self.totalRunTime * kRenderingFrequency) {

        [self stopAnimation];
    }
    */
    //@update for run once
    [self stopAnimation];
}
....

4 則留言:

  1. 你好, 感謝你的文章.
    我follow你的內容 可以建構出OpenGLES的環境了.
    但是我想請問一下 我想要新增一個xib 檔 且新增toolbar 在畫面下.
    讓openGL畫面下新增toolbar 跟幾個按鈕可以作用, 但是都無法出現, 這是為何?

    回覆刪除
  2. 您好, 不客氣 ~
    說明如下:

    1. 這一系列的文章是以純 OpenGL ES 為主, 所以在建專案的時候, 並沒有將 UI 的 xib 環境加進來. (應該可以調整)

    2. 你在 xib 上所新增的 UIController(toolbar), 可能最後被 OpenGL ES 的繪製動作所覆蓋上去了, 所以才無法看到.

    3. 先建一個 OpenGL Game 的 project, 它會夾帶一個 storyboard, 你可以先在 storyboard 上放一些: toolbar, 按鈕 ... 跑跑看, 是不是你想要的效果. (先不管上面的動畫)

    4. 解決方式之一:
    直接建立一個 OpenGL Game 的 project, 保留 storyboard 跟基本的 code, 刪除其餘的 code, 可以參考: Filter4Cam 實作: 1. 新增專案
    http://itouchs.blogspot.com/2012/02/filter4cam-1_17.html
    的 A 與 D, 接著再自行增加 OpenGL ES 的相關 code.

    5. 解決方式之二:
    先前, 你會發現當新增一個 OpenGL Game 的 project 時, 最大的不同點在:
    a. 你的 ViewController 是繼承 GLKViewController 而非 UIViewController
    b. 你的 ViewController 下的 view 是繼承 GLKView 而非 UIView
    所以, 你也可以自行調整了....

    以上說明, 希望有幫助 ~

    回覆刪除
  3. 你好:
    我確定是被opengl覆蓋了.
    然後我嘗試建構一個 OpenGL Game 的 project, 在xib檔新增一個toolbar
    可以做到我的需求

    但是你的內容使用的opengl架構是我比較熟悉的.所以我傾向於使用你建構的方式.
    我有嘗試將你的內容改寫到opengl標準project上.
    但是還是出現整片的opengl環境, toolbar還是不見蹤影
    我在想是不是有辦法 我在xib上新增一個View元件 再把opengl架在上面
    但是不知道從何下手!

    回覆刪除
  4. 你好:
    覺得這個功能還不錯, 所以直接把它實作了 ~
    I touchs: OpenGL ES 入門: 番外篇之 UIView 元件
    http://itouchs.blogspot.com/2012/04/opengl-es-uiview.html

    回覆刪除

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