通過GLSL實現(xiàn)金字塔旋轉(zhuǎn)案例,了解索引數(shù)組的使用,以及GLSL實現(xiàn)圖形變換
主要內(nèi)容:
索引數(shù)組的了解
旋轉(zhuǎn)金字塔案例
1、索引數(shù)組的了解
索引繪圖技巧就是指將圖形中的肉眼可見的頂點,通過索引的方式表示頂點之間的連接,將重復頂點復用進行圖形繪制的一種技巧
比如說一個金字塔,共有5個面,我們會將其分成6個三角形(底部有兩個三角形)
這樣我們會創(chuàng)建6個三角形,需要存儲18個頂點。但是金字塔本身其實只有5個頂點,每個頂點都會重復使用。這樣就造成了頂點在內(nèi)存/顯存中重復存儲。
通過索引數(shù)組就可以實現(xiàn)頂點的復用。
圖示:

可以寫一個數(shù)組存儲其三角形的連接方式
比如:
GLuint indices[] =
{
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
2、金字塔案例
案例地址: 三角形變換案例
效果:

2.1 簡單介紹
實現(xiàn)功能:
- 繪制金字塔
- 金字塔設置顏色
- 金字塔旋轉(zhuǎn)
分析如何實現(xiàn):
- 繪制金字塔 -- 通過索引數(shù)組組織頂點以構(gòu)建金字塔
- 設置顏色 -- 需要傳入色值到片元著色器中
- 金字塔旋轉(zhuǎn) -- 傳入視圖模型矩陣和投影矩陣到頂點著色器,在著色器中進行計算。
主要學習:
- 索引數(shù)組的使用
- 矩陣的使用
實現(xiàn)思路:
具體的GLSL的渲染流程在八、OpenGL ES - GLSL的使用已經(jīng)有足夠詳細的講解
因此本案例只講述新的功能實現(xiàn),重復的內(nèi)容不再說明。
- 初始化
- 創(chuàng)建圖層
- 創(chuàng)建上下文環(huán)境
- 清空緩存區(qū)
- 設置RenderBuffer、FrameBuffer
- 著色器的使用
- 著色器程序的編寫
- 著色器的編譯
- 鏈接到程序?qū)ο笾?/li>
- 程序?qū)ο蟮倪B接
- 程序?qū)ο蟮拈_啟
- 頂點/紋理坐標數(shù)據(jù)的傳遞
- 獲取數(shù)據(jù)
- 在顯存中創(chuàng)建緩存區(qū)并拷貝數(shù)據(jù)
- 獲取傳遞數(shù)據(jù)的通道的ID
- 開啟通道ID
- 設置數(shù)據(jù)傳遞的方式(也就是獲取緩存區(qū)中的哪部分數(shù)據(jù)來進行傳遞)
-
色值的傳遞(新增)
- 設置色值
- 傳遞色值
-
矩陣的傳遞(新增)
- 矩陣獲取
- 傳遞到頂點著色器
- 矩陣計算
- 繪圖并渲染上屏
- 使用索引繪圖(新增)
- 渲染上屏
2.2 色值傳遞
定義色值
//(1)頂點數(shù)組 前3頂點值(x,y,z),后3位顏色值(RGB)
GLfloat attrArr[] =
{
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, //左上0
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, //右上1
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, //左下2
0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, //右下3
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //頂點4
};
色值傳遞:
可以看到設置方式與頂點數(shù)組一毛一樣。
//10.--------處理頂點顏色值-------
//(1).glGetAttribLocation,用來獲取vertex attribute的入口的.
//注意:第二參數(shù)字符串必須和shaderv.glsl中的輸入變量:positionColor保持一致
GLuint positionColor = glGetAttribLocation(self.myProgram, "positionColor");
//(2).設置合適的格式從buffer里面讀取數(shù)據(jù)
glEnableVertexAttribArray(positionColor);
//(3).設置讀取方式
//參數(shù)1:index,頂點數(shù)據(jù)的索引
//參數(shù)2:size,每個頂點屬性的組件數(shù)量,1,2,3,或者4.默認初始值是4.
//參數(shù)3:type,數(shù)據(jù)中的每個組件的類型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默認初始值為GL_FLOAT
//參數(shù)4:normalized,固定點數(shù)據(jù)值是否應該歸一化,或者直接轉(zhuǎn)換為固定值。(GL_FALSE)
//參數(shù)5:stride,連續(xù)頂點屬性之間的偏移量,默認為0;
//參數(shù)6:指定一個指針,指向數(shù)組中的第一個頂點屬性的第一個組件。默認為0
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (float *)NULL + 3);
4.3 矩陣
矩陣計算過程與OpenGL中所學的矩陣的計算一樣,只是這里用到了第三方庫的API。
因此API不需要專門記憶,只要知道過程即可。
通過矩陣對視圖進行變換的過程以及實現(xiàn)可以查看之前的博客 四、OpenGL - 圖形變換
矩陣計算過程
代碼:
float width = self.frame.size.width;
float height = self.frame.size.height;
//12.創(chuàng)建4 * 4投影矩陣
KSMatrix4 _projectionMatrix;
//(1)獲取單元矩陣
ksMatrixLoadIdentity(&_projectionMatrix);
//(2)計算縱橫比例 = 長/寬
float aspect = width / height; //長寬比
//(3)獲取透視矩陣
/*
參數(shù)1:矩陣
參數(shù)2:視角,度數(shù)為單位
參數(shù)3:縱橫比
參數(shù)4:近平面距離
參數(shù)5:遠平面距離
參考PPT
*/
ksPerspective(&_projectionMatrix, 30.0, aspect, 5.0f, 20.0f); //透視變換,視角30°
//13.創(chuàng)建一個4 * 4 矩陣,模型視圖矩陣
KSMatrix4 _modelViewMatrix;
//(1)獲取單元矩陣
ksMatrixLoadIdentity(&_modelViewMatrix);
//(2)平移,z軸平移-10
ksTranslate(&_modelViewMatrix, 0.0, 0.0, -10.0);
//(3)創(chuàng)建一個4 * 4 矩陣,旋轉(zhuǎn)矩陣
KSMatrix4 _rotationMatrix;
//(4)初始化為單元矩陣
ksMatrixLoadIdentity(&_rotationMatrix);
//(5)旋轉(zhuǎn)
ksRotate(&_rotationMatrix, xDegree, 1.0, 0.0, 0.0); //繞X軸
ksRotate(&_rotationMatrix, yDegree, 0.0, 1.0, 0.0); //繞Y軸
ksRotate(&_rotationMatrix, zDegree, 0.0, 0.0, 1.0); //繞Z軸
//(6)把變換矩陣相乘.將_modelViewMatrix矩陣與_rotationMatrix矩陣相乘,結(jié)合到模型視圖
ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
計算過程:
- 投影矩陣的創(chuàng)建(參數(shù)也一樣)
- 模型視圖的創(chuàng)建,將模式視圖矩陣沿z軸平移-10個距離,這是將觀察者遠離屏幕10個單位距離
- 旋轉(zhuǎn)矩陣的創(chuàng)建,并進行旋轉(zhuǎn)
- 將模型視圖矩陣叉乘上旋轉(zhuǎn)矩陣得到模型視圖矩陣
上面四步就是為了得到投影矩陣和模型視圖矩陣,而模型視圖矩陣做了兩件事1)將觀察者遠離屏幕10個單位;2)模型視圖矩陣叉乘旋轉(zhuǎn)矩陣。
矩陣傳遞:
//11.找到myProgram中的projectionMatrix、modelViewMatrix 2個矩陣的地址。如果找到則返回地址,否則返回-1,表示沒有找到2個對象。
GLuint projectionMatrixSlot = glGetUniformLocation(self.myProgram, "projectionMatrix");
GLuint modelViewMatrixSlot = glGetUniformLocation(self.myProgram, "modelViewMatrix");
//(4)將投影矩陣傳遞到頂點著色器
/*
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
參數(shù)列表:
location:指要更改的uniform變量的位置
count:更改矩陣的個數(shù)
transpose:是否要轉(zhuǎn)置矩陣,并將它作為uniform變量的值。必須為GL_FALSE
value:執(zhí)行count個元素的指針,用來更新指定uniform變量
*/
glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);
//(7)將模型視圖矩陣傳遞到頂點著色器
/*
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
參數(shù)列表:
location:指要更改的uniform變量的位置
count:更改矩陣的個數(shù)
transpose:是否要轉(zhuǎn)置矩陣,并將它作為uniform變量的值。必須為GL_FALSE
value:執(zhí)行count個元素的指針,用來更新指定uniform變量
*/
glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
過程:
- 先獲取通道ID,注意此時的通道名稱必須要與頂點著色器中的作為通道的字符串變量保持一致。
- 使用glUniformMatrix4fv方法傳遞數(shù)據(jù)到著色器中
重要API:
傳遞矩陣到頂點著色器
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
參數(shù)列表:
location:指要更改的uniform變量的位置
count:更改矩陣的個數(shù)
transpose:是否要轉(zhuǎn)置矩陣,并將它作為uniform變量的值。必須為GL_FALSE
value:執(zhí)行count個元素的指針,用來更新指定uniform變量
4.4 索引繪圖
索引數(shù)組:
這里的數(shù)組存儲了三角形的連接方式
//(2).索引數(shù)組
GLuint indices[] =
{
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
索引繪圖:
這個API也要記住,用來進行索引繪圖的每個參數(shù)
/*
void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid * indices);
參數(shù)列表:
mode:要呈現(xiàn)的畫圖的模型
GL_POINTS
GL_LINES
GL_LINE_LOOP
GL_LINE_STRIP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
count:繪圖個數(shù)
type:類型
GL_BYTE
GL_UNSIGNED_BYTE
GL_SHORT
GL_UNSIGNED_SHORT
GL_INT
GL_UNSIGNED_INT
indices:繪制索引數(shù)組
*/
glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, indices);
4.5 著色器的實現(xiàn)
4.5.1 頂點著色器
attribute vec4 position;//頂點數(shù)組
attribute vec4 positionColor;//顏色數(shù)組
uniform mat4 projectionMatrix;//投影矩陣
uniform mat4 modelViewMatrix;//模型視圖矩陣
varying lowp vec4 varyColor;//需要傳遞到片元著色器的顏色數(shù)組的接收者
void main()
{
varyColor = positionColor;//賦值到片元著色器中
vec4 vPos;
// 矩陣計算 投影矩陣*模型視圖矩陣*頂點向量
//4*4 * 4*4 * 4*1
vPos = projectionMatrix * modelViewMatrix * position;
gl_Position = vPos;
}
注意:
- 著色器中的其他內(nèi)容之前都已經(jīng)熟悉了,這里新增的就是對矩陣的計算。
- 在OpenGL中便知道,矩陣需要左乘。

4.5.2 片元著色器
接收片元著色器中的色值
varying lowp vec4 varyColor;
void main()
{
gl_FragColor = varyColor;
}