GLKit 框架是為了簡化iOS上OpenGL ES的開發,提供的基于OpenGL ES的iOS框架。
實現思路:
1. 新建OpenGLES 上下文 并且配置環境
2. 設置頂點數據數據和設置緩存
3. 創建著色器效果,并啟動著色器
注意:
1. ViewController要繼承GLKViewController
2. 繼承GLKViewController后要實現 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 方法
一些方法的解析:
GLKBaseEffect
Effect效果類提供標準的公共著色效果的實現。能夠配置效果和相關的頂點數據,然后創建和加載適當的著色器。GLKit 包括三個可配置著色效果類:GLKBaseEffect實現OpenGL ES 1.1規范中的關鍵的燈光和材料模式, GLKSkyboxEffect提供一個skybox效果的實現, GLKReflectionMapEffect 在GLKBaseEffect基礎上包括反射映射支持。
為什么OpenGL中要用GLfloat而不用float,兩者有什么區別呢?
不同的機器上 float的占用的字節大小不同,
比如有的機器上 float 是4個字節
有的機器 float 是8個字節
而GLfloat 則是在不同的環境下對應不同的處理
以確保字節數相等
GLuint
GLuint 就是正整形, 和C里面的unsigned int 一樣。
Vertex Buffer Object (VBO)
創建VBO需要3個步驟:
使用glGenBuffers() 請求OpenGL ES 為圖形處理器控制的緩存書城一個獨一無二的標識符。
使用glBindBuffer() 告訴OpenGL ES為接下來的運算使用一個緩存。
使用glBufferData() 讓OpenGL ES 為當前捆綁的緩存分配并初始化足夠的連續內存。
void glGenBuffersARB(GLsizei n, GLuint* ids)
glGenBuffersARB() 創建(一個或多個)緩沖區對象并返回這些對象的標示符。該函數包括兩個參數:第一個是欲創建緩沖區對象的個數,第二個是用于存儲(一個或多個)緩沖區標示符的數組地址(標示符為GLuint類型)
void glBindBufferARB(GLenum target, GLuint id)
一旦緩沖區對象被建立,在使用它之前我們需要使用該函數將其與一個真實的緩沖區空間綁定。glBindBufferARB()有兩個參數:target和ID。
Target作為一個標示符,告訴VBO該緩沖區對象是存儲頂點數組數據(GL_ARRAY_BUFFER_ARB)還是索引數據(GL_ELEMENT_ARRAY_BUFFER_ARB)。
任何頂點的屬性,包括頂點坐標、紋理坐標、法向量和顏色分量數組都需要使用GL_ARRAY_BUFFER_ARB。在glDrawElements()或glRangeElements()里會用到的索引數組此處必須使用GL_ELEMENT_ARRAY_BUFFER_ARB。注意到這里的target實際上協助VBO決定了存儲相應緩沖區對象的最高效區域,比如某些系統會將索引存儲在AGP或者系統內存中,而頂點會存儲在顯存中。
一旦glBindBufferARB()被首次調用,VBO會首先在內存中賦予一個大小為0的緩沖區空間,并隨后初始化VBO狀態,比如用途和可操作性。
void glBufferDataARB(GLenum target, GLsizei size, const void* data, GLenum usage)
當緩沖區對象被初始化后,你可以使用glBufferDataARB將數據拷貝進緩沖區對象。該函數有四個參量。與前一個函數相同,第一個參量只能從GL_ARRAY_BUFFER_ARB或者GL_ELEMENT_ARRAY_BUFFER_ARB中選擇。Size代表欲復制數據的總字節數。第三個參量是指向該源數據數組的指針;如果該指針為NULL,則VBO會在內存中開辟出一塊size大小的空間。最后一個參量usage是另一個傳遞給VBO的標示符,用來決定該緩沖區對象如何使用:static, dynamic 還是 stream,和read, copy 和 draw。
VBO允許usage標示符取以下9種值:
GL_STATIC_DRAW_ARB
GL_STATIC_READ_ARB
GL_STATIC_COPY_ARB
GL_DYNAMIC_DRAW_ARB
GL_DYNAMIC_READ_ARB
GL_DYNAMIC_COPY_ARB
GL_STREAM_DRAW_ARB
GL_STREAM_READ_ARB
GL_STREAM_COPY_ARB
"Static”意味著VBO中的數據不會被改變(一次修改,多次使用),"dynamic”意味著數據可以被頻繁修改(多次修改,多次使用),"stream”意味著數據每幀都不同(一次修改,一次使用)。"Draw”意味著數據將會被送往GPU進行繪制,"read”意味著數據會被用戶的應用讀取,"copy”意味著數據會被用于繪制和讀取。注意在使用VBO時,只有draw是有效的,而copy和read主要將會在像素緩沖區(PBO)和幀緩沖區(FBO)中發揮作用。
系統會根據usage標示符為緩沖區對象分配最佳的存儲位置,比如系統會為GL_STATIC_DRAW_ARB和GL_STREAM_DRAW_ARB分配顯存,GL_DYNAMIC_DRAW_ARB分配AGP,以及任何_READ_相關的緩沖區對象都會被存儲到系統或者AGP中因為這樣數據更容易讀寫。
sizeof()
在 Pascal 語言中,sizeof() 是一種內存容量度量函數,功能是返回一個變量或者類型的大小(以字節為單位);在 C 語言中,sizeof() 是一個判斷數據類型或者表達式長度的運算符。
在Pascal 語言與C語言中,對 sizeof() 的處理都是在編譯階段進行。
glVertexAttribPointer()
glVertexAttribPointer(<#GLuint indx#>, <#GLint size#>, <#GLenum type#>, <#GLboolean normalized#>, <#GLsizei stride#>, <#const GLvoid *ptr#>)
//傳遞數據
//1:傳遞是什么數據?這里是頂點位置數據GLKVertexAttribPosition
//2:數據的大小(每個點的數據個數,我們每個點只有xy2個值所以是2,如果做三維的話還有z坐標,那么就是3)
//3:頂點的數據類型,我們定義的是GLfloat數組所以是GL_FLOAT,如果用int定義頂點那么就是GL_INT.。。
//4:這個。。一般都是GL_FALSE
//5:跨度值,簡單來講,就是隔多少個值取一個點。假如設為1(這個1不是真正的1,因為跨度是按內存空間來算的),
//就是1個單位,那么我們就只能取到1,3,5.。。24被跨過去了。這個在使用頂點結構體的時候會有用,現在設為0
//6:數據的地址,就是數組的名字 (這個解析是錯的)
glDrawArrays
該方法原型:
glDrawArrays(int mode, int first,int count)
參數1:有三種取值
1.GL_TRIANGLES:每三個頂之間繪制三角形,之間不連接
2.GL_TRIANGLE_FAN:以V0V1V2,V0V2V3,V0V3V4,……的形式繪制三角形
3.GL_TRIANGLE_STRIP:順序在每三個頂點之間均繪制三角形。這個方法可以保證從相同的方向上所有三角形均被繪制。以V0V1V2,V1V2V3,V2V3V4……的形式繪制三角形
參數2:從數組緩存中的哪一位開始繪制,一般都定義為0
參數3:頂點的數量
主要代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
[self initContext];
[self initImageVertexAndBuffer];
[self initTexture];
}
- (void)initContext {
//新建OpenGLES 上下文 并且配置環境
self.mContext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
GLKView * view = (GLKView *)self.view;
view.context = self.mContext;
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; //顏色緩沖區格式
[EAGLContext setCurrentContext:self.mContext];
}
- (void)initImageVertexAndBuffer {
//頂點數組,前三個是頂點坐標,后面兩個是紋理坐標
GLfloat imageVertexArray[] = {
0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下
0.5, 0.5, -0.0f, 1.0f, 1.0f, //右上
-0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上
0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下
-0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上
-0.5, -0.5, 0.0f, 0.0f, 0.0f, //左下
};
//頂點數據緩存
GLuint buffer;
//創建一個緩沖區
glGenBuffers(1, &buffer);
//綁定
glBindBuffer(GL_ARRAY_BUFFER, buffer);
//初始化并為緩存分配內存
glBufferData(GL_ARRAY_BUFFER, sizeof(imageVertexArray), imageVertexArray, GL_STATIC_DRAW);
//頂點緩存
//glEnableVertexAttribArray() 告訴OpenGL ES 接下來的渲染中是否使用緩存中的數據;
glEnableVertexAttribArray(GLKVertexAttribPosition);
//glVertexAttribPointer() 告訴OpenGL ES 在緩存中的數據類型和所有需要訪問的數據內存偏移值
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (GLfloat *)NULL + 0);
//紋理緩存
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (GLfloat *)NULL + 3);
}
- (void)initTexture {
NSString * filePath = [[NSBundle mainBundle] pathForResource:@"for_test" ofType:@"jpg"];
//GLKTextureLoaderOriginBottomLeft 紋理坐標系是相反的
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
//GLKTextureLoader讀取圖片,創建紋理GLKTextureInfo
GLKTextureInfo * tureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
self.mEffect = [[GLKBaseEffect alloc]init];
self.mEffect.texture2d0.enabled = GL_TRUE;
self.mEffect.texture2d0.name = tureInfo.name;
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
//定義清除屏幕的顏色(即屏幕的背景色,因為屏幕可能殘留其他數據,不清屏的話說不定看到花瓶哦)
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
//上面只是定義了要用什么顏色清理屏幕,這里才是真正的掃地工人!
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//啟動著色器
[self.mEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
一下函數的解析:
image.png
此demo對落影大神的基礎上加上更多的注釋和體會。