本文將帶大家深入了解Shader畸肆,下面是運(yùn)行截圖尿贫,可以前往我的博客查看代碼演示拍柒。
前言
上篇文章中我們已經(jīng)和Shader有了一面之緣揭厚,本文將帶大家深入Shader的世界币砂,介紹Shader的語言特性尖飞,數(shù)據(jù)類型,內(nèi)置方法等等凌受。Shader語言和C語言很相似阵子,如果你學(xué)過C語言應(yīng)該可以很快適應(yīng)Shader的編程風(fēng)格。本文提供了一個具備Shader基本編程元素的例子胜蛉,通過Shader控制三角形旋轉(zhuǎn)挠进,并把位置轉(zhuǎn)變成了顏色,讀者可以通過修改這個例子更快的熟悉Shader誊册。
代碼框架
無論是Vertex Shader還是Fragment Shader领突,都有基本的代碼框架。下面是本文使用的Vertex Shader案怯。
attribute vec4 position;
varying vec4 fragColor;
uniform float elapsedTime;
void main() {
fragColor = position * 0.5 + 0.5;
float rotateAngle = elapsedTime * 0.001;
float x = position.x * cos(rotateAngle) - position.y * sin(rotateAngle);
float y = position.x * sin(rotateAngle) + position.y * cos(rotateAngle);
gl_Position = vec4(x, y, 0.0, 1.0);
}
只有Vertex Shader可以聲明attribute
變量君旦,它用來接受CPU傳遞過來的頂點數(shù)據(jù)。除了attribute
變量之外嘲碱,還可以聲明uniform
變量和varying
變量金砍。具體含義還以下面會作詳解。main
方法是Shader代碼執(zhí)行的入口麦锯,這和C語言一模一樣恕稠。在Vertex Shader中你必須給gl_Position
賦值,否則這個Vertex Shader沒有任何意義扶欣,沒有任何頂點會傳遞給GPU鹅巍。
下面是本文使用的Fragment Shader。
varying mediump vec4 fragColor;
void main() {
gl_FragColor = fragColor;
}
Fragment Shader除了attribute
變量其他變量都可以擁有料祠,varying
變量必須和Vertex Shader中的varying
變量類型保持一致骆捧,varying
變量會從Vertex Shader傳遞到Fragment Shader中。那么問題來了髓绽,Vertex Shader是每個頂點調(diào)用一次凑懂,而Fragment Shader是每個像素調(diào)用一次,那么頂點之間像素的varying
變量值是如何計算的呢梧宫?答案是GPU會根據(jù)要繪制的形狀自動插值計算接谨。大家可以觀察示例,三角形頂點之間的顏色正是通過各個頂點的fragColor
插值計算出來的塘匣。
變量類型
Shader有下面幾種變量類型:
-
void
和C語言的void一樣脓豪,無類型 -
bool
布爾 -
int
有符號的int -
float
浮點數(shù) -
vec2
,vec3
,vec4
2,3忌卤,4維向量扫夜,如果你不知道什么是向量,可以理解為2驰徊,3笤闯,4長度的數(shù)組。 -
bvec2
,bvec3
,bvec4
2棍厂,3颗味,4維布爾值的向量。 -
ivec2
,ivec3
,ivec4
2牺弹,3浦马,4維int值的向量。 -
mat2
,mat3
,mat4
2x2, 3x3, 4x4 浮點數(shù)的矩陣张漂,如果你不了解矩陣晶默,后面會有一篇文章單獨介紹矩陣。 -
sampler2D
紋理航攒,后面會詳細(xì)介紹磺陡。 -
samplerCube
Cube紋理,后面會詳細(xì)介紹漠畜。
變量精度
細(xì)心的讀者可能會發(fā)現(xiàn)同樣是varying
變量币他,在Fragment Shader中多了一個mediump
修飾符。mediump
表示的是變量類型的精度盆驹。因為Fragment Shader是逐像素執(zhí)行圆丹,所以會盡量控制計算的復(fù)雜度。對于不需要過高精度的變量躯喇,可以手動指定精度從而提高性能辫封。精度主要分為下面3種。
-
highp
, 16bit廉丽,浮點數(shù)范圍(-2^62, 2^62)
倦微,整數(shù)范圍(-2^16, 2^16)
-
mediump
, 10bit,浮點數(shù)范圍(-2^14, 2^14)
正压,整數(shù)范圍(-2^10, 2^10)
-
lowp
, 8bit欣福,浮點數(shù)范圍(-2, 2)
,整數(shù)范圍(-2^8, 2^8)
如果你想所有的float都是高精度的焦履,可以在Shader頂部聲明precision highp float;
拓劝,這樣你就不需要為每一個變量聲明精度了雏逾。
運(yùn)算符
Shader可以使用所有C語言的運(yùn)算符。不過要注意的是二元運(yùn)算比如加法郑临,乘法等栖博,只能用在兩個類型相同的變量上,比如float只能和float相加厢洞。因為Shader不會為你進(jìn)行隱式的類型轉(zhuǎn)換仇让,這樣會增加GPU的負(fù)擔(dān)。我們表示float變量時躺翻,需要自行增加小數(shù)點丧叽,比如浮點數(shù)5要寫成5.0,否則會被認(rèn)定為整數(shù)公你。下面是能夠使用的運(yùn)算符踊淳,讀者可以當(dāng)做參考。
uniform變量
uniform變量會被所有Shader共享省店,比如有3個頂點嚣崭,Vertex Shader會被執(zhí)行3次,每次訪問的uniform變量都是同一個由js代碼設(shè)定好的值懦傍。下面是本文使用的設(shè)定uniform elapsedTime
的js代碼雹舀。
elapsedTimeUniformLoc = gl.getUniformLocation(program, 'elapsedTime');
gl.uniform1f(elapsedTimeUniformLoc, elapesdTime);
首先獲取uniform elapsedTime
在Shader中的位置,然后設(shè)置它的值粗俱。uniform1f
是uniformXXX
函數(shù)簇里面用來設(shè)置一個float
類型uniform的方法说榆。通過uniformXXX
里的XXX很容易看出來這個方法是設(shè)置什么類型的uniform的,下面是常見的幾種格式寸认。
-
uniform{n}{type}
n表示數(shù)目1~4签财,type表示類型,float
是f
偏塞,int
是i
唱蒸,unsigned int
是ui
。所以設(shè)置一個整數(shù)就是uniform1i
灸叼。 -
uniform{n}{type}v
相比于上面的多了一個v神汹,表示向量,所以傳遞的參數(shù)就是類型為type古今,維度為n的向量屁魏。 -
uniformMatrix{n}{type}v
這個用來設(shè)置類型為type nxn的矩陣。
上面這些方法會在后面的文章中用到捉腥,這里大致了解即可氓拼。
varying變量
varying
變量是Vertex Shader和Fragment Shader的橋梁,F(xiàn)ragment Shader中的varying
變量由Vertex Shader中的varying
變量自動插值計算出來。因為Fragment Shader是逐像素執(zhí)行桃漾,某些使用varying
變量的效果在Fragment Shader中實現(xiàn)會更加細(xì)膩坏匪,比如光照效果。
向量的訪問
當(dāng)我們擁有一個vec4
變量呈队,我們可以有很多種方法訪問它內(nèi)部的值剥槐。
-
vec4.x
,vec4.y
,vec4.z
,vec4.w
通過x,y宪摧,z,w可以分別取出4個值颅崩。 -
vec4.xy
,vec4.xx
,vec4.xz
,vec4.xyz
任意組合可以取出多種維度的向量几于。 -
vec4.r
,vec4.g
,vec4.b
,vec4.a
還可以通過r,g沿后,b沿彭,a分別取出4個值,同上可以任意組合尖滚。 -
vec4.s
,vec4.t
,vec4.p
,vec4.q
還可以通過s喉刘,t,p漆弄,q分別取出4個值睦裳,同上可以任意組合。
vec3
和vec2
也是同樣撼唾,無非就是少了幾個變量廉邑,比如vec3
只有x,y倒谷,z蛛蒙。vec2
只有x,y渤愁。
內(nèi)置方法
有很多內(nèi)置方法可以使用牵祟,如果可以選擇內(nèi)置方法實現(xiàn)算法,避免自己寫代碼再實現(xiàn)一遍抖格,因為內(nèi)置的方法能夠得到更好的硬件支持诺苹。下面是可用的方法表格。
可能很多方法你都不知道有什么用他挎,沒關(guān)系筝尾,后面使用到時我會再做解釋。
上面的介紹覆蓋了Shader的大部分基礎(chǔ)知識办桨,當(dāng)然還有很多使用細(xì)節(jié)和不常用的知識沒有介紹筹淫,這些知識會在后面使用到時再詳細(xì)介紹,這樣可以避免大家剛開始就對Shader產(chǎn)生畏懼感。