灰度滤镜
灰度滤镜就是获取我们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);
然后先看一个纹理元素的坐标纹理坐标对应到我们六边形中组成的矩形时,通过上边矩阵的坐标值反推得到原始纹理坐标值,然后对应纹理坐标系中的变换得到矩阵纹理坐标点
接下来,我们分析整个由六边形组成的图中,矩阵的坐标到底应该参考哪一个点
由最上边的图中可以知道,当当前位置在
横轴是偶数,纵轴是奇数的向量位置取左上角右下角
横轴是偶数,纵轴是偶数的向量位置取左下角右上角
横轴是奇数,纵轴是奇数的向量位置取左上角右下角
横轴是奇数,纵轴是偶数的向量位置取左下角右上角
所以才有了上边的判断坐标的代码
欢迎关注我的公众号,专注iOS开发、大前端开发、跨平台技术分享。