iOS知识集视频iOS开发资料收集区

GPU处理图像 Shader的入门

2015-09-06  本文已影响10391人  星光社的戴铭

Shader着色器

Shader出现在OpenGL ES 2.0中,允许创建自己的Shader。必须同时创建两个Shader,分别是Vertex shader和Fragment shader.

Shader工具

Shader会有很多坑,不过一些工具能够帮助你跳过这些坑

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属性负责变量精度,这个被加入可以提高效率
uniform sampler2D inputImageTexture; //接收一个图片的引用,当做2D的纹理,这个数据类型就是smpler2D。
void main()
{
     gl_FragColor = texture2D(inputImageTexture, textureCoordinate); //texture是GLSL(着色语言)特有的方法
}

GLSL着色语言

GLSL的官方快速入门指导

变量赋值

三个可以赋值给我们的变量的标签

向量

有很多种向量,但是有三种会经常看到

矩阵

是浮点数组的数组。三个经常处理的矩阵对象

向量和矩阵运算

线性代数发挥作用的地方。想知道线性代数如何工作可以看这个资源站:http://betterexplained.com/articles/linear-algebra-guide/

线性代数可以一次在很多值上进行并行操作,so,正好适合需求,GLSL内建了很多函数可以处理庞大的计算转换

GLSL特有函数

GLSL内建的函数可以在Shaderific网站上找到:http://www.shaderific.com/glsl-functions。很多C语言数学库基本数学运算都有对应的函数。

复杂的Shader的例子

一个饱和度调节的Fragment shader的例子,出自《图形着色器:理论和实践》这本书。

varying highp vec2 textureCoordinate; //

uniform sampler2D inputImageTexture;
uniform lowp float saturation;

const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721); //光亮度里三个值相加要为1,各个值代表着颜色的百分比,中间是绿色的值,70%的比重会让效果更好点。

void main()
{
     lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate); //根据坐标取样图片颜色信息
     lowp float luminance = dot(textureColor.rgb, luminanceWeighting); //GLSL中的点乘运算,线性代数的点运算符相乘两个数字。点乘计算需要将纹理颜色信息和相对应的亮度权重相乘。然后取出所有的三个值相加到一起计算得到这个像素的中和亮度值。
     lowp vec3 greyScaleColor = vec3(luminance); //创建一个三个值都是亮度信息的vec3,如果只指定一个值,编译器会将其它的都设置成这个值
     gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w); //用mix函数把计算的灰度值,初识的纹理颜色和得到的饱和度信息结合起来。
}

球形滤镜示例

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)); //归一化坐标空间需要考虑屏幕是一个单位宽和一个单位长。
     highp float distanceFromCenter = distance(center, textureCoordinateToUse); //计算特定像素点距离球形的中心有多远。使用GLSL内建的distance()函数,用勾股定律计算出中心坐标和长宽比矫正过的纹理坐标的距离
     lowp float checkForPresenceWithinSphere = step(distanceFromCenter, radius); //计算片段是否在球体内。

     distanceFromCenter = distanceFromCenter / radius;  //标准化到球心的距离,重新设置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()函数以刚才创建的球法线和折射率来计算当光线通过球时从任意一个点看起来如何。

     gl_FragColor = texture2D(inputImageTexture, (refractedVector.xy + 1.0) * 0.5) * checkForPresenceWithinSphere; //最后凑齐所有计算需要的颜色信息。
}

调试Shader

使用gl_FragColor调试代码。GPUImage是个开源的资源有些很酷的shader,非常好的学习shader的方式,可以拿一个你觉得很有意思的shader对着源码一点点看下去。GPUImage还有一个shader设计器https://github.com/BradLarson/GPUImage/tree/master/examples/Mac/ShaderDesigner 的Mac应用,可以测试shader而不用准备OpenGL代码。

性能调优

简单的方法达到调优目的,可以用下载Imagination Technologies PowerVR SDKhttp://community.imgtec.com/developers/powervr/这个工具帮助分析shader

边界探测

基于OpenCV库,不过这些步骤在GPUImage中都有完整的实现

Sobel边界探测

这种操作在滤镜方面比机器视觉方面多。Sobel边界探测用于探测边界的出现位置,边界是由明变暗或者由暗变明的区域。在被处理的图片中一个像素的亮度反映了这个像素周围边界的强度。

-1 0 +1
-2 0 +2
-1 0 +1

-1 -2 -1
0 0 0
+1 +2 +1


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()函数计算出水平和垂直矩阵转化后值的平方和的平方根的值,这个值会被拷贝进输出像素的红绿蓝通道中,这样就可以来代表边界的明显程度了。

     gl_FragColor = vec4(vec3(mag), 1.0);
}

Canny边界探测

Canny探测会比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 away
normalizedDirection = (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; //预期边界强度上下限
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边角探测

多步骤的方法来探测场景中的边角。

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

资料

相关数学

相关GLSL

上一篇下一篇

猜你喜欢

热点阅读