Shader著色器

Shader出現(xiàn)在OpenGL ES 2.0中袱蚓,允許創(chuàng)建自己的Shader钞啸。必須同時創(chuàng)建兩個Shader,分別是Vertex shader和Fragment shader.

Shader工具

Shader會有很多坑,不過一些工具能夠幫助你跳過這些坑

GPUImage:https://github.com/BradLarson/GPUImage

ShaderToy:https://www.shadertoy.com/

Shaderific:http://www.shaderific.com/

Quartz Composer:官方工具

Shader使用范例

Vertex shader

attribute vec4 position;
attribute vec4 inputTextureCoordinate体斩;
varying vec2 textureCoordinate;
void main(){    
    gl_position = position;    
    textureCoordinate = inputTextureCoordinate.xy;
}

Fragment shader

直通濾鏡

varying highp vec2 textureCoordinate;//highp屬性負(fù)責(zé)變量精度梭稚,這個被加入可以提高效率uniform sampler2D 
inputImageTexture;//接收一個圖片的引用,當(dāng)做2D的紋理絮吵,這個數(shù)據(jù)類型就是smpler2D弧烤。
void main(){    
    gl_FragColor = texture2D(inputImageTexture, textureCoordinate);//texture是GLSL(著色語言)特有的方法
}

GLSL著色語言

GLSL的官方快速入門指導(dǎo)

OpenGL ES:https://www.khronos.org/opengles/sdk/docs/reference_cards/OpenGL-ES-2_0-Reference-card.pdf

OpenGL:https://www.khronos.org/files/opengl-quick-reference-card.pdf

變量賦值

三個可以賦值給我們的變量的標(biāo)簽

Uniforms:在渲染循環(huán)里作為不變的輸入值

Attributes:隨頂點(diǎn)位置不同會變的輸入值

Varyings:用來在Vertex shader和Fragment shader之間傳遞信息的,比如在Vertex shader中寫入varying值蹬敲,然后就可以在Fragment shader中讀取和處理

向量

有很多種向量暇昂,但是有三種會經(jīng)常看到

vec2:兩個浮點(diǎn)數(shù)伴嗡,適合在Fragment shader中保存X和Y坐標(biāo)的情況

vec3:三個浮點(diǎn)數(shù)

vec4:四個浮點(diǎn)數(shù)急波,在圖像處理中持續(xù)追蹤每個像素的R,G,V,A這四個值。

矩陣

是浮點(diǎn)數(shù)組的數(shù)組瘪校。三個經(jīng)常處理的矩陣對象

mat2:相當(dāng)于保存了兩個vec2對象的值或四個浮點(diǎn)數(shù)澄暮。

mat3

mat4

向量和矩陣運(yùn)算

線性代數(shù)發(fā)揮作用的地方。想知道線性代數(shù)如何工作可以看這個資源站:http://betterexplained.com/articles/linear-algebra-guide/

線性代數(shù)可以一次在很多值上進(jìn)行并行操作阱扬,so泣懊,正好適合需求,GLSL內(nèi)建了很多函數(shù)可以處理龐大的計算轉(zhuǎn)換

GLSL特有函數(shù)

GLSL內(nèi)建的函數(shù)可以在Shaderific網(wǎng)站上找到:http://www.shaderific.com/glsl-functions价认。很多C語言數(shù)學(xué)庫基本數(shù)學(xué)運(yùn)算都有對應(yīng)的函數(shù)嗅定。

step():GPU處理?xiàng)l件邏輯不是很好。step()允許在不產(chǎn)生分支的前提下實(shí)現(xiàn)條件邏輯用踩。傳入step()函數(shù)的值小于閾值就返回0.0渠退,大于等于閾值就返回1.0。

mix():將兩個顏色值混合為一個脐彩。

clamp():可以確保值在一個區(qū)間內(nèi)碎乃。

復(fù)雜的Shader的例子

1.一個飽和度調(diào)節(jié)的Fragment shader的例子,出自《圖形著色器:理論和實(shí)踐》這本書惠奸。

