由于Android平臺下的可編程圖形硬件支持是 OpenGL ES 2.0標(biāo)準(zhǔn)灌曙,因此本教程向巴友們介紹 OpenGL ES著色語言示绊。 OpenGL ES 著色語言是一種高級的圖形編程語言。其源自于應(yīng)用廣泛的C語言宛官,同時具有RendeMan以及其他著色語言的一些優(yōu)良特性扶檐,易于被開發(fā)人員掌握凶杖。 OpenGL ES 的著色語言主要包括以下特性:
OpenGL ES 2.0著色語言是一種高級的過程語言(注意,不是面向?qū)ο蟮模?br>
對頂點著色器款筑、片元著色器使用的是同樣的語言智蝠。
基于C/C++的語法及流程控制腾么。
完美支持向量與矩陣的各種操作。
通過類型限定符來管理輸入與輸出杈湾。
擁有大量的內(nèi)置函數(shù)來提供豐富的功能解虱。
一、著色語言基礎(chǔ)
數(shù)據(jù)類型概述
- 標(biāo)量
標(biāo)量也被稱為“無向量”其值只有大小漆撞,并不具有方向殴泰。標(biāo)量之間的運算遵循簡單的代數(shù)法則,如質(zhì)量浮驳、密度悍汛、體積、時間以及溫度等都屬于標(biāo)量至会。OpenGL ES著色語言支持的標(biāo)量類型有布爾型(bool)离咐、整形(int)和浮點型(float)。 - 向量
OpenGL ES著色語言中奉件,向量可以看做是用同樣類型的標(biāo)量組成宵蛀,其基本類型也分為bool、int和float三種县貌。每個向量可以由2個术陶、3個、4個相同的標(biāo)量組成煤痕,具體情況如下:
向量類型
說明
向量類型
說明
vec2
包含了2個浮點數(shù)的向量
ivec4
包含了4個整數(shù)的向量
vec3
包含了3個浮點數(shù)的向量
bvec2
包含了2個布爾數(shù)的向量
vec4
包含了4個浮點數(shù)的向量
bvec3
包含了3個布爾數(shù)的向量
ivec2
包含了2個整數(shù)的向量
bvec4
包含了4個布爾數(shù)的向量
ivec3
包含了3個整數(shù)的向量
向量在著色器代碼的開發(fā)中有著十分重要的作用梧宫,可以很方面的存儲以及存儲顏色、位置杭攻、紋理坐標(biāo)等不僅包含一個組成部分的量祟敛。開發(fā)中,有時可能需奧單獨訪問向量中的某個分量兆解,基本的語法為“<向量名>.<分量名>”,根據(jù)目的的不同跑揉,主要有以下幾種用法:
將一個向量看做顏色時锅睛,可以使用r,g,b,a四個分量名,分別代表紅历谍、綠现拒、藍(lán)、透明度4個色彩通道望侈。具體用法如下:
[代碼]xml代碼:
//給向量aColor的紅色通道賦值
aColor.r = 0.6;
將一個向量看做位置時印蔬,可以使用x,y,z,w等4個分量名,分別代表X軸脱衙,Y軸侥猬,Z軸和向量的模四個分量例驹,具體用法和顏色類似。
將一個向量看做紋理坐標(biāo)時退唠,可以使用s,t,p,q四個分量名鹃锈,期分別代表紋理坐標(biāo)的不同分量,具體用法同顏色瞧预。(對紋理坐標(biāo)中的s,t等分量巴友可能不是很明白屎债,不用擔(dān)心,在后面介紹紋理貼圖的教程會進(jìn)行詳細(xì)的介紹)
訪問向量中的各個不同的分量不但可以采用“.”加上不同的分量名垢油,還可以將向量看做一個數(shù)組盆驹,用下標(biāo)來進(jìn)行訪問,具體用法如下:
[代碼]xml代碼:
//給向量aColor的紅色通道賦值
aColor[0] = 0.6;
- 矩陣
有一些基礎(chǔ)的開發(fā)人員都知道滩愁,3D場景中的移位躯喇、旋轉(zhuǎn)、縮放等變換都是由矩陣的運算來實現(xiàn)的惊楼。因此3D場景的開發(fā)中會非常多的使用矩陣玖瘸,矩陣按尺寸分為2x2矩陣、3x3矩陣檀咙、4x4矩陣雅倒,具體情況如下表所示:
矩陣類型
說明
mat2
2x2浮點數(shù)矩陣
mat3
3x3浮點數(shù)矩陣
mat4
4x4浮點數(shù)矩陣
對于矩陣的訪問,可以講矩陣作為列向量的數(shù)組來訪問弧可。如matrix為一個mat4蔑匣,可以使用matrix[2]取到該矩陣的第三列,其為一個vec4棕诵;也可以使用matix[2][2]取得第三列向量的第3個分量裁良。 - 采樣器
采樣器是著色語言中不同于C語言的一種特殊的基本數(shù)據(jù)類型,其專門用來進(jìn)行紋理采樣的相關(guān)操作校套。一般情況下价脾,一個采樣器變量代表一幅或一套紋理貼圖,其具體情況如下:
采樣器
說明
sampler2D
用于訪問二維紋理
smapler3D
用于訪問三維紋理
samplerCube
用于訪問立方貼圖紋理
需要注意的是笛匙,與前面介紹的幾種變量不同侨把,采樣器變量不能再著色器中初始化。一般情況下采樣器變量都用uniform限定符來修飾妹孙,從宿主語言(如java)接受傳遞進(jìn)著色器的值秋柄。 - 結(jié)構(gòu)體
OpenGL ES著色語言還提供了類似C語言中的用戶自定義結(jié)構(gòu)體,同樣也是使用struct關(guān)鍵字進(jìn)行聲明蠢正。其基本用法如下:
[代碼]xml代碼:
struct info{
vec3 color;
vec3 position;
vec2 textureCoor;
}
- 數(shù)組
聲明數(shù)組的方式主要有兩種骇笔,
在聲明數(shù)組的同時,指定數(shù)組的大小:
[代碼]xml代碼:
vec3 position[20];
在聲明數(shù)組時笨触,也可以不指定數(shù)組的大小懦傍,但是必須符合下列兩種情況之一。
u 引用數(shù)組之前旭旭,要再次使用第一種聲明方式來生命該數(shù)組:
[代碼]xml代碼:
//聲明了一個大小不定的vec3數(shù)組
vec3 position[];
//再次聲明該數(shù)組谎脯,并且指定大小。
vec3 position[5];
u 代碼中訪問數(shù)組的下標(biāo)都是編譯時常量持寄,這時編譯器會自動創(chuàng)建適當(dāng)大小的數(shù)組源梭,使得數(shù)組尺寸足夠存儲編譯器看到的最大索引值對應(yīng)的元素。
[代碼]xml代碼:
//聲明了一個大小不定的vec3數(shù)組
vec3 position[];
//position需要一個大小為4的數(shù)組
position[3] = vec3(3.0);
//position需要一個大小為21的數(shù)組
position[20] = vec3(6.0);
- 空類型使用void表示稍味,僅用來聲明不返回任何值得函數(shù)废麻。例如在頂點著色器以及片元著色器中必須存在的main函數(shù)就是一個返回值為空的函數(shù),代碼如下:
[代碼]xml代碼:
void main() {
}
數(shù)據(jù)類型的基本使用
- 聲明模庐、作用域及初始化
變量的聲明以及作用域與Java/C++語法類似烛愧,可以再任何需要的位置聲明變量,同時期作用域也同樣分為局部變量和全局變量:
[代碼]xml代碼:
//聲明了全局變量a和b
int a,b;
//聲明了全局變量aPosition并賦值
vec3 aPosition = vec3(1.0, 2.2, 3.3);
void myFunction() {
//聲明了局部變量c并賦值
int c = 14;
//給全局變量a賦值
a = 4;
//給全局變量b賦值
b = a * c;
}
向量的初始化還有一些很靈活的技巧掂碱,巴友們體會一下下面的代碼:
[代碼]xml代碼:
//聲明浮點變量a并賦值
float a = 12.3;
//聲明浮點變量b并賦值
float b = 11.4;
//聲明2維向量va并賦值
vec2 va = vec2(2.3, 2.5);
//聲明2維向量vb并賦值
vec2 vb = vec2(a, b);
//聲明3維向量vc并賦值
vec3 vc = vec3(vb, 12.5);
//聲明4維向量vd并賦值
vec4 vd = vec4(va, vb);
//聲明4維向量ve并賦值, 相當(dāng)于vec4(0.2 , 0.2 , 0.2, 0.2);
vec4 ve = vec4(0.2);
- 運算符
與大多數(shù)編程語言類似怜姿,常見的運算符都可以在該語言中使用。下面按照優(yōu)先級順序列出了OpenGL ES著色語言中可以使用的運算符:
運算符
說明
運算符
說明
[]
用于索引
.
成員選擇與混合
++ --
自加1與自減1后綴
++ --
自加1與自減1前綴
- !
一元非與邏輯非
- /
乘法與除法
加法與減法
< > <= >=
關(guān)系運算符
== !=
等于和不等于
&&
邏輯與
^^
邏輯異或
||
邏輯或
?:
選擇
= += -= *= /=
賦值運算符
- 限定符
與其他的編程語言一樣疼燥,著色器中對變量也有很多可選的限定符,主要如下:
限定符
說明
attribute
一般用于每個頂點都各不相同的量醉者,如頂點位置、顏色等撬即。
uniform
一般用于對同一組頂點組成的單個3D物體中所有頂點都相同的量立磁,如當(dāng)前光源的位置唱歧。
varying
用于從頂點著色器傳遞到片元著色器的量
const
用于聲明常量
attribute限定符
顧名思義為屬性限定符,其修飾的變量用來接收渲染管線傳遞進(jìn)頂點著色器的當(dāng)前待處理頂點的各種屬性值粒竖。這些屬性值每個頂點各自擁有獨立的副本,用于描述頂點的各項特征温圆,如頂點坐標(biāo)、法向量孩革、顏色岁歉、紋理坐標(biāo)等。
用attribute限定符修士的變量其值是由宿主程序批量出入渲染管線的,管線進(jìn)行基本處理后再傳遞給頂點著色器锅移。數(shù)據(jù)中有多少個頂點熔掺,管線就調(diào)用多少次頂點著色器,每次講一個頂點的各種屬性數(shù)據(jù)傳遞給頂點著色器中對應(yīng)atribute變量非剃。因此置逻,頂點著色器每次執(zhí)行將完成對一個頂點各項屬性數(shù)據(jù)的處理。
從上面的介紹中可以看出备绽,atribute限定符只能用于頂點著色器中券坞,不能再片元著色器中使用,且attribute限定符只能用來修飾浮點數(shù)標(biāo)量肺素、浮點向量以及矩陣變量恨锚,不能用來修飾其他類型的變量。下面的代碼片段給出了在頂點著色器中正確使用attribute限定符的情況:
[代碼]xml代碼:
//頂點位置
attribute vec3 aPosition;
//頂點法向量
attribute vec3 aNormal;
前面已經(jīng)提到倍靡,對于用attribute限定符修飾的變量的值是由宿主程序批量傳入渲染管線的猴伶,相關(guān)代碼如下:
[代碼]java代碼:
// 聲明頂點位置屬性引用
int maPositionHandle;
// 獲取頂點位置屬性引用的值,
// mProgram為著色器程序ID,
// aPosition為著色器中對應(yīng)屬性的變量名稱塌西。
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
// 將頂點位置數(shù)據(jù)傳入渲染管線
// maPositionHandle:頂點位置屬性引用
// 3:每頂點一組的數(shù)據(jù)個數(shù)(這里是X,Y,Z坐標(biāo)他挎,因此為3)
// GLES20.GL_FLOAT:數(shù)據(jù)類型
// false:是否格式化
// 3 * 4:每組數(shù)據(jù)的尺寸,這個魅族3個浮點數(shù)值(X,Y,Z坐標(biāo))捡需,每個浮點數(shù)4個字節(jié)
// mVertexBuffer:存放了數(shù)據(jù)的緩沖
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);
具體代碼可以參考第一個教程中的Triangle類办桨。
http://www.apkbus.com/blog-99192-39498.html
uniform限定符
uniform為一致變量限定符,一致變量指的是對于同一組頂點組成的單個3D物體中所有頂點都相同的量栖忠。Uniform變量可以用在頂點著色器或片元著色器中崔挖,其支持用來修飾所有的基本數(shù)據(jù)類型。與屬性限定符類似庵寞,一致變量的值也是從宿主程序傳入的狸相。
下面的代碼片給出了在頂點或片元著色器中正確使用uniform限定符的情況:
[代碼]xml代碼:
//總變換矩陣
uniform mat4 uMVPMatrix;
//變換矩陣
uniform mat4 uMMatrix;
//光源位置
uniform vec3 uLightLocation;
//攝像機位置
uniform vec3 uCamera;
將一致變量的值由宿主程序傳入渲染管線的代碼如下:
[代碼]java代碼:
//總變換矩陣一致變量引用
int muMVPMatrixHandle;
//獲取著色器程序中總變換矩陣一致變量的引用
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
//通過一致變量引用將一致變量值傳入渲染管線
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0);
需要注意的是,隨一致性變量類型不同將值傳入渲染管線的方法也有所不同捐川,這些方法的名稱都以glUniform開頭脓鹃,常用的如下所列:
glUniformNf/glUniformNfv方法,將N個浮點數(shù)傳入管線古沥,以備管線傳遞給由N個浮點數(shù)組成的一致變量瘸右,N的取值為1,2,3,4。
glUniformNi/glUniformNiv方法岩齿,將N個整數(shù)傳入管線太颤,以備管線傳遞給由N個整數(shù)組成的一致變量,N的取值為1,2,3,4龄章。
glUniformMatrixNfv方法做裙,將N * N的矩陣傳入管線,以備管線傳遞給N * N矩陣類型的一致變量仔戈,N的取值為2,3,4拧廊。
verying限定符
想要將頂點著色器中的信息傳入到片元著色器中,必須使用varying限定符卦绣。歐諾個varying限定符修飾的全局變量又稱為易變變量,易變變量可以看成是頂點著色器和片元著色器之間的動態(tài)接口廊蜒,方便頂點著色器與片元著色器之間信息的傳遞山叮。下圖給出了易變變量的工作原理:
從上圖可以看出添履,首先頂點著色器再每個頂點中都對一邊變量vPosition進(jìn)行了賦值暮胧。接著在片元著色器中接受易變變量vPosition的值時得到的并不是某個頂點賦的特定值,而是根據(jù)片元所在的位置以及圖元中各個頂點的位置進(jìn)行差值計算產(chǎn)生的值钞翔。
如圖中頂點1席舍、2、3的vPosition值分別為vec3(0,7,0)汰扭、vec3(5,0,0)福铅、vec3(-5,0,0),則插值后片元a的vPosition值為vec3(1.45, 2.06, 0)。
從上述介紹中可以看到珊泳,光柵化后產(chǎn)生了多少個片元,就會插值計算出多少套易變變量色查。同時渲染管線就會調(diào)用多少次片元著色器秧了⊙檎保可以看出帝嗡,3D物體的渲染中,片元著色器執(zhí)行的次數(shù)會大大超過頂點著色器哟玷。因此GPU硬件中配置的片元著色器硬件數(shù)量往往多于頂點著色器硬件數(shù)量以提高渲染速度。
const限定符
用const限定符修飾的變量其值是不可以變的喉脖,也就是常量树叽,又稱為編譯時常量谦絮。編譯時常量在聲明的時候必須進(jìn)行初始化。例如:
[代碼]xml代碼:
const int tempx = 1;