OpenGL ES學(xué)習(xí) - 著色器

GLSL
前一篇主要是學(xué)習(xí)OpenGL的基本使用以及完成我們的Hello Triangle項目葬荷。在里面我們配置了頂點和片段著色器供置,對于Hello Triangle項目來說這兩個著色器完全夠用督暂。但如果需要處理更為復(fù)雜的繪制壁晒,顯然這兩個基本著色器是遠(yuǎn)遠(yuǎn)不行的赁温,所以這篇文章主要更深入著色器程序挑势,對著色器程序進(jìn)行更完善的配置价淌。
前面我們已經(jīng)說過在Xcode中如何編寫著色器源碼了申眼,在新建空文件的時候我們保存的后綴為.glsl。沒錯蝉衣,著色器是使用一種叫GLSL的類C語言完成的括尸。GLSL是為圖形計算量身定制的,它包含一些針對向量和矩陣操作的有用特性病毡。這是前面使用到的頂點著色器源碼

attribute vec4 Position;
void main(void) {
gl_Position = Position;
}

第一行定義輸入變量也叫頂點屬性濒翻,這是一個4分量的向量Position。其實我們能聲明的頂點屬性是有上限的,它一般由硬件來決定有送。OpenGL確保至少有16個包含4分量的頂點屬性可用淌喻。可以使用glGetIntegerv來輸出上限數(shù):

 int numAttributs;
 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &numAttributs);
 NSLog(@"----numAttributs:%d",numAttributs);

這里輸出的是----numAttributs:16雀摘,表明能聲明的頂點屬性上限為16個裸删,大部分情況下是夠用了。

數(shù)據(jù)類型
和其他編程語言一樣阵赠,GLSL有數(shù)據(jù)類型可以來指定變量的種類涯塔,GLSL中包含C等其它語言大部分的默認(rèn)基礎(chǔ)數(shù)據(jù)類型:Int、float豌注、double伤塌、uint和bool,GLSL也有兩種容器類型轧铁,分別是向量(Vector)和矩陣(Matrix)每聪。

向量(Vector)
GLSL中的向量是一個可以包含有1、2齿风、3或者4個分量的容器药薯,分量的類型可以是前面默認(rèn)基礎(chǔ)類型的任意一個。它們可以是下面的形式(n代表分量的數(shù)量):

vecn 包含n個float分量的默認(rèn)向量
bvecn 包含n個bool分量的默認(rèn)向量
ivecn 包含n個int分量的默認(rèn)向量
uvecn 包含n個unsigned int分量的默認(rèn)向量
dvecn 包含n個double分量的默認(rèn)向量

大多數(shù)時候我們使用vecn救斑,因為float足夠滿足大多數(shù)要求了童本。
一個向量的分量可以通過vec.x這種方式獲取,這里x是指這個向量的第一個分量脸候。你可以分別使用.x穷娱、.y、.z和.w來獲取它們的第1运沦、2泵额、3、4個分量携添。GLSL也允許你對顏色使用rgba嫁盲,或是對紋理坐標(biāo)使用stpq訪問相同的分量。 向量這一數(shù)據(jù)類型也允許一些有趣而靈活的分量選擇方式烈掠,叫做重組:

vec1 pos1;
vec2 pos2 = pos1.xx  // 可以使用pos1的x分量重組一個vec2的pos2
vec4 pos3 = pos2.xyxy // 使用pos2的x和y分量重組一個4分量的pos3

你可以使用上面4個字母任意組合來創(chuàng)建一個和原來向量一樣長的(同類型)新向量羞秤,只要原來向量有那些分量即可;然而左敌,你不允許在一個vec2向量中去獲取.z元素瘾蛋,例如上面的pos2里面的沒有z元素的。

除了上面的重組矫限,我們還可以這樣操作:

vec2 pos4 = vec2(0.5瘦黑,0.5);
vec3 pos5 = vec3(pos4京革,1.0);
vec4 pos6 = vec4(pos5.xyz幸斥,1.0)匹摇;

//向量是一種靈活的數(shù)據(jù)類型,我們可以把用在各種輸入和輸出上甲葬。

Uniform

 precision mediump float;
 void main(void) {
 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
 }