varying highp vec2 textureCoordinate;//
uniform sampler2D inputImageTexture;
uniform lowp float saturation;
const mediump vec3 luminanceWeighting = vec3(0.2125,0.7154,0.0721);//光亮度里三個值相加要為1梅誓,各個值代表著顏色的百分比,中間是綠色的值佛南,70%的比重會讓效果更好點(diǎn)梗掰。
void main(){    
    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);//根據(jù)坐標(biāo)取樣圖片顏色信息
    lowp float luminance = dot(textureColor.rgb, luminanceWeighting);//GLSL中的點(diǎn)乘運(yùn)算,線性代數(shù)的點(diǎn)運(yùn)算符相乘兩個數(shù)字嗅回。點(diǎn)乘計算需要將紋理顏色信息和相對應(yīng)的亮度權(quán)重相乘及穗。然后取出所有的三個值相加到一起計算得到這個像素的中和亮度值。
    lowp vec3 greyScaleColor = vec3(luminance);//創(chuàng)建一個三個值都是亮度信息的vec3绵载,如果只指定一個值埂陆,編譯器會將其它的都設(shè)置成這個值
    gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);//用mix函數(shù)把計算的灰度值苛白,初識的紋理顏色和得到的飽和度信息結(jié)合起來。
}

2.球形濾鏡示例

varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp vec2 center;
uniform highp float radius;
uniform highp float aspectRatio;
uniform highp float refractiveIndex;

void main(){    
    highp vec2 textureCoordinateToUse = vec2(textureCoordinate.x, (textureCoordinate.y * aspectRatio +0.5-0.5* aspectRatio));//歸一化坐標(biāo)空間需要考慮屏幕是一個單位寬和一個單位長焚虱。
    highp float distanceFromCenter =distance(center, textureCoordinateToUse);//計算特定像素點(diǎn)距離球形的中心有多遠(yuǎn)购裙。使用GLSL內(nèi)建的distance()函數(shù),用勾股定律計算出中心坐標(biāo)和長寬比矯正過的紋理坐標(biāo)的距離
    lowp float checkForPresenceWithinSphere = step(distanceFromCenter, radius);//計算片段是否在球體內(nèi)
    distanceFromCenter = distanceFromCenter / radius;//標(biāo)準(zhǔn)化到球心的距離鹃栽,重新設(shè)置distanceFromCenter
    highp float normalizedDepth = radius * sqrt(1.0- distanceFromCenter * distanceFromCenter);//模擬一個玻璃球躏率,需要計算球的“深度”是多少。
    highp vec3 sphereNormal = normalize(vec3(textureCoordinateToUse - center, normalizedDepth));//歸一化
    highp vec3 refractedVector = refract(vec3(0.0,0.0, -1.0), sphereNormal, refractiveIndex);//GLSL的refract()函數(shù)以剛才創(chuàng)建的球法線和折射率來計算當(dāng)光線通過球時從任意一個點(diǎn)看起來如何谍咆。
    gl_FragColor = texture2D(inputImageTexture, (refractedVector.xy +1.0) *0.5) * checkForPresenceWithinSphere;//最后湊齊所有計算需要的顏色信息禾锤。
}

調(diào)試Shader

使用gl_FragColor調(diào)試代碼。GPUImage是個開源的資源有些很酷的shader摹察,非常好的學(xué)習(xí)shader的方式恩掷,可以拿一個你覺得很有意思的shader對著源碼一點(diǎn)點(diǎn)看下去。GPUImage還有一個shader設(shè)計器https://github.com/BradLarson/GPUImage/tree/master/examples/Mac/ShaderDesigner的Mac應(yīng)用供嚎,可以測試shader而不用準(zhǔn)備OpenGL代碼黄娘。

性能調(diào)優(yōu)

簡單的方法達(dá)到調(diào)優(yōu)目的,可以用下載Imagination Technologies PowerVR SDKhttp://community.imgtec.com/developers/powervr/這個工具幫助分析shader

消除條件邏輯:使用step()這樣的函數(shù)

減少依賴紋理的讀瓤说巍:如果希望從附近像素取樣而不是計算Fragment shader相鄰像素的偏差逼争,最好在Vertex shader中計算然后把結(jié)果以varying的方式傳入Fragment shader里。

計算盡量簡單:能夠得到一個近似值就盡量用劝赔,不要用類似sin()誓焦,cos(),tan()的比較消耗的操作着帽。

盡可能的將計算放到Vertex上:如果計算在圖片上會有相同的結(jié)果或線性變化最好這樣做杂伟。因?yàn)閂ertex shader對每個頂點(diǎn)運(yùn)行一次,而Fragment shader會在每個像素上運(yùn)行一次仍翰。

移動設(shè)備使用合適的精度:在向量上使用低精度的值會變得更快赫粥。兩個lowp vec4相加可以在一個時鐘周期內(nèi)完成,兩個highp vec4相加則需要四個時鐘周期予借。

邊界探測

基于OpenCV庫越平,不過這些步驟在GPUImage中都有完整的實(shí)現(xiàn)

