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