前面几篇文章已经详细介绍了OpenGL以及OpenGL ES的基本使用、加载一张图片、加载三维立体图像等,学习使用OpenGL的最终主要目的就是处理图片滤镜,视频滤镜,
常见的一些视频/图像的处理app基本上都是使用OpenGLES实现的,本篇介绍学习自定义一些常用滤镜以及实现原理,主要是顶点着色器程序和片元着色器程序,大部分色滤镜都是顶点着色器进行
1.分屏滤镜,二三四六九分屏,分屏滤镜主要是改变像素的纹素,通过坐标显示对应映射坐标的纹素
分屏滤镜就是处理像素点映射到想要显示的像素点的纹素值,可以从顶点着色器处理也可以在片元着色器中处理
//二分屏顶点着色器代码: attribute vec4 Position; attribute vec2 TextureCoords; varying vec2 TextureCoordsVarying; void main (void) { gl_Position = Position; TextureCoordsVarying = TextureCoords; } //二分屏片元着色器代码: precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; float y; if (uv.y >= 0.0 && uv.y <= 0.5) { y = uv.y + 0.25; } else { y = uv.y - 0.25; } gl_FragColor = texture2D(Texture, vec2(uv.x, y)); } //三分屏顶点着色器代码 attribute vec4 Position; attribute vec2 TextureCoords; varying vec2 TextureCoordsVarying; void main (void) { gl_Position = Position; TextureCoordsVarying = TextureCoords; } //三分屏片元着色器代码 uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; float y; if (uv.y >= 0.0 && uv.y <= 0.33) { y = uv.y + 0.33; }else if (uv.y > 0.66 && uv.y <= 1.0){ y = uv.y - 0.33; }else{ y = uv.y; } gl_FragColor = texture2D(Texture, vec2(uv.x, y)); } //四分屏顶点着色器 let verstr = """ attribute vec4 Position; attribute vec2 TextureCoords; varying vec2 TextureCoordsVarying; void main (void) { gl_Position = Position; TextureCoordsVarying = TextureCoords; } """ //四分屏片元着色器程序代码 let fragStr = """ precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main() { vec2 uv = TextureCoordsVarying.xy; float y; float x; if (uv.y >= 0.0 && uv.y <= 0.5) { y = uv.y * 2.0; } else { y = (uv.y - 0.5) * 2.0; } if (uv.x >= 0.0 && uv.x <= 0.5) { x = uv.x * 2.0; } else { x = (uv.x - 0.5) * 2.0; } gl_FragColor = texture2D(Texture, vec2(x, y)); } """ //六分屏片元着色器代码,顶点着色器和前面的一样 let fsh6 = """ precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main(){ vec2 uv = TextureCoordsVarying.xy; if (uv.x < 0.3333) { uv.x = uv.x * 3.0; }else if(uv.x > 0.6666){ uv.x = (uv.x - 0.6666) * 3.0; }else{ uv.x = (uv.x - 0.3333) * 3.0; } if (uv.y < 0.5){ uv.y = uv.y * 2.0; }else{ uv.y = (uv.y - 0.5) * 2.0; } gl_FragColor = texture2D(Texture, uv); } //九分屏片元着色器程序代码 let fsh9 = """ precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; void main(){ vec2 uv = TextureCoordsVarying.xy; if (uv.x < 0.3333) { uv.x = uv.x * 3.0; }else if(uv.x > 0.6666){ uv.x = (uv.x - 0.6666) * 3.0; }else{ uv.x = (uv.x - 0.3333) * 3.0; } if (uv.y < 0.3333){ uv.y = uv.y * 3.0; }else if(uv.y < 0.6666){ uv.y = (uv.y - 0.3333) * 3.0; }else { uv.y = (uv.y - 0.6666) * 3.0; } gl_FragColor = texture2D(Texture, uv); } """
2.灰度滤镜,就是使rgb三色的平衡,0.2125, 0.7154, 0.0721,人眼对绿色比较敏感,所以绿色值更大一些
灰度滤镜有多种实现方法:
1.浮点算法:Gray=R*0.3+G*0.59+B*0.11
2.整数?方法:Gray=(R*30+G*59+B*11)/100
3.移位?方法:Gray =(R*76+G*151+B*28)>>8;
4.平均值法:Gray=(R+G+B)/3;
5.仅取绿?色:Gray=G
let maskFsh = """ precision highp float; uniform sampler2D Texture; varying highp vec2 TextureCoordsVarying; const highp vec3 w = vec3(0.2125, 0.7154, 0.0721); void main(){ vec4 color = texture2D(Texture,TextureCoordsVarying); float color1 = dot(color.rgb,w); gl_FragColor = vec4(vec3(color1),1.0); } """
2.漩涡滤镜,给定中心点、半径,旋转角度,距离中心点约近旋转角度越大,坐标某点的颜色值等于旋转之后的纹素颜色值,图像漩涡主要是在某个半径范围里,把当前采样点旋转 ?定?角度,旋转以后当前点的颜色就被旋转后的点的颜色代替,因此整个半径范围里会有旋转的效果。如果旋 转的时候旋转?角度随着当前点离半径的距离递减,整个图像就会出现漩涡效果。这?使?了了抛物线递减因 子:(1.0-(r/Radius)*(r/Radius))。
//漩涡滤镜片元着色器代码: precision mediump float; //PI const float PI = 3.14159265; //纹理理采样器器 uniform sampler2D Texture; //旋转?角度 const float uD = 80.0; //旋涡半径 const float uR = 0.5; //纹理理坐标 varying vec2 TextureCoordsVarying; void main() { //旋转正?方形范围:[512,512] ivec2 ires = ivec2(512, 512); //获取旋转的直径 float Res = float(ires.s); //纹理理坐标[0,0],[1,0],[0,1],[1,1]... vec2 st = TextureCoordsVarying; //半径 = 直径 * 0.5; float Radius = Res * uR; //准备旋转处理理的纹理理坐标 = 纹理理坐标 * 直径 vec2 xy = Res * st; //纹理理坐标减去中点 vec2 dxy = xy - vec2(Res/2., Res/2.); //r 半径 即跟中点的距离 float r = length(dxy); //抛物线递减因?子:(1.0-(r/Radius)*(r/Radius) ) 衰减因子为二次函数 float beta = atan(dxy.y, dxy.x) + radians(uD) * (1.0-(r/Radius)*(r/Radius)); if(r<=Radius) { //获取的纹理理坐标旋转beta度. xy = Res/2.0 + r*vec2(cos(beta), sin(beta)); } //st = 旋转后的纹理理坐标/旋转范围 st = xy/Res; //将旋转的纹理理坐标替换原始纹理理坐标TextureCoordsVarying 获取对应像素点的颜?色. vec3 irgb = texture2D(Texture, st).rgb; //将计算后的颜?色填充到像素点中 gl_FragColor gl_FragColor = vec4( irgb, 1.0 ); }
3.马赛克滤镜,马赛克滤镜就是某一小半径大小的圆内的颜色值的相同,都取圆心的颜色值,
马赛克效果就是把图片的一个相当??的区域?同一个 点的颜色来表示.可以认为是大规模的降低图像的分辨 率,?而让图像的?一些细节隐藏起来。
//马赛克滤镜片元着色器代码: on mediump float; //纹理理坐标 varying vec2 TextureCoordsVarying; //纹理理采样器器 uniform sampler2D Texture; //纹理理图?片size const vec2 TexSize = vec2(400.0, 400.0); //?马赛克Size const vec2 mosaicSize = vec2(16.0, 16.0); void main() { //计算实际图像位置 vec2 intXY = vec2(TextureCoordsVarying.x*TexSize.x, TextureCoordsVarying.y*TexSize.y); // floor (x) 内建函数,返回?小于/等于X的最?大整数值. // 0123456789 假如m大小为3 ,floor(x/3)* 3,结果,012取0 345取3 678 取6,就形成了某一片是一个文素的颜色,形成马赛克 // floor (intXY.x / mosaicSize.x) * mosaicSize.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. gl_FragColor = color; }
4.六边形马赛克
滤镜实现思路: 我们要做的效果就是让一张图片,分割成由六边形组成,让每个六边形中的颜色相同(直接取六边形中?点像素RGB较?便,我们 这里采?的就是这种?方法)将它进?行行分割,取每个六边形的中?点画出?个六边形,如下图:
如上图,画出很多长和宽比例为 2:√3 的的矩形阵。然后我们可以对每个点进行编号,如上图中,采?用坐标系标记.
假如我们的屏幕的左上点为上图的(0,0)点,则屏幕上的任?点我们找到它所对应的那个矩形了了。 假定我们设定的矩阵?例例为 2*LEN : √3*LEN ,那么屏幕上的任意 点(x, y)所对应的矩阵坐标为(int(x/(2*LEN)), int(y/ (√3*LEN)))。
//wx,wy -> 表示纹理坐标在所对应的矩阵坐标为 int wx = int(x /( 1.5 * length)); int wy = int(y /(TR * length));
//六边形马赛克片元着色器代码 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; }
5.三角形马赛克
三角形马赛克和六边形马赛克原理类是,理解了六边形马赛克实现原理,三角形就是把六边形分成了六个三角形,每个三角形内的颜色值取同一个。
//三角形马赛克的片元着色器代码: 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); 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; }
由于篇幅过长还有一部分滤镜效果见下一篇,所有滤镜效果以及加载方法等详细代码github地址:https://github.com/duzhaoquan/ImagesVideoFilters.git
原文:https://www.cnblogs.com/duzhaoquan/p/13181709.html