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. 說明: 為了讓每次練習的繪圖程式碼能獨自分開, 而不是在同一個方法中
(ViewController 的 drawView:), 利用註解來區分, 因此做了些調整:
a. 在 GLView.h 裡, 針對不同的繪圖, 而在 <GLViewDelegate> protocol 的
宣告中新增不同的繪圖方法.
b. 在 ViewController.m 裡, 實作 <GLViewDelegate> protocol 所宣告的
不同繪圖方法.
c. 最後在 GLView.m 的 drawView 裡, 將原本呼叫
[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. 編譯並執行
由於 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];
}
....
你好, 感謝你的文章.
回覆刪除我follow你的內容 可以建構出OpenGLES的環境了.
但是我想請問一下 我想要新增一個xib 檔 且新增toolbar 在畫面下.
讓openGL畫面下新增toolbar 跟幾個按鈕可以作用, 但是都無法出現, 這是為何?
您好, 不客氣 ~
回覆刪除說明如下:
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
所以, 你也可以自行調整了....
以上說明, 希望有幫助 ~
你好:
回覆刪除我確定是被opengl覆蓋了.
然後我嘗試建構一個 OpenGL Game 的 project, 在xib檔新增一個toolbar
可以做到我的需求
但是你的內容使用的opengl架構是我比較熟悉的.所以我傾向於使用你建構的方式.
我有嘗試將你的內容改寫到opengl標準project上.
但是還是出現整片的opengl環境, toolbar還是不見蹤影
我在想是不是有辦法 我在xib上新增一個View元件 再把opengl架在上面
但是不知道從何下手!
你好:
回覆刪除覺得這個功能還不錯, 所以直接把它實作了 ~
I touchs: OpenGL ES 入門: 番外篇之 UIView 元件
http://itouchs.blogspot.com/2012/04/opengl-es-uiview.html