Sobel邊界探測

這種操作在濾鏡方面比機(jī)器視覺方面多。Sobel邊界探測用于探測邊界的出現(xiàn)位置灵迫,邊界是由明變暗或者由暗變明的區(qū)域秦叛。在被處理的圖片中一個像素的亮度反映了這個像素周圍邊界的強(qiáng)度。

第一步瀑粥,將彩色圖片弄成灰階圖挣跋,這個過程就是將每個像素的紅綠藍(lán)部分合一代表亮度的值。如是果YUV而不是RGB格式的可以省略這步利凑,因?yàn)閅UV是將亮度信息和色度信息分開的浆劲。如果簡化到只剩亮度的話一個像素周圍的邊界強(qiáng)度就可以由周圍3*3個臨近像素計算得到。這個計算涉及Convolution Matrix(卷積矩陣)哀澈,每個像素都要與這個矩陣計算出一個數(shù)值牌借,因?yàn)闆]有順序要求所以可以采取并行運(yùn)算。

Sobel的水平處理矩陣

-1 0 +1

-2 0 +2

-1 0 +1

Sobel的垂直矩陣

-1 -2 -1

0 0 0

+1 +2 +1

和Sobel類似的變體割按,Prewitt邊界探測掌挚。這個變體會在橫向豎向矩陣中用不同的矩陣蒿涎,但是運(yùn)作過程差不多。

OpenGL ES代碼
precision mediump float;//varying的都是在Vertex shader上定義了
varying vec2 textureCoordinate;
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 topLeftTextureCoordinate;
varying vec2 topRightTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying vec2 bottomLeftTextureCoordinate;
varying vec2 bottomRightTextureCoordinate;
uniform sampler2D inputImageTexture;
void main(){    
    float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;    
    float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;    
    float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;    
    float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;    
    float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;    
    float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;    
    float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;    
    float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;    
    float h = -bottomLeftIntensity -2.0* leftIntensity - topLeftIntensity + bottomRightIntensity +2.0* rightIntensity +  topRightIntensity;    
    float v = -topLeftIntensity -2.0* topIntensity - topRightIntensity + bottomLeftIntensity +2.0* bottomIntensity + bottomRightIntensity;    
    float mag = length(vec2(h, v));//length()函數(shù)計算出水平和垂直矩陣轉(zhuǎn)化后值的平方和的平方根的值,這個值會被拷貝進(jìn)輸出像素的紅綠藍(lán)通道中磕潮,這樣就可以來代表邊界的明顯程度了。
    gl_FragColor = vec4(vec3(mag),1.0);
}

Canny邊界探測

Canny探測會比Sobel復(fù)雜些跺嗽,這樣做會得到一條物體邊界的干凈線條得问。

探測過程:

先用Sobel矩陣得到邊界梯度的強(qiáng)度。這個和Sobel比就是最后一個計算有些不同

vec2 gradientDirection;
gradientDirection.x = -bottomLeftIntensity -2.0* leftIntensity - topLeftIntensity + bottomRightIntensity +2.0* rightIntensity + topRightIntensity;
gradientDirection.y = -topLeftIntensity -2.0* topIntensity - topRightIntensity + bottomLeftIntensity +2.0* bottomIntensity + bottomRightIntensity;
float gradientMagnitude = length(gradientDirection);
vec2 normalizedDirection = normalize(gradientDirection);
normalizedDirection = sign(normalizedDirection) * floor(abs(normalizedDirection) +0.617316);// Offset by 1-sin(pi/8) to set to 0 if near axis, 1 if awaynormalizedDirection = (normalizedDirection +1.0) *0.5;// Place -1.0 - 1.0 within 0 - 1.0
gl_FragColor = vec4(gradientMagnitude, normalizedDirection.x, normalizedDirection.y,1.0);

著色器步驟

precision mediump float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float texelWidth;//要處理的圖片中臨近像素之間的距離丈氓。
uniform highp float texelHeight;//同上
uniform mediump float upperThreshold;//預(yù)期邊界強(qiáng)度上下限
uniform mediump float lowerThreshold; 
void main(){    
    vec3 currentGradientAndDirection = texture2D(inputImageTexture, textureCoordinate).rgb;    
    vec2 gradientDirection = ((currentGradientAndDirection.gb *2.0) -1.0) * vec2(texelWidth, texelHeight);    
    float firstSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate + gradientDirection).r;
    float secondSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate - gradientDirection).r; 
    float multiplier = step(firstSampledGradientMagnitude, currentGradientAndDirection.r);    
    multiplier = multiplier * step(secondSampledGradientMagnitude, currentGradientAndDirection.r);    
    float thresholdCompliance = smoothstep(lowerThreshold, upperThreshold, currentGradientAndDirection.r);    
    multiplier = multiplier * thresholdCompliance;    
    gl_FragColor = vec4(multiplier, multiplier, multiplier,1.0);
}

