灰度滤镜
灰度滤镜就是获取我们YUV颜色空间里的亮度值Y,我们现在只讲解FragmentShader改动的部分,其他的顶点着色器中的代码是不用改动的。
灰度滤镜用很多中设置方法:
1、仅取绿色值法 - 将三种RGB色值都设置为G分量的值
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
void main (void) {
vec4 mask = texture2D(Texture, TextureCoordsVarying);
gl_FragColor = vec4(mask.g,mask.g,mask.g,1.0);
}
其中mask纹理像素值中的RGB全部设置为了mask中的G分量值。
2、浮点算法(权值法) - 对其中的颜色分量设置权重,我们设置的权重是按照GPUImage中的权重进行设置的(0.2125, 0.7154, 0.0721),看代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
void main (void) {
vec4 mask = texture2D(Texture, TextureCoordsVarying);
float luminance = dot(mask.rgb, W);
gl_FragColor = vec4(vec3(luminance), 1.0);
}
按照我们设置的权重值,分别为rgb中的分量设置权重,比较容易理解
这两个滤镜方法进行比较,其实仔细观察还是能看出下边的浮点算法滤镜的灰度会更纯好一些,上边的图片还是有一些其他颜色
颠倒滤镜
颠倒滤镜其实就是讲图片进行上下/左右的反转,这里我们以上下反转作为实例,直接看片元着色器部分的代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {
vec4 color = texture2D(Texture, vec2(TextureCoordsVarying.x, 1.0 - TextureCoordsVarying.y));
gl_FragColor = color;
}
其实只是将y方向的分量进行了反转,即用1-实际的纹理坐标中的y值,就得到了上下反转的结果,还是比较简单的
旋涡滤镜
旋涡滤镜其实就是将照片中间部分纹理进行一个一个旋转,并且随着半径增大旋转角度也会增大,直接看片元着色器中的代码部分
precision mediump float;
const float PI = 3.14159265;
uniform sampler2D Texture;
const float uD = 80.0;
const float uR = 0.5;
varying vec2 TextureCoordsVarying;
void main()
{
ivec2 ires = ivec2(512, 512);
float Res = float(ires.s);
vec2 st = TextureCoordsVarying;
float Radius = Res * uR;
vec2 xy = Res * st;
vec2 dxy = xy - vec2(Res/2., Res/2.);
float r = length(dxy);
//(1.0 - r/Radius);
float beta = atan(dxy.y, dxy.x) + radians(uD) * 2.0 * (-(r/Radius)*(r/Radius) + 1.0);
vec2 xy1 = xy;
if(r<=Radius)
{
xy1 = Res/2. + r*vec2(cos(beta), sin(beta));
}
st = xy1/Res;
vec3 irgb = texture2D(Texture, st).rgb;
gl_FragColor = vec4( irgb, 1.0 );
}
PI:我们的计算中的π,取值3.14159265
uR:设置为0.5,其实就是为了取旋涡半径用到的
ivec2:整形的二维向量,这里的ires其实就是一个512宽高的正方形
Res:取出当前正方形的边长
uD:旋转的角度,Radius是我们旋转的半径
xy:通过直径获得纹理坐标对应的物体坐标
dxy:取出纹理坐标减去半径之后的具体物体坐标
r:当前半径
最重要的旋转角度如何设置?
float beta = atan(dxy.y, dxy.x) + radians(uD) * 2.0 * (-(r/Radius)*(r/Radius) + 1.0);
atan(dxy.y, dxy.x):获取的当前的夹角,如果不设置其他的值,那么当前图片没有任何旋转效果。
radians(uD) * 2.0:在原来夹角的基础上加上我们设置的旋转角度802 = 160
(-(r/Radius)(r/Radius) + 1.0):抛物线衰减因子,通过距离圆心的距离计算我们旋转衰减的增益值
假如我们的向量的模r小于当前的正方形的一半也就是当前圆的半径:
r*vec2(cos(beta), sin(beta)) :旋涡效果
Res/2.:半径
得出旋转后的坐标
st = xy1/Res - 计算旋转后的纹理坐标
正方形马赛克
正方形马赛克就是马赛克的形状是正方形的
看代码部分
precision mediump float;
varying vec2 TextureCoordsVarying;
uniform sampler2D Texture;
//纹理图像尺寸
const vec2 TexSize = vec2(400.0, 400.0);
//马赛克设置的实际尺寸
const vec2 mosaicSize = vec2(10.0, 10.0);
void main()
{
//实际图像位置
vec2 intXY = vec2(TextureCoordsVarying.x*TexSize.x, TextureCoordsVarying.y*TexSize.y);
//floor返回小于、等于x的最大整数
//通过出发公式可以获取到某一段以内的数值都设置为同一个值
vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);
vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);
vec4 color = texture2D(Texture, UVMosaic);
gl_FragColor = color;
}
其中下边的方法是关键,如果在某一区间内,将这一段区域的像素值都设置为某一个整数位置的像素值,达到正方形马赛克的效果
vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);
六边形马赛克
六边形的马赛克就稍微有点复杂了,我们需要知道六边形的几何知识然后在解释,让自己更加理解.
我们将一张图片分割成六边形,并且让每一个六边形的颜色相同(取六边型的中心点作为我们整个六边形的颜色值),将其分割,分割之后其实有四种不同的矩形将一个六边形分割开来。
我们对照代码来分析:
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//六边型的增量
const float mosaicSize = 0.03;
void main (void)
{
float length = mosaicSize;
float TR = 0.866025;
//纹理坐标值
float x = TextureCoordsVarying.x;
float y = TextureCoordsVarying.y;
//转化为矩阵中的坐标
int wx = int(x / 1.5 / length);
int wy = int(y / TR / length);
vec2 v1, v2, vn;
//分析矩阵中的坐标是在奇数还是在偶数行,根据奇数偶数值来确定我们的矩阵的角标坐标值
if (wx/2 * 2 == wx) {
if (wy/2 * 2 == wy) {
//(0,0),(1,1)
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
} else {
//(0,1),(1,0)
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
}
}else {
if (wy/2 * 2 == wy) {
//(0,1),(1,0)
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
} else {
//(0,0),(1,1)
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
}
}
//获取距离
float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));
float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));
//设置具体的纹理坐标
if (s1 < s2) {
vn = v1;
} else {
vn = v2;
}
vec4 color = texture2D(Texture, vn);
gl_FragColor = color;
}
首先我们应该知道一个六边型的宽高比是3:√3,所以我们设置组成六边型的矩形比例也是3:√3,然后乘以一个增量mosaicSize = 0.03,然后我们将原纹理像素坐标转化到我们的矩形中得到矩形的坐标值
int wx = int(x / 1.5 / length);
int wy = int(y / TR / length);
然后先看一个纹理元素的坐标
纹理坐标对应到我们六边形中组成的矩形时,通过上边矩阵的坐标值反推得到原始纹理坐标值,然后对应纹理坐标系中的变换得到矩阵纹理坐标点
接下来,我们分析整个由六边形组成的图中,矩阵的坐标到底应该参考哪一个点
由最上边的图中可以知道,当当前位置在
横轴是偶数,纵轴是奇数的向量位置取左上角右下角
横轴是偶数,纵轴是偶数的向量位置取左下角右上角
横轴是奇数,纵轴是奇数的向量位置取左上角右下角
横轴是奇数,纵轴是偶数的向量位置取左下角右上角
所以才有了上边的判断坐标的代码