在前面的程序中廊勃,我們是在片段著色器中定義的最終輸出顏色為vec4(1.0, 0.0, 0.0, 1.0)。這里就有個問題经窖,如果我們想從應(yīng)用程序中直接給片段著色器設(shè)置顏色該怎么做坡垫。
OpenGL顯然早已解決了這個問題。在OpenGL中存在一種從CPU中的應(yīng)用向GPU中的著色器發(fā)送數(shù)據(jù)的方式画侣,但凡定義在著色器程序中的屬性前面加上Uniform修飾冰悠,這個屬性就是一個全局常量,可以存儲著色器所需要的各種數(shù)據(jù)配乱。另外uniform 的空間被頂點著色器和片段著色器分享溉卓。也就是說頂點著色器和片段著色器被鏈接到一起進(jìn)入項目,它們分享同樣的uniform搬泥。因此一個在頂點著色器中聲明的uniform桑寨,相當(dāng)于在片段著色器中也聲明過了。當(dāng)應(yīng)用程序裝載uniform 時忿檩,它的值在頂點著色器和片段著色器都可用尉尾。在鏈接階段,鏈接器將分配常量在項目里的實際地址燥透,那個地址是被應(yīng)用程序使用和加載的標(biāo)識沙咏。

下面我們在片段著色器中聲明一個uniform屬性,并在著色器主函數(shù)中設(shè)置最終輸出顏色為定義好的uniform:

precision mediump float;
uniform vec4 color;
void main(void) {
gl_FragColor = color;
}

//現(xiàn)在這個uniform現(xiàn)在還是空的班套;我們還沒有給它添加任何數(shù)據(jù)肢藐,這里需要注意如果你聲明了一個uniform卻在GLSL代碼中沒用過,
編譯器會靜默移除這個變量孽尽,導(dǎo)致最后編譯出的版本中并不會包含它窖壕,這可能導(dǎo)致幾個非常麻煩的錯誤

現(xiàn)在我們需要在應(yīng)用中找到這個uniform所在的位置然后對其進(jìn)行更新:

// 獲取uniform的位置值忧勿,如果glGetUniformLocation返回-1就代表沒有找到這個位置值杉女。
  int vertexColorLoc = glGetUniformLocation(programHandle, "color");
 
  glUseProgram(programHandle);
 
 // 然后我們可以通過glUniform4f函數(shù)設(shè)置uniform值。
 glUniform4f(vertexColorLoc, 0.5f, 1.0f, 0.5f, 1.0f);

這里有一點需要注意鸳吸,查詢uniform地址不要求之前使用過著色器程序熏挎,但是更新一個uniform之前你必須先使用程序(調(diào)用glUseProgram),因為它是在當(dāng)前激活的著色器程序中設(shè)置uniform的晌砾,所以在設(shè)置uniform之前我們還需要調(diào)用glUseProgram函數(shù)激活著色器程序
這樣運行的話我們就可以看到顏色不一樣的圖形了坎拐。

多個屬性
在前面的教程中,我們了解了如何填充VBO、配置頂點屬性指針以及如何把它們都儲存到一個VAO里哼勇。這次都伪,我們同樣打算把顏色數(shù)據(jù)加進(jìn)頂點數(shù)據(jù)中。我們將把顏色數(shù)據(jù)添加為3個float值至vertices數(shù)組积担。我們將把三角形的三個角分別指定為紅色陨晶、綠色和藍(lán)色:

const GLfloat vertices[] = {
   //位置                 // 顏色
  0.5f, -0.5f, 0.0f,  1.0f,0.0f,0.0f // 右上角
 -0.5f, -0.5f, 0.0f,  0.0f,1.0f,0.0f // 右下角
  0.0f,  0.5f, 0.0f , 0.0f,0.0f,1.0f // 左下角
 };

定義好頂點數(shù)組,我們還需要修改一下頂點著色器源碼帝璧,由于新增了顏色值先誉,所以在頂點著色器中我們需要定義一個輸入屬性來接受頂點數(shù)組中傳進(jìn)來的顏色數(shù)據(jù)。并且還需要定義一個輸出屬性的烁,用來傳遞顏色值給片段著色器褐耳。所以現(xiàn)在的頂點著色器源碼如下:

attribute vec4 Position; 
attribute vec4 color;

varying vec4 aColor; // 向片段著色器輸出一個顏色

void main(void) {
    gl_Position = Position;
    aColor = color; // aColor設(shè)置為我們從頂點數(shù)據(jù)那里得到的輸入顏色
}

再看下片段著色器:

precision mediump float;

varying lowp vec4 aColor; // 接受頂點著色器輸出的顏色

void main(void) {
    
    gl_FragColor = aColor; // 設(shè)置最終顏色為接受的顏色
}

配置完頂點著色器和片段著色器,接下來需要重新設(shè)置頂點數(shù)據(jù)格式了渴庆,因為我們添加了另一個頂點屬性铃芦,并且更新了VBO的內(nèi)存,更新后的VBO內(nèi)存中的數(shù)據(jù)現(xiàn)在看起來像這樣:

相比較只有頂點坐標(biāo)屬性的時候把曼,VBO內(nèi)存中的每個頂點數(shù)據(jù)中新增了顏色值杨帽,知道了現(xiàn)在的數(shù)據(jù)布局,我們就可以使用glVertexAttribPointer函數(shù)更新頂點格式:

// 位置屬性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), 0);
glEnableVertexAttribArray(0);

// 顏色屬性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

這里需要注意的是第四個參數(shù):由于我們現(xiàn)在有了兩個頂點屬性嗤军,所以不得不重新計算連續(xù)頂點屬性之間的偏移量注盈,為獲得數(shù)據(jù)隊列中下一個屬性值(比如位置向量的下個x分量)我們必須向右移動6個float,其中3個是位置值叙赚,另外3個是顏色值老客。這使我們的步長值為6乘以float的字節(jié)數(shù)(=24字節(jié))。

最后一個參數(shù)是指定第一個組件在數(shù)組的第一個頂點屬性中的偏移量震叮,上圖可以看出位置頂點屬性在前胧砰,所以它的偏移量是0。顏色屬性緊隨位置數(shù)據(jù)之后苇瓣,所以偏移量就是3 * sizeof(float)尉间,用字節(jié)來計算就是12字節(jié)。設(shè)置好之后運行程序應(yīng)該是下圖這樣:

這個圖片可能不是你所期望的那種击罪,因為我們只提供了3個顏色哲嘲,而不是我們現(xiàn)在看到的大調(diào)色板。這是在片段著色器中進(jìn)行的所謂片段插值(Fragment Interpolation)的結(jié)果媳禁。當(dāng)渲染一個三角形時眠副,光柵化(Rasterization)階段通常會造成比原指定頂點更多的片段。光柵會根據(jù)每個片段在三角形形狀上所處相對位置決定這些片段的位置竣稽。

基于這些位置囱怕,它會插值(Interpolate)所有片段著色器的輸入變量霍弹。比如說,我們有一個線段娃弓,上面的端點是綠色的典格,下面的端點是藍(lán)色的。如果一個片段著色器在線段的70%的位置運行台丛,它的顏色輸入屬性就會是一個綠色和藍(lán)色的線性結(jié)合钝计;更精確地說就是30%藍(lán) + 70%綠。
這正是在這個三角形中發(fā)生了什么齐佳。我們有3個頂點私恬,和相應(yīng)的3個顏色,從這個三角形的像素來看它可能包含50000左右的片段炼吴,片段著色器為這些像素進(jìn)行插值顏色本鸣。如果你仔細(xì)看這些顏色就應(yīng)該能明白了:紅首先變成到紫再變?yōu)樗{(lán)色。片段插值會被應(yīng)用到片段著色器的所有輸入屬性上硅蹦。

參考《OpenGL ES 3.0編程指南》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荣德,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子童芹,更是在濱河造成了極大的恐慌涮瞻,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件假褪,死亡現(xiàn)場離奇詭異署咽,居然都是意外死亡,警方通過查閱死者的電腦和手機生音,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門宁否,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缀遍,你說我怎么就攤上這事慕匠。” “怎么了域醇?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵台谊,是天一觀的道長。 經(jīng)常有香客問我譬挚,道長锅铅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任殴瘦,我火速辦了婚禮狠角,結(jié)果婚禮上号杠,老公的妹妹穿的比我還像新娘蚪腋。我一直安慰自己丰歌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布屉凯。 她就那樣靜靜地躺著立帖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悠砚。 梳的紋絲不亂的頭發(fā)上晓勇,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機與錄音灌旧,去河邊找鬼绑咱。 笑死,一個胖子當(dāng)著我的面吹牛枢泰,可吹牛的內(nèi)容都是我干的描融。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼衡蚂,長吁一口氣:“原來是場噩夢啊……” “哼窿克!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毛甲,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤年叮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后玻募,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體只损,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年七咧,在試婚紗的時候發(fā)現(xiàn)自己被綠了改执。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡坑雅,死狀恐怖辈挂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情裹粤,我是刑警寧澤终蒂,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站遥诉,受9級特大地震影響拇泣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矮锈,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一霉翔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苞笨,春花似錦债朵、人聲如沸子眶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽臭杰。三九已至,卻和暖如春谚中,著一層夾襖步出監(jiān)牢的瞬間渴杆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工宪塔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留磁奖,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓某筐,卻偏偏與公主長得像点寥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子来吩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361