上一章簡單介紹了一下著色器冗栗,對于Android開發(fā)者比較直白的理解就是兩個特定時間觸發(fā)的回調(diào)函數(shù)泄朴。只不過這兩個回調(diào)函數(shù)有著稍微特殊的傳值方式罢吃,以及用了一門新的編程語言。這篇主要介紹的就是著色器語言基礎(chǔ)語法宴偿。
特點(diǎn)
Open GL ES 著色器語言是一種高級的面向圖形編輯語言湘捎,主要特性:
- OpenGL ES著色器語言是一種高級的過程語言,對圖形計算需要的數(shù)學(xué)工具支持的非常好窄刘。
- 對頂點(diǎn)著色器窥妇,片元著色器使用的是同樣的語言,不做區(qū)分娩践。
- 基于C/C++的語法及流程控制活翩。
- 完美支持向量與矩陣的各種操作。
- 擁有大量的內(nèi)置函數(shù)來提供豐富的功能翻伺。
著色器版本規(guī)范
OpenGL ES3.0頂點(diǎn)著色器和片段著色器的第1行總是聲明著色器版本材泄。聲明著色器版本通知著色器編譯器預(yù)期在著色器中出現(xiàn)的語法和結(jié)構(gòu)。編譯器按照聲明的著色語言版本檢查著色器語法吨岭。采用如下語法聲明著色器使用OpenGLES著色語言3.00版本∶
#version 300 es
沒有聲明版本號的著色器被認(rèn)定為使用OpenGL ES著色語言的1.00版本拉宗。著色語言的1.00版本用于OpenGL ES2.0。對于OpenGLES 3.0辣辫,規(guī)范的作者決定匹配API和著色語言的版本號旦事,這就是版本號從1.00跳到3.00的原因。OpenGLES著色語言3.0增加了許多新功能急灭,包括非方矩陣姐浮、全整數(shù)支持、插值限定符葬馋、統(tǒng)一變量塊卖鲤、布局限定符肾扰、新的內(nèi)建函數(shù)、全循環(huán)扫尖、全分支支持以及無限的著色器指令長度白对。
數(shù)據(jù)類型
著色器作為一個強(qiáng)數(shù)據(jù)類型的編程語言有著豐富的數(shù)據(jù)類型。OpenGL ES雖然是基于C/C++語法的换怖,但是還是有很大的不同。該語言不支持double蟀瞧,byte沉颂,short,long悦污,也取消了C中的union铸屉,enum,unsigned以及位運(yùn)算切端。
基本數(shù)據(jù)類型
在計算機(jī)圖形中彻坛,兩個基本數(shù)據(jù)類型組成了變換的基礎(chǔ)∶向量和矩陣。這兩種數(shù)據(jù)類型在OpenGL ES著色語言中也是核心踏枣。
GLSL中昌屉,向量可以看做是用同樣類型的標(biāo)量組成,標(biāo)量分為bool茵瀑,int间驮,uint(無符號int)和float四種。每個向量可以由2個马昨,3個或者4個相同的標(biāo)量組成:
變量分類 | 說明 |
---|---|
標(biāo)量 | float,int,uint,bool |
浮點(diǎn)向量 | float,vec2,vec3,vec4 |
整數(shù)向量 | int,ivec2,ivec3,ivec4 |
布爾向量 | bool,bvec2,bvec3,bvec |
矩陣 | mat2(或mat2x2)竞帽,mat2x3,at2×4鸿捧,mat3x2屹篓,mat3(或國mat3x3),mat3x4,mat4×2, mat4x3,mat4(或mat4×4) |
著色語言中的變量必須以某個類型聲明匙奴。例如堆巧,下面的聲明描述如何聲明一個標(biāo)量、一個向量和一個矩陣∶
float specularAtten; //A floating-point-based scalar
vec4 vPosition; //A floating-point-based 4-tuple vector
mat4 mViewProjection; //A 4 x 4 matrix variable declaration
ivec2 vOffset; //An integer-based 2-tuple vector
變量可以在聲明時或者聲明以后初始化饥脑。初始化通過使用構(gòu)造器進(jìn)行恳邀,構(gòu)造器也用于類型轉(zhuǎn)換。
變量構(gòu)造器
OpenGL ES著色語言在類型轉(zhuǎn)換方面有非常嚴(yán)格的規(guī)則灶轰。也就是說谣沸,變量只能賦值為相同類型的其他變量或者與相同類型的變量進(jìn)行運(yùn)算。在語言中不允許隱含類型轉(zhuǎn)換的原因是笋颤,這樣可以避免著色器作者遇到可能導(dǎo)致難以跟蹤的缺陷的意外轉(zhuǎn)換乳附。為了應(yīng)付類型轉(zhuǎn)換内地,語言中有一些可用的構(gòu)造器。你可以使用構(gòu)造器初始化變量赋除,并作為不同類型變量之間的轉(zhuǎn)換手段阱缓。變量可以在聲明(或者以后在著色器中)時使用構(gòu)造器初始化。每種內(nèi)建變量類型都有一組相關(guān)的構(gòu)造器举农。
我們首先來看看如何使用構(gòu)造器初始化和轉(zhuǎn)換標(biāo)量值荆针。
float myFloat=1.0;
float myFloat2= 1;//ERROR:invalid type conversion
bool myBool= true;
int myInt= 0;
int myInt2= 0.0;//ERROR:invalid type conversion
myFloat = float(myBool);//Convert from bool->float
myFloat =float(myInt);// Convert from int ->float
myBool = bool(myInt); // Convert from int ->bool
類似地,構(gòu)造器可以用于轉(zhuǎn)換和初始化向量數(shù)據(jù)類型颁糟。向量構(gòu)造器的參數(shù)將被轉(zhuǎn)換為與被構(gòu)造的向量相同的基本類型(foat航背、int或bool)。向量構(gòu)造器的參數(shù)傳遞有兩種基本方法∶
●如果只為向量構(gòu)造器提供一個標(biāo)量參數(shù)棱貌,則該值用于設(shè)置向量的所有值玖媚。
●如果提供了多個標(biāo)量或者向量參數(shù),則向量的值從左到右使用這些參數(shù)設(shè)置婚脱。如果提供了多個標(biāo)量參數(shù)今魔,那么在向量中必須有至少和參數(shù)中一樣多的分量。
下面是構(gòu)造向量的一些例子∶
vec4 myVec4 = vec4(1.0); // {1.0, 1.0, 1.0, .10}
vec3 myVec3 = vec3(1.0, 1.0, 1.0); // {1.0, 1.0, 1.0}
vec3 temp = vec3(myVec3);
vec2 myVec2 = vec2(myVec3); // {myVec3.x, myVec3.y}
myVec4 = vec4(myVec2, myVec3); // {myVec2.x, myVec2.y, myVec3.x, myVec3.y}
常量
可以將任何基本類型聲明為常數(shù)變量障贸。常數(shù)變量是著色器中不變的值错森。聲明常量時,在聲明中加入const 限定符惹想。常數(shù)變量必須在聲明時初始化问词。下面是const聲明的一些例子∶
const float zero=0.0;
const float pi = 3.14159;
const vec4 red= vec4(1.0,0.0,0.0,1.0);
const mat4 identity = mat4(1.0);
正如在C或者C++中那樣,聲明為const的變量是只讀的嘀粱,不能在源代碼中修改激挪。
向量
向量在著色器代碼的開發(fā)中十分重要,可以很方便的存儲以及操作顏色锋叨,位置垄分,紋理坐標(biāo)等。也可以單獨(dú)訪問向量中的某個分量娃磺,基本語法為< 向量名>.<分量名>薄湿。 ?根據(jù)組成向量的分量數(shù)量,每個分量可以通過使用{x偷卧,y豺瘤,z,w}听诸、{r坐求,g,b晌梨,a}或者{s桥嗤,t须妻,p,q}組合訪問泛领。3種不同命名方案的原因是向量可以互換地用于表示數(shù)學(xué)上的向量荒吏、顏色和紋理坐標(biāo)。x渊鞋、r绰更、s分量總是引用向量的第一個分量。不同的命名約定只是為了方便锡宋。但是动知,不能在訪問向量時混合使用命名約定(換言之,不能采用xgr這樣的引用方法员辩,因為一次只能使用一種命名約定)。使用"."運(yùn)算符時鸵鸥,可以在操作中重新排列向量的分量奠滑,下面是一個例子。
-
將向量看做顏色時妒穴,可以使用r宋税,g,b讼油,a 4個分量名杰赛,分別代碼紅,綠矮台,藍(lán)乏屯,透明度
aColor.r = 0.6;
aColor.g = 0,8; -
將一個向量看做位置時∈莺眨可以使用x辰晕,y,z确虱,w等4個分量名含友,其分別代表x軸,y軸校辩,z軸窘问,向量的模
aPosition.x = 67.2;
aPosition.z = 34.5; 將一個向量看做紋理坐標(biāo)時,可以使用s宜咒,t惠赫,p,q 4個分量名荧呐,其分別代表紋理坐標(biāo)中的1維汉形,2維纸镊,3維
矩陣
矩陣類型 | 說明 |
---|---|
mat2 | 2x2的浮點(diǎn)數(shù)矩陣 |
mat3 | 3x3的浮點(diǎn)數(shù)矩陣 |
mat4 | 4x4的浮點(diǎn)數(shù)矩陣 |
OpenGL ES著色器語言中,矩陣是按列順序組織的概疆,也就是一個矩陣可以看做由幾個列向量組成逗威。例如,mat3就可以看做由3個vec3組成岔冀。
對于矩陣的訪問凯旭,可以將矩陣作為列向量的數(shù)組來訪問。如使套; matrix 為一個mat4罐呼,可以使用matrix[2]取到該矩陣的第三列,其為一個vec4侦高;也可以使用matrix[2][2]取得第三列的向量的第三個分量嫉柴,其為一個float。
混合選擇
通過運(yùn)算符“.”可以進(jìn)行混合選擇操作奉呛,在運(yùn)算符“.” 之后列出一個向量中需要的各個分量的名稱计螺,就可以選擇并重新排列這些分量
vec4 color = vec4(0.7,0.1,0.5,1.0);
vec3 temp = color.agb;//相當(dāng)于拿一個向量(1.0,0.1,0。5)賦值給temp
vec4 tempL = color.aabb; // 相當(dāng)于拿了一個向量(1.0,1.0,0.5,0.5)賦值給tempL
vec3 tempLL;
tempLL.grb = color.aab; // 對向量tempLL的3個分量賦值
一次混合最多只能列出4個分量名稱瞧壮,且一次出現(xiàn)的各部分的分量名稱必須是來自同一名稱組登馒。
內(nèi)建函數(shù)
前一節(jié)描述了著色器作者如何創(chuàng)建一個函數(shù)。OpenGL ES著色語言中最強(qiáng)大的功能之一是該語言中提供的內(nèi)建函數(shù)咆槽。下面是幾句著色器示例代碼∶
float nDotL = dot(normal,light);
float rDotV=dot(viewDir,(2.0* normal)* nDotL-light);
float specular = specularColor * pow(rDotV, specularPower);
如你所見陈轿,這個著色器代碼塊使用了內(nèi)建函數(shù)dot計算兩個向量的點(diǎn)積,用內(nèi)建函數(shù)pow計算標(biāo)量的冪次秦忿。這只是兩個簡單的例子麦射,OpenGL ES著色語言有許多內(nèi)建函數(shù),可以處理通常在著色器中進(jìn)行的各種計算任務(wù)小渊。篇幅有限不展示所有內(nèi)建函數(shù)法褥,只需要記住常見的數(shù)學(xué)運(yùn)算OpenGLES基本都有對應(yīng)的內(nèi)建函數(shù)。現(xiàn)在只是想讓你知道OpenGLES著色語言中有許多內(nèi)建函數(shù)酬屉。為了高效編寫著色器半等,你必須熟悉最常見的內(nèi)建函數(shù)。
內(nèi)建變量
內(nèi)建變量類似于內(nèi)建函數(shù)呐萨,都是OpenGL ES環(huán)境提前幫你定義好的特殊變量杀饵,
變量 | 描述 |
---|---|
gl_VertexID | 輸入變量,用于保存頂點(diǎn)的整數(shù)索引谬擦。highp 精度的整數(shù)變量 |
gl_InstanceID | 輸入變量切距,用于保存實例化繪圖調(diào)用中圖元的實例編號。highp 精度的整數(shù)變量惨远,通常情況下為 0 |
gl_Position | 輸出變量谜悟,用于輸出頂點(diǎn)作為的裁剪坐標(biāo)话肖。highp 精度的浮點(diǎn)變量 |
gl_PointSize | 用于指定點(diǎn)精靈的尺寸,單位為像素葡幸。highp 精度的浮點(diǎn)變量 |
gl_FrontFacing | 不由頂點(diǎn)著色器直接寫入最筒,而是根據(jù)頂點(diǎn)著色器生成的位置值和渲染的圖元類型生成,它是一個布爾變量 |
最長用的就是gl_Position蔚叨,代表了頂點(diǎn)的位置床蜘。
修飾符
修飾全局變量的關(guān)鍵字。
限定符 | 說明 |
---|---|
uniform | 一般用于對同一組頂點(diǎn)組成的單個3D物體中所有頂點(diǎn)都相同的量蔑水,如當(dāng)前光源位置 |
in | 頂點(diǎn)/片元輸入變量 |
out | 頂點(diǎn)/片元輸出變量 |
layout | 布局限定符邢锯,為屬性指定位置 |
const | 用于聲明常量 |
上述內(nèi)容基本上就是OpenGL ES3.0著色器語言語法和c語言區(qū)別比較大的地方,其他的類似結(jié)構(gòu)體搀别,數(shù)組丹擎,控制流語句基本都是和c一樣的,在這不做過多贅述歇父。
讀完上面的內(nèi)容之后大家肯定還是有很多不懂的地方鸥鹉,因為很多名詞的解釋語句中也有很多專業(yè)名詞,說白了就是用一句看不懂的話解釋一個看不懂的詞庶骄,這種感覺就很容易勸退新手。大家不要慌践磅,后面會結(jié)合實際代碼再次穿插的復(fù)習(xí)這篇文章的一些關(guān)鍵知識點(diǎn)的单刁。
復(fù)盤著色器
現(xiàn)在咱們在有了一定的基礎(chǔ)知識之后,咱們來復(fù)盤一下上一篇文章中寫的著色器府适。
頂點(diǎn)著色器:
#version 300 es
layout (location = 0) in vec4 av_Position;
void main() {
gl_Position = av_Position;
gl_PointSize = 10.0;
}
先來看第一行
#version 300 es
指定了著色器規(guī)范的版本是3.0
layout (location = 0) in vec4 av_Position;
這一行則是定義了一個輸入類型的 四維float向量羔飞,并且限定了變量的內(nèi)存布局位置為 0 。這一行設(shè)計到了上邊3個知識點(diǎn)檐春。
首先變量類型逻淌,vec4代表了av_Position是一個四個float數(shù)據(jù)構(gòu)成的四維向量。然后in代表了這是一個輸入型的變量疟暖,是可以被外界(Android環(huán)境)賦值的卡儒。然后就是比較繞嘴的內(nèi)存布局限定符layout,它制定了這個變量的內(nèi)存位置俐巴。大家翻看第一章的代碼:
var avPosition:Int = GLES30.glGetAttribLocation(pointProgram, "av_Position")//尋找變量av_Position的位置索引
GLES30.glEnableVertexAttribArray(avPosition)//啟用變量av_Position
GLES30.glVertexAttribPointer(avPosition, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)//給變量av_Position賦值(這是頂點(diǎn)數(shù)組的賦值方式骨望,后續(xù)會詳細(xì)講述)
GLES30.glDisableVertexAttribArray(avPosition)//關(guān)閉變量av_Position
給頂點(diǎn)著色器中被in修飾的變量(頂點(diǎn)屬性)賦值的流程大致就是上面這四步,使用變量的內(nèi)存索引位置做賦值的依據(jù)的欣舵。而layout的作用就是制定變量在內(nèi)存中的索引位置擎鸠。比如在上述代碼中,由于頂點(diǎn)著色器已經(jīng)使用了layout (location = 0) 來修飾av_Position缘圈,所以用glGetAttribLocation獲取到的av_Position的內(nèi)存索引值肯定是0劣光,上邊賦值的代碼可以改成
GLES30.glEnableVertexAttribArray(0)//啟用變量av_Position av_Position內(nèi)存索引恒為0
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)//給變量av_Position賦值(這是頂點(diǎn)數(shù)組的賦值方式袜蚕,后續(xù)會詳細(xì)講述)
GLES30.glDisableVertexAttribArray(0)//關(guān)閉變量av_Position
如果不用layout修飾的話,其實變量的索引值是會依次累加的绢涡,比如第一個變量索引為0牲剃,第二個是1,第三個是2.....
void main() {}
著色器的main方法
gl_Position = av_Position;
gl_PointSize = 10.0;
這兩個語句則是給兩個內(nèi)建變量賦值垂寥,頂點(diǎn)位置是av_Position颠黎,畫筆粗細(xì)是10。
再看片元著色器:
#version 300 es
precision mediump float;
out vec4 fragColor;
void main() {
fragColor =vec4(1.0,0.0,0.0,1.0);
}
和頂點(diǎn)著色器相同的就不說了滞项,主要看一下不同的
precision mediump float;
這一句是指定著色器的精度限制狭归,高精度可以帶來更準(zhǔn)確的圖形繪制效果但是也會增加GPU的負(fù)擔(dān),相應(yīng)的低精度會減少GPU計算量文判,但是對于高度復(fù)雜的模型可能會導(dǎo)致繪制的不準(zhǔn)確过椎。mediump代表中等精度。
out vec4 fragColor;
定義了一個 out( 輸出類型)的四維向量戏仓,這個向量的作用是輸出片元的顏色疚宇,作用類似頂點(diǎn)著色器的gl_PointSize(指定頂點(diǎn)的位置),但是不同的是gl_PointSize是內(nèi)建變量赏殃,而片元著色器的顏色指定fragColor則是自己定義的一個輸出變量敷待。
fragColor =vec4(1.0,0.0,0.0,1.0);
給fragColor賦值,顏色使用的rgba規(guī)范仁热,既紅色榜揖。
好了這就是第二章的所有內(nèi)容,講述了著色器的基本語法抗蠢,使用方式举哟,以及復(fù)盤了兩個示例著色器。下一章會詳細(xì)介紹應(yīng)用層(Android層)給著色器的兩種最重要的傳值方式——頂點(diǎn)數(shù)組迅矛、統(tǒng)一變量妨猩,以及繪制更多個點(diǎn)、繪制三角形和繪制不同顏色的點(diǎn)秽褒。