本篇文章对上篇文章OpenGL ES 滤镜——分屏滤镜做了补充。又多添加了几种滤镜的效果:灰度、倒置、马赛克、六边形马赛克、三角形马赛克。
1.灰度
灰度滤镜的实现方法有下面五种:
- 浮点算法:Gray=R0.3+G0.59+B*0.11
- 整数⽅方法:Gray=(R30+G59+B*11)/100
- 移位⽅方法:Gray =(R76+G151+B*28)>>8;
- 平均值法:Gray=(R+G+B)/3;
- 仅取绿⾊色:Gray=G;
最常用的是上面的方法1
,要注意各个权值相加的结果要等于1。绿色的占比最高,以为人的眼睛对绿色最为敏感,绿色值越深,观察到的图片颜色越暗淡。方法5
的实现原理是在取一个RGB颜色时,仅仅取它的绿色的值。
代码如下:
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);
//通过vec3生成一个三维向量(luminance,luminance,luminance)
//透明度为1
gl_FragColor = vec4(vec3(mask.g), 1.0);
}
复制代码
2.倒置
图像的倒置就比较简单了,只需要将纹理坐标的y值翻转即可。
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;
}
复制代码
3.马赛克
马赛克效果就是把图⽚的⼀个相当⼤小的区域⽤同一个点的颜⾊表示.可以认为是大规模的降低图像的分辨率,而让图像的一些细节隐藏起来。
效果图如下:
实现思路:我们把可以把图片分成很多个小的矩形,每个矩形的颜色值取矩形中的某一个点的颜色来填充。案例中我们取矩形的左上角顶点的颜色值,作为小矩形填充的颜色值。
代码及注释如下:
precision highp float;
uniform sampler2D Texture;
varying highp vec2 TextureCoordsVarying;
//图片大小
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) 内建函数 返回x小于或等于x的最大整数
//floor(intXY.x/MosaicSize.x)*MosaicSize.x 得到小矩形的顶点的x值
//floor(intXY.y/MosaicSize.y)*MosaicSize.y 得到小矩形的顶点的y值
vec2 XYMosaic = vec2(floor(intXY.x/MosaicSize.x)*MosaicSize.x, floor(intXY.y/MosaicSize.y)*MosaicSize.y);
//XYMosaic还原回纹理坐标体系中 即取值范围变回(0,1)
vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);
//根据新的纹理坐标去颜色值
gl_FragColor = texture2D(Texture, UVMosaic);
}
复制代码
4.六边形马赛克
同样上面的马赛克原理一样,只不过把小矩形换成了小六边形,这次我们取六边形中心点的颜色作为六边形的填充色。
由上图可以看出,我们仍然可以用矩形把图片进行分割。但是矩形中点的归属,到底属于哪个六边形,需要我们给出判断。也就是下图中小红点到底属于六边形d1还是属于六边d2呢?
我们先一步一步的来,首先我们需要确定矩形的大小。
4.1 确定矩形大小
根据正六边形特性,我们可以得出上面的几个数值。假设正六边形的边长为1,那么矩形的宽为1.5,矩形的高为√3/2,也就是矩形的宽高为3:√3。
定义以下三个变量
const float mosaicSize = 0.03;
const float TW = 1.5;
const float TH = 0.866025;
复制代码
mosaicSize
为单位长度,TW
为矩形的宽相对于单位长度的倍数,TH
为矩形的高相对于单位长度的倍数。
4.2 确定矩形位置
float x = TextureCoordsVarying.x;
float y = TextureCoordsVarying.y;
int wx = int(x/mosaicSize/TW);
int wy = int(y/mosaicSize/TH);
复制代码
wx
为矩形在x轴方向上的index,wy
为矩形在x轴方向上的index。然后再根据矩形的宽度和高度,矩形的顶点位置就很容易求出来了。
//左上角
vec2(float(wx)*mosaicSize*TW, float(wy)*mosaicSize*TH);
//右上角
vec2(float(wx+1)*mosaicSize*TW, float(wy)*mosaicSize*TH);
//右下角
vec2(float(wx+1)*mosaicSize*TW, float(wy+1)*mosaicSize*TH);
//左下角
vec2(float(wx)*mosaicSize*TW, float(wy+1)*mosaicSize*TH);
复制代码
4.3 确定点的归属
有了矩形的位置,我们就可以继续讨论点的归属问题了,其实通过前面的图片我们也可以知道,点离哪个六边形越近,就归属于哪个六边形嘛。但是还有两种情况需要我们讨论。就是我们到底应该比较点到v1跟v2的距离呢,还是比较点到v3跟v2的距离呢?这两行情况我们需要分别进行计算。
通过观察我们发现,当wx+wy的值为偶数是,属于上图的情况1,否则,属于情况2。
vec2 v1, v2, vn;
if ((wx + wy)/2 * 2 == (wx + wy)) {
v1 = vec2(float(wx)*mosaicSize*TW, float(wy)*mosaicSize*TH);
v2 = vec2(float(wx+1)*mosaicSize*TW, float(wy+1)*mosaicSize*TH);
} else {
v1 = vec2(float(wx+1)*mosaicSize*TW, float(wy)*mosaicSize*TH);
v2 = vec2(float(wx)*mosaicSize*TW, float(wy+1)*mosaicSize*TH);
}
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;
}
复制代码
4.4 最终确定颜色
确定了点归属,我们就可以根据顶点的颜色,给整个六边形填充颜色了。
vec4 color = texture2D(Texture, vn);
gl_FragColor = color;
复制代码
5.三角形马赛克
三角形马赛克是在六边形马赛克的基础上演变而来的。一个六边形由六个三角形组成。在分别取六个三角形的中心点的颜色进行填充三角形就好了。
precision highp float;
uniform sampler2D Texture;
varying highp vec2 TextureCoordsVarying;
const float mosaicSize = 0.03;
const float TW = 1.5;
const float TH = 0.866025;
const float PI6 = 0.523599;
void main(){
float x = TextureCoordsVarying.x;
float y = TextureCoordsVarying.y;
int wx = int(x/mosaicSize/TW);
int wy = int(y/mosaicSize/TH);
vec2 v1, v2, vn;
if ((wx + wy)/2 * 2 == (wx + wy)) {
v1 = vec2(float(wx)*mosaicSize*TW, float(wy)*mosaicSize*TH);
v2 = vec2(float(wx+1)*mosaicSize*TW, float(wy+1)*mosaicSize*TH);
} else {
v1 = vec2(float(wx+1)*mosaicSize*TW, float(wy)*mosaicSize*TH);
v2 = vec2(float(wx)*mosaicSize*TW, float(wy+1)*mosaicSize*TH);
}
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;
}
//以下为三角形处理
//计算反正切值,求得与x轴夹角
float vecx = x-vn.x;
float vecy = y-vn.y;
float a = atan(vecy,vecx);
//分别求出6个三角形的中心点
vec2 center1 = vec2(vn.x, vn.y - mosaicSize * TH / 2.0);
vec2 center2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TH / 2.0);
vec2 center3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TH / 2.0);
vec2 center4 = vec2(vn.x, vn.y + mosaicSize * TH / 2.0);
vec2 center5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TH / 2.0);
vec2 center6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TH / 2.0);
//判断当前纹理坐标点在哪个三角形
if (a<PI6*4.0 && a>=PI6*2.0) {
vn = center1;
} else if (a<PI6*2.0 && a>=0.0){
vn = center2;
} else if (a<0.0 && a>=-PI6*2.0) {
vn = center3;
} else if (a<-PI6*2.0 && a>=-PI6*4.0) {
vn = center4;
} else if (a<-PI6*4.0 && a>=-PI6*6.0) {
vn = center5;
} else if (a<PI6*6.0 && a>=PI6*4.0) {
vn = center6;
}
vec4 color = texture2D(Texture, vn);
gl_FragColor = color;
}
复制代码
float atan(float y,float x)
为反正切函数,返回其正切为y/x的角度。x和y的符号用来确定角度所在的象限,这个函数返回的值的范围是[0,π]。
float atan(float y/x)
为反正切函数,返回其正切为y/x的角度。这个函数返回的值的范围是[-π/2, π/2]。无法区分具体象限。