獲取示例代碼
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
...
// 使用fragment.glsl 和 vertex.glsl中的shader
glUseProgram(self.shaderProgram);
[self drawTriangle];
}
- (void)drawTriangle {
static GLfloat triangleData[18] = {
0, 0.5f, 0, 1, 0, 0, // x, y, z, r, g, b,每一行存儲一個點的信息皿渗,位置和顏色
-0.5f, -0.5f, 0, 0, 1, 0,
0.5f, -0.5f, 0, 0, 0, 1,
};
// 啟用Shader中的兩個屬性
// attribute vec4 position;
// attribute vec4 color;
GLuint positionAttribLocation = glGetAttribLocation(self.shaderProgram, "position");
glEnableVertexAttribArray(positionAttribLocation);
GLuint colorAttribLocation = glGetAttribLocation(self.shaderProgram, "color");
glEnableVertexAttribArray(colorAttribLocation);
// 為shader中的position和color賦值
// glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
// indx: 上面Get到的Location
// size: 有幾個類型為type的數(shù)據(jù),比如位置有x,y,z三個GLfloat元素粥航,值就為3
// type: 一般就是數(shù)組里元素數(shù)據(jù)的類型
// normalized: 暫時用不上
// stride: 每一個點包含幾個byte仙逻,本例中就是6個GLfloat竟宋,x,y,z,r,g,b
// ptr: 數(shù)據(jù)開始的指針逻卖,位置就是從頭開始,顏色則跳過3個GLFloat的大小
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData);
glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData + 3 * sizeof(GLfloat));
glDrawArrays(GL_TRIANGLES, 0, 3);
}
在上篇文章的基礎(chǔ)上我們增加了- (void)drawTriangle
欺缘。效果如下
在解釋代碼之前我們先來了解一下OpenGL渲染的基本流程栋豫。
從圖中可以看出,最開始的輸入是頂點數(shù)據(jù)谚殊。比如三角形丧鸯,就是三個點。每個頂點數(shù)據(jù)可以包含任意數(shù)量的信息嫩絮,最基本的有位置丛肢,顏色。后面介紹貼圖時還會包含UV信息剿干。經(jīng)過各種處理蜂怎,最終放入FrameBuffer,就是上一篇文章說的緩沖區(qū)置尔。接下來我們按照這個流程解釋繪制的代碼杠步。
第一步,提供 Vertex Data
static GLfloat triangleData[18] = {
0, 0.5f, 0, 1, 0, 0, // x, y, z, r, g, b,每一行存儲一個點的信息,位置和顏色
-0.5f, -0.5f, 0, 0, 1, 0,
0.5f, -0.5f, 0, 0, 0, 1,
};
我們要繪制的是一個三角形幽歼,三角形有3個點朵锣,每個點我希望包含位置信息和顏色信息,至于兩點之間的顏色如何甸私,我們不關(guān)心诚些,OpenGL ES會處理。綜上皇型,我們?yōu)槊恳粋€點分配6個GLfloat大小的空間诬烹,前三個存儲位置x,y,z,后三個存儲顏色r,g,b弃鸦。三個點就是18個GLfloat的數(shù)組绞吁。
使用GLfloat而不是float是為了跨平臺,保證不同平臺的GLfloat占用的字節(jié)數(shù)都是一致的寡键。從而規(guī)范化了傳遞給Shader的數(shù)據(jù)的格式和大小掀泳。
第二步,調(diào)用Vertex Shader
attribute vec4 position;
attribute vec4 color;
varying vec4 fragColor;
void main(void) {
fragColor = color;
gl_Position = position;
}
這是本例使用的Vertex Shader西轩,他只做了兩件事,將頂點數(shù)據(jù)里的顏色傳遞給了Fragment Shader脑沿,將位置傳遞給了OpenGL ES藕畔。更詳細(xì)的解釋會再下一篇介紹。
第三步庄拇,Primitive Assembly
glDrawArrays(GL_TRIANGLES, 0, 3);
這一步注服,以形狀為單位匯總渲染指令,為下一步柵格化顏色插值做準(zhǔn)備措近。本例中只繪制了三角形溶弟,還可以通過glDrawArrays繪制直線,點等瞭郑。
第四步辜御,Rasterization
這一步會柵格化繪制的形狀。第一步我們說過只需傳遞頂點的顏色屈张,兩點中間的顏色OpenGL會幫我們處理擒权。OpenGL將會計算出每一個像素對應(yīng)的屬性,比如顏色阁谆,這些值都是根據(jù)頂點的屬性值以及形狀計算而來的碳抄。
這是例子渲染出來的三角形,由圖可以看出场绿,三角形內(nèi)部的每個像素的顏色都是根據(jù)像素點與三個點的距離計算出來的剖效。離紅色點越近像素的紅色成分越多。
第五步,F(xiàn)ragment Shader
經(jīng)過柵格化之后璧尸,每一個像素都要經(jīng)過Fragment Shader處理一遍劝贸。下面就是本例使用的Fragment Shader。
varying lowp vec4 fragColor;
void main(void) {
gl_FragColor = fragColor;
}
本例使用的Fragment Shader什么也沒做逗宁,就是把OpenGL計算出來的顏色直接遞交回給OpenGL映九。
第五步,Per-Fragment Operations
這里主要處理OpenGL對像素的一些固定操作瞎颗。比如深度測試件甥,剪裁測試等。通過OpenGL的API進行配置哼拔。
第六步引有,F(xiàn)ramebuffer
最終寫入Framebuffer,交換緩沖區(qū)后顯示在窗口上倦逐。
最后再介紹兩個重要的點譬正。
坐標(biāo)系
static GLfloat triangleData[18] = {
0, 0.5f, 0, 1, 0, 0, // x, y, z, r, g, b,每一行存儲一個點的信息,位置和顏色
-0.5f, -0.5f, 0, 0, 1, 0,
0.5f, -0.5f, 0, 0, 0, 1,
};
中間點是0,0點檬姥。x,y,z的生長方向如圖曾我,Z軸是由內(nèi)向外。屏幕長寬都是2健民。
如何將頂點數(shù)據(jù)傳遞給Shader
上面解釋步驟時抒巢,從第一步到第二步如何傳遞頂點數(shù)據(jù)沒有介紹。它的代碼實現(xiàn)如下秉犹。
// 啟用Shader中的兩個屬性
// attribute vec4 position;
// attribute vec4 color;
GLuint positionAttribLocation = glGetAttribLocation(self.shaderProgram, "position");
glEnableVertexAttribArray(positionAttribLocation);
GLuint colorAttribLocation = glGetAttribLocation(self.shaderProgram, "color");
glEnableVertexAttribArray(colorAttribLocation);
// 為shader中的position和color賦值
// glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
// indx: 上面Get到的Location
// size: 有幾個類型為type的數(shù)據(jù)蛉谜,比如位置有x,y,z三個GLfloat元素,值就為3
// type: 一般就是數(shù)組里元素數(shù)據(jù)的類型
// normalized: 暫時用不上
// stride: 每一個點包含幾個byte崇堵,本例中就是6個GLfloat型诚,x,y,z,r,g,b
// ptr: 數(shù)據(jù)開始的指針,位置就是從頭開始鸳劳,顏色則跳過3個GLFloat的大小
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData);
glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData + 3 * sizeof(GLfloat));
首先通過glEnableVertexAttribArray
激活shader中的兩個屬性狰贯。glGetAttribLocation
是為了獲取shader中某個屬性的位置。這是shader與OpenGL約定的數(shù)據(jù)互通方式
GLuint positionAttribLocation = glGetAttribLocation(self.shaderProgram, "position");
glEnableVertexAttribArray(positionAttribLocation);
GLuint colorAttribLocation = glGetAttribLocation(self.shaderProgram, "color");
glEnableVertexAttribArray(colorAttribLocation);
Shader中的屬性
attribute vec4 position;
attribute vec4 color;
頂點數(shù)據(jù)只會傳遞給Vertex Shader棍辕,所以不能把attribute vec4 position;
寫到Fragment Shader里暮现,從上面的流程圖也可以看出來。激活Vertex Shader中的屬性后就可以傳值給它了楚昭,下面是傳值代碼栖袋。
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData);
glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData + 3 * sizeof(GLfloat));
上面第一行代碼就是告訴Vertex Shader,向位置屬性傳遞的數(shù)據(jù)大小是3個GLfloat抚太,每個頂點數(shù)據(jù)有6個GLfloat塘幅,位置數(shù)據(jù)起始的指針是(char *)triangleData昔案。OpenGL讀取完第一個位置數(shù)據(jù)后,就會將指針增加6個GLfloat的大小电媳,訪問下一個頂點位置踏揣。顏色也是相同的道理。
本篇主要介紹了繪制三角形需要用到哪些API以及繪制流程匾乓。下一篇會重點介紹Shader的相關(guān)知識捞稿。