OpenGL ES 案例:灰度+颠倒+马赛克滤镜
2020-08-12 本文已影响0人
w執念
本案例的目的是理解如何用GLSL实现灰度,颠倒,正方形马赛克,六边形马赛克,三角形马赛克共五种滤镜。具体效果如下
效果2.gif
要实现我们的滤镜效果,首先得能通过GLSL方式正常显示一张纹理图片。对应的顶点着色器和片元着色器的代码如下:
//顶点着色器
attribute vec4 Position;
attribute vec2 TextureCoords;
varying vec2 TextureCoordsVarying;
void main (void) {
gl_Position = Position;
TextureCoordsVarying = TextureCoords;
}
//片元着色器
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {
vec4 mask = texture2D(Texture, TextureCoordsVarying);
gl_FragColor = vec4(mask.rgb, 1.0);
}
灰度滤镜
灰度滤镜的实现原理是让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);
gl_FragColor = vec4(vec3(luminance), 1.0);
}
颠倒滤镜
在片元着色器中,翻转纹理坐标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;
}
马赛克滤镜
⻢赛克效果就是把图⽚的⼀个相当⼤⼩的区域⽤同⼀个点的颜⾊来表示.可以认为是⼤规模的降低图像的分辨率,⽽让图像的⼀些细节隐藏起来。不同的马赛克效果的算法实现都有一定的区别,但本质上都是通过对片元着色器的纹理坐标的运算操作重新得到对应纹素的过程。
正方形马赛克滤镜
正方形马赛克的实现,大致可以分为以下几个步骤:
- 根据纹理坐标计算出实际图像中的位置
- 计算出一个小马赛克的坐标
- 将马赛克坐标换算回纹理坐标
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;
}
扩展:若要实现局部马赛克,可判断小马赛克的坐标位置实现,例如
if (XYMosaic.x>200.0) {
vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);
vec4 color = texture2D(Texture, UVMosaic);
gl_FragColor = color;
}
else{
vec4 color =texture2D(Texture,TextureCoordsVarying);
gl_FragColor = color;
}
六边形马赛克滤镜
六边形马赛克原理:将一张图片,分割成由六边形组成,再取每个六边形的中心点点画出一个个的矩形,根据矩形的奇偶排列情况求出对应的2个中心点,并计算纹理坐标与两个中心点的距离,根据距离判断,采取就近原则,当前的六边形就采用近的中心点的颜色值。
如图,画出很多⻓和宽⽐例为 3:√3 的的矩形阵,然后我们可以对每个点进⾏编号,采⽤坐标系标记。假如我们的屏幕的左上点为上图的(0,0)点,则屏幕上的任⼀点我们找到它所对应的那个矩形了。假定我们设定的矩阵⽐例为 3LEN : √3LEN ,那么屏幕上的任意点(x, y)所对应的矩阵坐标为(int(x/(3LEN)), int(y/(√3LEN)))。然后再根据行列的奇偶情况,求对应的中心点纹理坐标v1、v2,当奇行奇列和偶行偶列时,两个点对应的纹理坐标就分别为(0,0) (1,1),当奇行偶列和偶行奇列时,两个点对应的纹理坐标就分别为(0,1) (1,0)。
例图2.png
此时我们就可以计算出和两个中心点的坐标,以偶行偶列的情况,如下图
例图3.png
拿到这两个中心点的坐标后,最后我们只需要分别求出这两个中心点与当前像素点的距离,取距离近的那个的中心点的颜色值作为六边形的颜色值。具体代码如下:
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 TB = 1.5;
float x = TextureCoordsVarying.x;
float y = TextureCoordsVarying.y;
int wx = int(x / TB / length);
int wy = int(y / TR / length);
vec2 v1, v2, vn;
if (wx/2 * 2 == wx) {
if (wy/2 * 2 == wy) {
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 {
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) {
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 {
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;
}
三角形马赛克
三角形马赛克是由六边形马赛克演变而来,得到三角形的前提,就是的先有六边形,然后将正六边形6等分,每个三角形都是正三角形,再求出纹理坐标与六边形中心点的夹角,同时求出每个三角形的中心点,根据夹角判断,夹角属于哪个三角形,就将该三角形的中心点颜色作为整个三角形的纹素
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
float mosaicSize = 0.03;
void main (void){
const float TR = 0.866025;
const float PI6 = 0.523599;
float x = TextureCoordsVarying.x;
float y = TextureCoordsVarying.y;
int wx = int(x/(1.5 * mosaicSize));
int wy = int(y/(TR * mosaicSize));
vec2 v1, v2, vn;
if (wx / 2 * 2 == wx) {
if (wy/2 * 2 == wy) {
v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy));
v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * TR * float(wy + 1));
} else {
v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy + 1));
v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * TR * float(wy));
}
} else {
if (wy/2 * 2 == wy) {
v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy + 1));
v2 = vec2(mosaicSize * 1.5 * float(wx+1), mosaicSize * TR * float(wy));
} else {
v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy));
v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * 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 mid = texture2D(Texture, vn);
float a = atan((x - vn.x)/(y - vn.y));
vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);//求出6个area的面积的中心点?
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 = color;
}