Android OpenGLES3.0 入門教程(二)——著色器基礎(chǔ)知識

上一章簡單介紹了一下著色器冗栗,對于Android開發(fā)者比較直白的理解就是兩個特定時間觸發(fā)的回調(diào)函數(shù)泄朴。只不過這兩個回調(diào)函數(shù)有著稍微特殊的傳值方式罢吃,以及用了一門新的編程語言。這篇主要介紹的就是著色器語言基礎(chǔ)語法宴偿。

特點(diǎn)

Open GL ES 著色器語言是一種高級的面向圖形編輯語言湘捎,主要特性:

  1. OpenGL ES著色器語言是一種高級的過程語言,對圖形計算需要的數(shù)學(xué)工具支持的非常好窄刘。
  2. 對頂點(diǎn)著色器窥妇,片元著色器使用的是同樣的語言,不做區(qū)分娩践。
  3. 基于C/C++的語法及流程控制活翩。
  4. 完美支持向量與矩陣的各種操作。
  5. 擁有大量的內(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)秽褒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壶硅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子销斟,更是在濱河造成了極大的恐慌森瘪,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件票堵,死亡現(xiàn)場離奇詭異扼睬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門窗宇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來措伐,“玉大人,你說我怎么就攤上這事军俊〗募樱” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵粪躬,是天一觀的道長担败。 經(jīng)常有香客問我,道長镰官,這世上最難降的妖魔是什么提前? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮泳唠,結(jié)果婚禮上狈网,老公的妹妹穿的比我還像新娘。我一直安慰自己笨腥,他們只是感情好拓哺,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脖母,像睡著了一般士鸥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谆级,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天础淤,我揣著相機(jī)與錄音,去河邊找鬼哨苛。 笑死,一個胖子當(dāng)著我的面吹牛币砂,可吹牛的內(nèi)容都是我干的建峭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼决摧,長吁一口氣:“原來是場噩夢啊……” “哼亿蒸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掌桩,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤边锁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后波岛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茅坛,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年则拷,在試婚紗的時候發(fā)現(xiàn)自己被綠了贡蓖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曹鸠。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖斥铺,靈堂內(nèi)的尸體忽然破棺而出彻桃,到底是詐尸還是另有隱情,我是刑警寧澤晾蜘,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布邻眷,位于F島的核電站,受9級特大地震影響剔交,放射性物質(zhì)發(fā)生泄漏肆饶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一省容、第九天 我趴在偏房一處隱蔽的房頂上張望抖拴。 院中可真熱鬧,春花似錦腥椒、人聲如沸阿宅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洒放。三九已至,卻和暖如春滨砍,著一層夾襖步出監(jiān)牢的瞬間往湿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工惋戏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留领追,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓响逢,卻偏偏與公主長得像绒窑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舔亭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345