OpenGL ES 案例之马赛克滤镜
2020-08-20 本文已影响0人
爱看书de图图
结合实际案例,我们来实现各种滤镜效果,首先看一下效果:
这里的基础功能搭建,和不通着色器的调用,我们不在赘述,跟上一篇案例一模一样。OpenGL ES案例之GLSL分屏滤镜。接下来我们注重讲述各个滤镜效果的实现原理,简单的一笔带过,复杂的我们详细描述。希望大家都可以看懂。
灰度滤镜
灰度滤镜的原理算法如下公式,比较常用的是第一种,至于第五种的意思,就是只取绿色,因为人眼对绿色敏感度较高,绿色越少灰度越高。灰度滤镜的实现方法有很多,比如GPUImage
,或者系统自带的CoreImage
。
- 浮点算法:
Gray=R*0.3+G*0.59+B*0.11
- 整数⽅法:
Gray=(R*30+G*59+B*11)/100
- 移位⽅法:
Gray =(R*76+G*151+B*28)>>8
- 平均值法:
Gray=(R+G+B)/3
- 仅取绿色:
Gray=G
片元着色器代码部分:
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//RGB的变换因子,即权重值
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
void main(){
//获取对应纹理坐标系下色颜色值
vec4 mask = texture2D(Texture, TextureCoordsVarying);
//将颜色mask 与 变换因子相乘得到灰度值
float luminance = dot(mask.rgb, W);
//将灰度值转换为(luminance,luminance,luminance,mask.a)并填充到像素中
gl_FragColor = vec4(vec3(luminance), 1.0);
}
颠倒滤镜
颠倒滤镜比较简单,就是改变纹理的映射关系即可。
vec4 color = texture2D(Texture, vec2(TextureCoordsVarying.x, 1.0 - TextureCoordsVarying.y));
马赛克滤镜
马赛克滤镜这里设置三种马赛克图形。
- 正方形
- 六边形
- 三角形
马赛克原理:把图片的一个相当大小的区域用同一个颜色值来表示,可以认为是大规模的降低图像的分辨率,从而让图像的一些细节隐藏起来。
正方形马赛克
假定我们纹理图片如上图所示,马赛克的作用就是把其他颜色都换成临近的红色,来达到模糊的效果。算法步骤:
1.扩大纹理,比如扩大400倍
2.把扩大后的纹理比上马赛克的size,然后向下取整再乘以马赛克的size以达到模糊效果
3.把纹理缩小到原始大小
precision mediump float;
varying vec2 TextureCoordsVarying;
uniform sampler2D Texture;
const vec2 TexSize = vec2(400.0, 400.0);
const vec2 mosaicSize = vec2(16.0, 16.0);
void main()
{
vec2 intXY = vec2(TextureCoordsVarying.x*TexSize.x, TextureCoordsVarying.y*TexSize.y);
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;
}
六边形马赛克
六边形马赛克原理:我们看上图,六边形马赛克图如左边所示,当我们把它分割以后,我们观察绿色框框内的青色矩形,我们知道,这些图形可以看做是如右图的两种
AB
图形组成的。
偶行偶列 /,矩形A,顶点为左上,右下
偶行奇列 \,矩形B,顶点为左下,右上
奇行奇列 /,矩形A,顶点为左上,右下
奇行偶列 \,矩形B,顶点为左下,右上
那么我们所去的所有像素点都会落在AB
矩形中,在这里我们用A
矩形来举例说明。假设我们取了像素点X
,那么A
矩形代表六边形的顶点就是顶点1和顶点2,所以像素点X
的取值必然是在顶点1和顶点2中选择一个。然后我们采用临近原则,由于d2
小于d1
,所以我们这里取得像素中心点的纹理就是顶点2的色值。对应的原理也适用于B
矩形,B
矩形对应的六边形的顶点就是左下和右上方的顶点。
AB
矩形的宽高比我们知道是3比根号3,如果像素点X的坐标是(x,y)
,那么对应的像素点X
在矩阵中的坐标就为(int(x /( 1.5 * length)),int(y /(TR * length)))
四个顶点的映射坐标就是下图:
片元着色器代码:
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//六边形的边长
const float mosaicSize = 0.03;
void main(){
float length = mosaicSize;
//矩形的高的比例为√3,取值 √3/2 ,也可以直接取√3
float TR = 0.866025;
//矩形的长的比例为3,取值 3/2 = 1.5,也可以直接取3
float TB = 1.5;
//取出纹理坐标
float x = TextureCoordsVarying.x;
float y = TextureCoordsVarying.y;
//根据纹理坐标计算出对应的矩阵坐标
//即 矩阵坐标wx = int(纹理坐标x/ 矩阵长),矩阵长 = TB*len
//即 矩阵坐标wy = int(纹理坐标y/ 矩阵宽),矩阵宽 = TR*len
int wx = int(x / TB / length);
int wy = int(y / TR / length);
vec2 v1, v2, vn;
//判断wx是否为偶数,等价于 wx % 2 == 0
if (wx/2 * 2 == wx) {
if (wy/2 * 2 == wy) {//偶行偶列
//(0,0),(1,1)
v1 = vec2(length * TB * float(wx), length * TR * float(wy));
v2 = vec2(length * TB * float(wx+1), length * TR * float(wy+1));
}else{//偶行奇列
//(0,1),(1,0)
v1 = vec2(length * TB * float(wx), length * TR * float(wy+1));
v2 = vec2(length * TB * float(wx+1), length * TR * float(wy));
}
}else{
if (wy/2 * 2 == wy) {//奇行偶列
//(0,1),(1,0)
v1 = vec2(length * TB * float(wx), length * TR * float(wy+1));
v2 = vec2(length * TB * float(wx+1), length * TR * float(wy));
}else{//奇行奇列
//(0,0),(1,1)
v1 = vec2(length * TB * float(wx), length * TR * float(wy));
v2 = vec2(length * TB * 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));
//选择距离小的则为六边形的中心点,且获取它的颜色
vn = (s1 < s2) ? v1 : v2;
//获取六边形中心点的颜色值
vec4 color = texture2D(Texture, vn);
gl_FragColor = color;
}
三角形马赛克
三角形马赛克原理:三角形马赛克是由六边形马赛克演变而来,把六边形六等分,就得到了三角形马赛克。
//获取像素点与中心点的角度
float a = atan((x-vn.x)/(y-vn.y));
//判断夹角,属于哪个三角形,则获取哪个三角形的中心点坐标
vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);
vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0);
vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
if (a >= PI6 && a < PI6 * 3.0) {
vn = area1;
}else if (a >= PI6 * 3.0 && a < PI6 * 5.0){
vn = area2;
}else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0) || (a < -PI6 * 5.0 && a > -PI6 * 6.0)){
vn = area3;
}else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0){
vn = area4;
}else if (a <= -PI6 && a > -PI6 * 3.0){
vn = area5;
}else if (a > -PI6 && a < PI6){
vn = area6;
}
//获取对应三角形重心的颜色值
vec4 color = texture2D(Texture, vn);
// 将颜色值填充到片元着色器内置变量gl_FragColor
gl_FragColor = color;