IOS的OpenGL應用
1. 打開Xcode, 新建一個工程
選擇:IOS -> Application -> Single View Application模板
輸入工程名稱和基本信息,勾選“UseStoryboards”,然後創建
在“Build Phases”中,添加三個框架
QuartzCore.framework
添加 “#import<GLKit/glkit.h>”,並將它修改爲繼承"GLKViewController"
雙擊“MainStoryboard.storyboard”展開,選擇"view"
然後,在“Identity Inspector"中,將它的類改爲”GLKView“
好了,OpenGL的環境基本上搭建出來了。
基本上,所有的代碼都是加到ViewController.m文件中
1、添加全局屬性聲明
@interface ViewController ()
@property(strong,nonatomic)EAGLContext* context;
@property(strong,nonatomic)GLKBaseEffect* effect;
@end
@implementation ViewController
@synthesize context, effect;
2、 添加一組頂點數據這是一個正方形頂點數組。實際上它是二個三角形接合而成的
GLfloat squareVertexData[48] =
{
0.5f, 0.5f, -0.9f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.9f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.9f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.9f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.9f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.9f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
};
每行頂點數據的排列含義是:
頂點X、頂點Y,頂點Z、法線X、法線Y、法線Z、紋理S、紋理T。
在後面解析此數組時,將參考此規則。
頂點位置用於確定在什麼地方顯示,法線用於光照模型計算,紋理則用在貼圖中。
一般約定爲“頂點以逆時針次序出現在屏幕上的面”爲“正面”。
世界座標是OpenGL中用來描述場景的座標,Z+軸垂直屏幕向外,X+從左到右,Y+軸從下到上,是右手笛卡爾座標系統。我們用這個座標系來描述物體及光源的位置。
三、初始化OpenGL環境
1、 基本的初始化代碼
在ViewController.m中有個函數(void)viewDidLoad,它是程序運行時,初始化回調函數。在viewDidLoad函數內補充我們自己的初始化代碼。
// 使用“ES2”創建一個“EAGLEContext”實例
self.context = [[[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2]autorelease];
// 將“view”的context設置爲這個“EAGLContext”實例的引用。並且設置顏色格式和深度格式。
GLKView* view = (GLKView*)self.view;
view.context = self.context;
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
// 將此“EAGLContext”實例設置爲OpenGL的“當前激活”的“Context”。這樣,以後所有“GL”的指令均作用在這個“Context”上。隨後,發送第一個“GL”指令:激活“深度檢測”。
[EAGLContext setCurrentContext:context];
glEnable(GL_DEPTH_TEST);
// 創建一個GLK內置的“着色效果”,並給它提供一個光源,光的顏色爲綠色。
self.effect = [[[GLKBaseEffect alloc]init]autorelease];
self.effect.light0.enabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(0.0f, 1.0f, 0.0f, 1.0f);
2、 運行。現在應該是粉紅色屏幕了(目前場景仍是空的),說明初始化過程沒問題
四、 將項點數據寫入能用的頂點屬性存儲區
1、 寫入過程
首先將數據保存進GUP的一個緩衝區中,然後再按一定規則,將數據取出,複製到各個通用頂點屬性中。
注:如果頂點數據只有一種類型(如單純的位置座標),換言之,在讀數據時,不需要確定第一個數據的內存位置(總是從0開始),則不必事先保存進緩衝區。
2、 頂點數組保存進緩衝區// 聲明一個緩衝區的標識(GLuint類型)讓OpenGL自動分配一個緩衝區並且返回這個標識的值.綁定這個緩衝區到當前“Context”.最後,將我們前面預先定義的頂點數據“squareVertexData”複製進這個緩衝區中。
// 注:參數“GL_STATIC_DRAW”,它表示此緩衝區內容只能被修改一次,但可以無限次讀取。
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(squareVertexData), squareVertexData, GL_STATIC_DRAW);
3、將緩衝區的數據複製進能用頂點屬性中glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 4*8, (char*)NULL + 0);
首先,激活頂點屬性(默認它的關閉的)。“GLKVertexAttribPosition”是頂點屬性集中“位置Position”屬性的索引。
頂點屬性集中包含五種屬性:位置、法線、顏色、紋理0,紋理1。
它們的索引值是0到4。
激活後,接下來使用“glVertexAttribPointer”方法填充數據。
參數含義分別爲:
頂點屬性索引(這裏是位置)、3個分量的矢量、類型是浮點(GL_FLOAT)、填充時不需要單位化(GL_FALSE)、在數據數組中每行的跨度是32個字節(4*8=32。從預定義的數組中可看出,每行有8個GL_FLOAT浮點值,而GL_FLOAT佔4個字節,因此每一行的跨度是4*8)。
最後一個參數是一個偏移量的指針,用來確定“第一個數據”將從內存數據塊的什麼地方開始。
4、繼續複製其他數據在前面定義了項點數據數組中,還包含了法線和紋理座標,所以參照上面的方法,將剩餘的數據分別複製進能用頂點屬性
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FLOAT, 4*8, (char*)NULL + 12 );
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 4*8, (char*)NULL+24);
原則上,必須先“激活”某個索引,才能將數據複製進這個索引表示的內存中。
因爲紋理座標只有兩個(S和T),所以上面參數是“2”。
五、執行渲染循環
萬事具備,現在可以讓OpenGL顯示一些東西了。
在GLKit框架中,儘管OpenGL的行爲,是由“GLKViewController”和“GLKView”聯合控制的,但實際上“GLKView”類中完全不需要寫任何自己的代碼,因爲,“GLKView”類中每幀觸發的兩個方法“update”和“glkView”,都轉交給“GLKViewController”代理執行了。
1、添加代理方法
在ViewController.m中添加兩個方法
- (void)update
{
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
}
這兩個方法每幀都執行一次(循環執行),一般執行頻率與屏幕刷新率相同(但也可以更改)。
第一次循環時,先調用“glkView”再調用“update”。
一般,將場景數據變化放在“update”中,而渲染代碼則放在“glkView”中。
2、渲染場景
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
前兩行爲渲染前的“清除”操作,清除顏色緩衝區和深度緩衝區中的內容,並且填充淡藍色背景(默認背景是黑色)。
“prepareToDraw”方法,是讓“效果Effect”針對當前“Context”的狀態進行一些配置,它始終把“GL_TEXTURE_PROGRAM”狀態定位到“Effect”對象的着色器上。此外,如果Effect使用了紋理,它也會修改“GL_TEXTURE_BINDING_2D”。
接下來,用“glDrawArrays”指令,讓OpenGL“畫出”兩個三角形(拼合爲一個正方形)。OpenGL會自動從通用頂點屬性中取出這些數據、組裝、再用“Effect”內置的着色器渲染。
3、運行結果
渲染內容終於呈現了,藍色背景、還有一個綠色矩形(其實是兩個三角形)。綠色並非是此物體的本色,而受是綠色燈光影響。
ps:第一次在xcode中寫gl程序,完全借鑑mississi的博客