在前面的博客中,我們都使用到了片元著色器和頂點(diǎn)著色器提茁,相信我們對(duì)著色器語(yǔ)言有了一點(diǎn)了解。前面我們所使用的著色器馁菜,代碼非常簡(jiǎn)單茴扁,能做的事情非常有限,而在后面的博客中我們將會(huì)用到的著色器的越來(lái)越復(fù)雜汪疮,所以在這里單獨(dú)寫一篇博客來(lái)介紹我們使用到的著色器語(yǔ)言GLSL峭火。
關(guān)于著色器
著色器是用來(lái)實(shí)現(xiàn)圖像渲染的,用來(lái)替代固定渲染管線的可編程程序智嚷。著色器替代了傳統(tǒng)的固定渲染管線卖丸,可以實(shí)現(xiàn)3D圖形學(xué)計(jì)算中的相關(guān)計(jì)算,由于其可編程性纤勒,可以實(shí)現(xiàn)各種各樣的圖像效果而不用受顯卡的固定渲染管線限制坯苹。這極大的提高了圖像的畫質(zhì)。
本篇博客重點(diǎn)介紹GLSL語(yǔ)言本身摇天,關(guān)于固定管道和可編程管道的介紹可自行查閱粹湃,或者直接參照Android OpenGLES2.0(一)——了解OpenGLES2.0的OpenGLES1.x和OpenGLES2.0的渲染管道圖,即可知道固定渲染管道和可編程渲染管道的區(qū)別泉坐。
關(guān)于著色器前面的博客也提到過(guò)为鳄,在OpenGLES中著色器分為頂點(diǎn)著色器和片元著色器,我們可以理解為:頂點(diǎn)著色器是針對(duì)每個(gè)頂點(diǎn)執(zhí)行一次腕让,用于確定頂點(diǎn)的位置孤钦;片元著色器是針對(duì)每個(gè)片元(可以理解為每個(gè)像素)執(zhí)行一次,用于確定每個(gè)片元(像素)的顏色纯丸。
著色器語(yǔ)言簡(jiǎn)介
OpenGLES的著色器語(yǔ)言GLSL是一種高級(jí)的圖形化編程語(yǔ)言偏形,其源自應(yīng)用廣泛的C語(yǔ)言。與傳統(tǒng)的C語(yǔ)言不同的是觉鼻,它提供了更加豐富的針對(duì)于圖像處理的原生類型俊扭,諸如向量、矩陣之類坠陈。OpenGLES 主要包含以下特性:
- GLSL是一種面向過(guò)程的語(yǔ)言萨惑,和Java的面向?qū)ο笫遣煌摹?/li>
- GLSL的基本語(yǔ)法與C/C++基本相同捐康。
- 它完美的支持向量和矩陣操作。
- 它是通過(guò)限定符操作來(lái)管理輸入輸出類型的庸蔼。
- GLSL提供了大量的內(nèi)置函數(shù)來(lái)提供豐富的擴(kuò)展功能解总。
在前面的博客示例中,我們所使用的都是非常簡(jiǎn)單的著色器姐仅,基本沒(méi)有使用過(guò)GLSL的內(nèi)置函數(shù)花枫,在后面使用光照、貼圖等等其他功能萍嬉,我們不可避免的要使用這些內(nèi)置函數(shù)乌昔。
著色器語(yǔ)言基礎(chǔ)
GLSL雖然是基于C/C++的語(yǔ)言,但是它和C/C++還是有很大的不同的壤追,比如在GLSL中沒(méi)有double
、long
等類型供屉,沒(méi)有union
行冰、enum
、unsigned
以及位運(yùn)算等特性伶丐。
數(shù)據(jù)類型
GLSL中的數(shù)據(jù)類型主要分為標(biāo)量悼做、向量、矩陣哗魂、采樣器肛走、結(jié)構(gòu)體、數(shù)組录别、空類型七種類型:
- 標(biāo)量:標(biāo)量表示的是只有大小沒(méi)有方向的量朽色,在GLSL中標(biāo)量只有bool、int和float三種组题。對(duì)于int葫男,和C一樣,可以寫為十進(jìn)制(16)崔列、八進(jìn)制(020)或者十六進(jìn)制(0x10)梢褐。關(guān)于進(jìn)制不了解的,得自己補(bǔ)補(bǔ)赵讯,這是編程基礎(chǔ)盈咳。對(duì)于標(biāo)量的運(yùn)算,我們最需要注意的是精度边翼,防止溢出問(wèn)題鱼响。
- 向量:向量我們可以看做是數(shù)組,在GLSL通常用于儲(chǔ)存顏色讯私、坐標(biāo)等數(shù)據(jù)热押,針對(duì)維數(shù)西傀,可分為二維、三維和四位向量桶癣。針對(duì)存儲(chǔ)的標(biāo)量類型拥褂,可以分為bool、int和float牙寞。共有vec2饺鹃、vec3、vec4间雀,ivec2悔详、ivec3、ivec4惹挟、bvec2茄螃、bvec3和bvec4九種類型,數(shù)組代表維數(shù)连锯、i表示int類型归苍、b表示bool類型。需要注意的是运怖,GLSL中的向量表示豎向量拼弃,所以與矩陣相乘進(jìn)行變換時(shí),矩陣在前摇展,向量在后(與DirectX正好相反)吻氧。向量在GPU中由硬件支持運(yùn)算,比CPU快的多咏连。
- 作為顏色向量時(shí)盯孙,用rgba表示分量,就如同取數(shù)組的中具體數(shù)據(jù)的索引值捻勉。三維顏色向量就用rgb表示分量镀梭。比如對(duì)于顏色向量vec4 color,color[0]和color.r都表示color向量的第一個(gè)值踱启,也就是紅色的分量报账。其他相同。
- 作為位置向量時(shí)埠偿,用xyzw表示分量透罢,xyz分別表示xyz坐標(biāo),w表示向量的模冠蒋。三維坐標(biāo)向量為xyz表示分量羽圃,二維向量為xy表示分量。
- 作為紋理向量時(shí)抖剿,用stpq表示分量朽寞,三維用stp表示分量识窿,二維用st表示分量。
- 矩陣:在GLSL中矩陣擁有22脑融、33喻频、4*4三種類型的矩陣,分別用mat2肘迎、mat3甥温、mat4表示。我們可以把矩陣看做是一個(gè)二維數(shù)組妓布,也可以用二維數(shù)組下表的方式取里面具體位置的值姻蚓。
- 采樣器:采樣器是專門用來(lái)對(duì)紋理進(jìn)行采樣工作的,在GLSL中一般來(lái)說(shuō)匣沼,一個(gè)采樣器變量表示一副或者一套紋理貼圖狰挡。所謂的紋理貼圖可以理解為我們看到的物體上的皮膚。
- 結(jié)構(gòu)體:和C語(yǔ)言中的結(jié)構(gòu)體相同释涛,用struct來(lái)定義結(jié)構(gòu)體圆兵,關(guān)于結(jié)構(gòu)體參考C語(yǔ)言中的結(jié)構(gòu)體。
- 數(shù)組:數(shù)組知識(shí)也和C中相同枢贿,不同的是數(shù)組聲明時(shí)可以不指定大小,但是建議在不必要的情況下刀脏,還是指定大小的好局荚。
- 空類型:空類型用void表示,僅用來(lái)聲明不返回任何值得函數(shù)愈污。
變量聲明示例:
float a=1.0;
int b=1;
bool c=true;
vec2 d=vec2(1.0,2.0);
vec3 e=vec3(1.0,2.0,3.0)
vec4 f=vec4(vec3,1.2);
vec4 g=vec4(0.2); //相當(dāng)于vec(0.2,0.2,0.2,0.2)
vec4 h=vec4(a,a,1.3,a);
mat2 i=mat2(0.1,0.5,1.2,2.4);
mat2 j=mat2(0.8); //相當(dāng)于mat2(0.8,0.8,0.8,0.8)
mat3 k=mat3(e,e,1.2,1.6,1.8);
運(yùn)算符
GLSL中的運(yùn)算符有(越靠前耀态,運(yùn)算優(yōu)先級(jí)越高):
- 索引:[]
- 前綴自加和自減:++,–
- 一元非和邏輯非:~暂雹,首装!
- 加法和減法:+,-
- 等于和不等于:==杭跪,仙逻!=
- 邏輯異或:^^
- 三元運(yùn)算符號(hào),選擇:涧尿?:
- 成員選擇與混合:.
- 后綴自加和自減:++系奉,–
- 乘法和除法:*,/
- 關(guān)系運(yùn)算符:>姑廉,<缺亮,=,>=桥言,<=萌踱,<>
- 邏輯與:&&
- 邏輯或:||
- 賦值預(yù)算:=葵礼,+=,-=并鸵,*=鸳粉,/=
類型轉(zhuǎn)換
GLSL的類型轉(zhuǎn)換與C不同。在GLSL中類型不可以自動(dòng)提升能真,比如float a=1;就是一種錯(cuò)誤的寫法赁严,必須嚴(yán)格的寫成float a=1.0,也不可以強(qiáng)制轉(zhuǎn)換粉铐,即float a=(float)1;也是錯(cuò)誤的寫法疼约,但是可以用內(nèi)置函數(shù)來(lái)進(jìn)行轉(zhuǎn)換,如float a=float(1);還有float a=float(true);(true為1.0蝙泼,false為0.0)等程剥,值得注意的是,低精度的int不能轉(zhuǎn)換為低精度的float汤踏。
限定符
在之前的博客中也提到了织鲸,GLSL中的限定符號(hào)主要有:
- attritude:一般用于各個(gè)頂點(diǎn)各不相同的量。如頂點(diǎn)顏色溪胶、坐標(biāo)等搂擦。
- uniform:一般用于對(duì)于3D物體中所有頂點(diǎn)都相同的量。比如光源位置哗脖,統(tǒng)一變換矩陣等瀑踢。
- varying:表示易變量,一般用于頂點(diǎn)著色器傳遞到片元著色器的量才避。
- const:常量橱夭。
限定符與java限定符類似,放在變量類型之前桑逝,并且只能用于全局變量棘劣。在GLSL中,沒(méi)有默認(rèn)限定符一說(shuō)楞遏。
流程控制
GLSL中的流程控制與C中基本相同茬暇,主要有:
if(){}、if(){}else{}橱健、if(){}else if(){}else{}
while(){}和do{}while()
for(;;){}
break和continue
函數(shù)
GLSL中也可以定義函數(shù)而钞,定義函數(shù)的方式也與C語(yǔ)言基本相同。函數(shù)的返回值可以是GLSL中的除了采樣器的任意類型拘荡。對(duì)于GLSL中函數(shù)的參數(shù)臼节,可以用參數(shù)用途修飾符來(lái)進(jìn)行修飾,常用修飾符如下:
- in:輸入?yún)?shù),無(wú)修飾符時(shí)默認(rèn)為此修飾符网缝。
- out:輸出參數(shù)巨税。
- inout:既可以作為輸入?yún)?shù),又可以作為輸出參數(shù)粉臊。
浮點(diǎn)精度
與頂點(diǎn)著色器不同的是草添,在片元著色器中使用浮點(diǎn)型時(shí),必須指定浮點(diǎn)類型的精度扼仲,否則編譯會(huì)報(bào)錯(cuò)远寸。精度有三種,分別為:
- lowp:低精度屠凶。8位驰后。
- mediump:中精度。10位矗愧。
- highp:高精度灶芝。16位。
具體如下表:
不僅僅是float可以制定精度唉韭,其他(除了bool相關(guān))類型也同樣可以夜涕,但是int、采樣器類型并不一定要求指定精度属愤。加精度的定義如下:
uniform lowp float a=1.0;
varying mediump vec4 c;
當(dāng)然女器,也可以在片元著色器中設(shè)置默認(rèn)精度,只需要在片元著色器最上面加上precision <精度> <類型>
即可制定某種類型的默認(rèn)精度住诸。其他情況相同的話晓避,精度越高,畫質(zhì)越好只壳,使用的資源也越多。
程序結(jié)構(gòu)
前面幾篇博客都有使用到著色器暑塑,我們對(duì)著色器的程序結(jié)構(gòu)也應(yīng)該有一定的了解吼句。也許一直沉浸在Android應(yīng)用開(kāi)發(fā),沒(méi)有了解C開(kāi)發(fā)的朋友事格,對(duì)這種結(jié)構(gòu)并不熟悉惕艳。GLSL程序的結(jié)構(gòu)和C語(yǔ)言差不多,main()方法表示入口函數(shù)驹愚,可以在其上定義函數(shù)和變量远搪,在main中可以引用這些變量和函數(shù)。定義在函數(shù)體以外的叫做全局變量逢捺,定義在函數(shù)體內(nèi)的叫做局部變量谁鳍。與高級(jí)語(yǔ)言不通的是,變量和函數(shù)在使用前必須聲明,不能再使用的后面聲明變量或者函數(shù)倘潜。
內(nèi)建變量
在著色器中我們一般都會(huì)聲明變量來(lái)在程序中使用绷柒,但是著色器中還有一些特殊的變量,不聲明也可以使用涮因。這些變量叫做內(nèi)建變量废睦。內(nèi)建變量,相當(dāng)于著色器硬件的輸入和輸出點(diǎn)养泡,使用者利用這些輸入點(diǎn)輸入之后嗜湃,就會(huì)看到屏幕上的輸出。通過(guò)輸出點(diǎn)可以知道輸出的某些數(shù)據(jù)內(nèi)容澜掩。當(dāng)然购披,實(shí)際上肯定不會(huì)這樣簡(jiǎn)單,這么說(shuō)只是為了幫助理解输硝。在頂點(diǎn)著色器中的內(nèi)建變量和片元著色器的內(nèi)建變量是不相同的今瀑。著色器中的內(nèi)建變量有很多,在此点把,我們只列出最常用的集中內(nèi)建變量橘荠。
頂點(diǎn)著色器的內(nèi)建變量
輸入變量:
gl_Position:頂點(diǎn)坐標(biāo)
gl_PointSize:點(diǎn)的大小,沒(méi)有賦值則為默認(rèn)值1郎逃,通常設(shè)置繪圖為點(diǎn)繪制才有意義哥童。
片元著色器的內(nèi)建變量
1. 輸入變量
gl_FragCoord:當(dāng)前片元相對(duì)窗口位置所處的坐標(biāo)。
gl_FragFacing:bool型褒翰,表示是否為屬于光柵化生成此片元的對(duì)應(yīng)圖元的正面贮懈。
2. 輸出變量
gl_FragColor:當(dāng)前片元顏色
gl_FragData:vec4類型的數(shù)組。向其寫入的信息优训,供渲染管線的后繼過(guò)程使用朵你。
常用內(nèi)置函數(shù)
常見(jiàn)函數(shù)
radians(x):角度轉(zhuǎn)弧度
degrees(x):弧度轉(zhuǎn)角度
sin(x):正弦函數(shù),傳入值為弧度揣非。相同的還有cos余弦函數(shù)抡医、tan正切函數(shù)、asin反正弦早敬、acos反余弦忌傻、atan反正切
pow(x,y):xy
exp(x):ex
exp2(x):2x
log(x):logex
log2(x):log2x
sqrt(x):x√
inversesqr(x):1x√
abs(x):取x的絕對(duì)值
sign(x):x>0返回1.0,x<0返回-1.0搞监,否則返回0.0
ceil(x):返回大于或者等于x的整數(shù)
floor(x):返回小于或者等于x的整數(shù)
fract(x):返回x-floor(x)的值
mod(x,y):取模(求余)
min(x,y):獲取xy中小的那個(gè)
max(x,y):獲取xy中大的那個(gè)
mix(x,y,a):返回x?(1?a)+y?a
step(x,a):x< a返回0.0水孩,否則返回1.0
smoothstep(x,y,a):a < x返回0.0,a>y返回1.0琐驴,否則返回0.0-1.0之間平滑的Hermite插值俘种。
dFdx(p):p在x方向上的偏導(dǎo)數(shù)
dFdy(p):p在y方向上的偏導(dǎo)數(shù)
fwidth(p):p在x和y方向上的偏導(dǎo)數(shù)的絕對(duì)值之和
幾何函數(shù)
length(x):計(jì)算向量x的長(zhǎng)度
distance(x,y):返回向量xy之間的距離
dot(x,y):返回向量xy的點(diǎn)積
cross(x,y):返回向量xy的差積
normalize(x):返回與x向量方向相同秤标,長(zhǎng)度為1的向量
矩陣函數(shù)
matrixCompMult(x,y):將矩陣相乘
lessThan(x,y):返回向量xy的各個(gè)分量執(zhí)行x< y的結(jié)果,類似的有g(shù)reaterThan,equal,notEqual
lessThanEqual(x,y):返回向量xy的各個(gè)分量執(zhí)行x<= y的結(jié)果安疗,類似的有類似的有g(shù)reaterThanEqual
any(bvec x):x有一個(gè)元素為true抛杨,則為true
all(bvec x):x所有元素為true,則返回true荐类,否則返回false
not(bvec x):x所有分量執(zhí)行邏輯非運(yùn)算
紋理采樣函數(shù)
紋理采樣函數(shù)有texture2D怖现、texture2DProj、texture2DLod玉罐、texture2DProjLod屈嗤、textureCube、textureCubeLod及texture3D吊输、texture3DProj饶号、texture3DLod、texture3DProjLod等季蚂。
texture表示紋理采樣茫船,2D表示對(duì)2D紋理采樣,3D表示對(duì)3D紋理采樣
Lod后綴扭屁,只適用于頂點(diǎn)著色器采樣
Proj表示紋理坐標(biāo)st會(huì)除以q
紋理采樣函數(shù)中算谈,3D在OpenGLES2.0并不是絕對(duì)支持。我們?cè)俅螘簳r(shí)不管3D紋理采樣函數(shù)料滥。重點(diǎn)只對(duì)texture2D函數(shù)進(jìn)行說(shuō)明然眼。texture2D擁有三個(gè)參數(shù),第一個(gè)參數(shù)表示紋理采樣器葵腹。第二個(gè)參數(shù)表示紋理坐標(biāo)高每,可以是二維、三維践宴、或者四維鲸匿。第三個(gè)參數(shù)加入后只能在片元著色器中調(diào)用,且只對(duì)采樣器為mipmap類型紋理時(shí)有效阻肩。
小結(jié)
到這里晒骇,關(guān)于著色器語(yǔ)言GLSL就大致介紹完了,相對(duì)Java來(lái)說(shuō)磺浙,GLSL語(yǔ)言不知簡(jiǎn)單了多少倍。結(jié)合之前博客的案例徒坡,相信我們也能夠?qū)懸恍┖?jiǎn)單的著色器了撕氧。在后續(xù)的博客中,我們將會(huì)學(xué)習(xí)紋理喇完、光照伦泥、混合與霧、圖像處理等內(nèi)容,將會(huì)頻繁的用到著色器語(yǔ)言不脯,在使用的過(guò)程中府怯,我們才能更好的掌握GLSL。