Harris邊角探測

多步驟的方法來探測場景中的邊角周循。

先弄得只有亮度信息,再通過Sobel矩陣万俗,普里維特矩陣或者其它相關(guān)的矩陣計算出一個像素X和Y方向的梯度值湾笛,計算的結(jié)果會將X梯度傳入紅色部分,Y梯度傳入綠色部分闰歪,X與Y梯度的乘積傳入藍(lán)色部分嚎研。

對計算的結(jié)果進(jìn)行高斯模糊。將模糊后圖中取出紅綠藍(lán)編碼的值帶到計算邊角點(diǎn)可能性公式:

R = Ix2 × Iy2 ? Ixy × Ixy ? k × (Ix2 + Iy2)2

資料

相關(guān)數(shù)學(xué)

3D Math Primer for Graphics and Game Developmenthttp://www.amazon.com/Math-Primer-Graphics-Game-Development/dp/1568817231/ref=sr_1_1?ie=UTF8&qid=1422837187&sr=8-1&keywords=3d+math+primer+for+graphics+and+game+development

The Nature of Codehttp://natureofcode.com/

The Computational Beauty of Naturehttp://www.amazon.com/Computational-Beauty-Nature-Explorations-Adaptation/dp/0262561271/ref=sr_1_1?s=books&ie=UTF8&qid=1422837256&sr=1-1&keywords=computational+beauty+of+nature

相關(guān)GLSL

Graphic Shaders: Theory and Practicehttp://www.amazon.com/Graphics-Shaders-Theory-Practice-Second/dp/1568814348/ref=sr_1_1?s=books&ie=UTF8&qid=1422837351&sr=1-1&keywords=graphics+shaders+theory+and+practice

The OpenGL Shading Languagehttp://www.amazon.com/OpenGL-Shading-Language-Randi-Rost/dp/0321637631/ref=sr_1_1?s=books&ie=UTF8&qid=1422896457&sr=1-1&keywords=opengl+shading+language

OpenGL 4 Shading Language Cookbookhttp://www.amazon.com/OpenGL-Shading-Language-Cookbook-Second/dp/1782167021/ref=sr_1_2?s=books&ie=UTF8&qid=1422896457&sr=1-2&keywords=opengl+shading+language

GPU Gemshttp://http.developer.nvidia.com/GPUGems/gpugems_part01.html

GPU Pro: Advanced Rendering Techniqueshttp://www.amazon.com/GPU-Pro-Advanced-Rendering-Techniques/dp/1568814720/ref=sr_1_4?s=books&ie=UTF8&qid=1422837427&sr=1-4&keywords=gpu+pro

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末库倘,一起剝皮案震驚了整個濱河市临扮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌于樟,老刑警劉巖公条,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迂曲,居然都是意外死亡靶橱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門路捧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來关霸,“玉大人,你說我怎么就攤上這事杰扫《涌埽” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵章姓,是天一觀的道長佳遣。 經(jīng)常有香客問我识埋,道長,這世上最難降的妖魔是什么零渐? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任窒舟,我火速辦了婚禮,結(jié)果婚禮上诵盼,老公的妹妹穿的比我還像新娘惠豺。我一直安慰自己,他們只是感情好风宁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布洁墙。 她就那樣靜靜地躺著,像睡著了一般戒财。 火紅的嫁衣襯著肌膚如雪热监。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天饮寞,我揣著相機(jī)與錄音狼纬,去河邊找鬼。 笑死骂际,一個胖子當(dāng)著我的面吹牛疗琉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播歉铝,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盈简,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了太示?” 一聲冷哼從身側(cè)響起柠贤,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎类缤,沒想到半個月后臼勉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡餐弱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年宴霸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膏蚓。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓢谢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驮瞧,到底是詐尸還是另有隱情氓扛,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布论笔,位于F島的核電站采郎,受9級特大地震影響千所,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蒜埋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一真慢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧理茎,春花似錦、人聲如沸管嬉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚯撩。三九已至础倍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胎挎,已是汗流浹背沟启。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留犹菇,地道東北人德迹。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像揭芍,于是被迫代替她去往敵國和親胳搞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容