OpenGL ES入门:滤镜篇 - 漩涡、马赛克

栏目: 后端 · 发布时间: 5年前

内容简介:和上一篇文章详细内容请参考图片的显示由三个颜色通道(rgb)显示的,而灰度滤镜只有一个值,也就是说只要得到亮度便可。下面提供5种方式实现灰度滤镜(前三种是利用权重来实现)
OpenGL ES入门:滤镜篇 - 漩涡、马赛克

和上一篇文章 OpenGL ES入门: 滤镜篇 - 分屏滤镜 一样,使用 GLSL 实现滤镜的前提条件是 能够用GLSL显示普通图片

思路:

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

详细内容请参考 OpenGL/OpenGL ES入门: 使用OpenGL ES 渲染图片

灰度滤镜GLSL 算法解析

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

图片的显示由三个颜色通道(rgb)显示的,而灰度滤镜只有一个值,也就是说只要得到亮度便可。下面提供5种方式实现灰度滤镜(前三种是利用权重来实现)

原理:

  • 浮点算法: 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 highp vec2 varyTextureCoord;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721); // 借用GPUImage的值

void main() {
    vec4 mask = texture2D(Texture, varyTextureCoord);
    float temp = dot(mask.rgb, W);
    gl_FragColor = vec4(vec3(temp), 1.0);
}
复制代码

漩涡滤镜GLSL 算法解析

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

把我最喜欢的女明星旋转成这样,真是罪过。。。

原理:

图形漩涡主要是在某个半径范围内,把当前采样点旋转一定角度,旋转以后当前点的颜色就被旋转后的点的颜色代替,因此整个半径范围里会有旋转的效果。 如果旋转的时候旋转角度随着当前点距离半径的距离递减,整个图像就会出现漩涡效果,如上图一样。 这里会使用抛物线递减因子: (1.0 - (r / Radius) * (r / Radius))

片元着色器代码

precision mediump float; 

uniform sampler2D Texture; 
//旋转⻆角度
const float uD = 80.0; 
//旋涡半径
const float uR = 0.5;
//纹理坐标
varying vec2 TextureCoordsVarying;

void main() {
    //获取旋转的直径
    float Res = float(512); 
    //纹理坐标[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.0, Res/2.0);
    //r
    float r = length(dxy);
    //抛物线递减因⼦子:(1.0-(r/Radius)*(r/Radius) )
    float beta = atan(dxy.y, dxy.x) + radians(uD) * 2.0 * (1.0-(r/Radius)*(r/Radius));
    if(r <= Radius)
    {
        //获取的纹理坐标旋转beta度.
        xy = vec2(Res/2.0, 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 );
}
复制代码

解析:

相比前面所说的滤镜,这个算法比较复杂,上面代码中,每一行也都有注释,但是,个人感觉看起来还是挺难懂的(可能笔者自己学的太差),所以这里把自己理解的方式叙述一下,如果有错误,欢迎大家指正。

上述代码中的 Res ,类似把一张图片分成了512个像素点一样来进行分析,实际上最后几步也通过 xy/Res 把它转换回来,所以下面就针对一个像素点或者说一个纹理来理解。

请先熟悉上面代码,至少知道每个变量的所代表的意义。

对于一个纹理来说:

vec2 xy = 1 * st 表示着纹理坐标,

vec2 dxy = xy - vec2(1.0/2.0, 1.0/2.0) ,通过下图来理解一下

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

通过上面图片来理解 vec2 dxy = xy - vec2(1.0/2.0, 1.0/2.0)

然后通过 length() ,来取模,得到 r

先忽略 beta 这一行,看 if(r <= Radius) ,通过这个判断获取到的纹理像素,就是上图中深蓝色圆的纹素,所以最终形成的圆形漩涡就是通过这种方式来实现。

从这里也可以看出,如果你想控制圆形漩涡的半径或者圆形漩涡的位置都是可控的,即 uR 控制着圆的半径,向量 vec2(Res/2.0, Res/2.0) 控制着圆心位置,和 Res 无关,取决于分母 2 ,有兴趣的小伙伴可以尝试一下。

下面来看角度 beta 的获取

当前角度:

atan(dxy.y, dxy.x)

加剧漩涡角度:

atan(dxy.y, dxy.x) + radians(uD) * 2.0

加剧漩涡衰减角度:

atan(dxy.y, dxy.x) + radians(uD) * 2.0 * (1.0-(r/Radius)*(r/Radius))

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

上图理解当前角度的计算。

获取的纹理坐标旋转beta度,看下图,为了方便,

假设 (x0,y0) 为未旋转之前的点, (x1,y1) 为旋转之后的点,旋转角度为 beta ,长度即是模 r 不变.

那么 r * vec2(cos(beta), sin(beta)) 便可理解为向量 dx1y1 ,如果这里不清晰,可以尝试画个三角形,分别求出 cos、sin 的函数表达式,然后乘以模 r ,我相信应该很好理解,

所以 vec2(Res/2.0, Res/2.0) + r*vec2(cos(beta), sin(beta)) 也就是最终的向量 (x,y) ,如下图所示:

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

后面的代码也就是简单的获取纹理像素,然后填充到内建函数 gl_FragColor 中。

OK,有关漩涡滤镜实现原理的解析就到这里,如果大家发现描述有误,请及时提醒笔者改正,谢谢!!!

马赛克滤镜 GLSL 算法解析

原理:

马赛克效果就是把图片的一个相当大小的区域用同一个点的颜色来表示,可以认为是大规模的降低图像的分辨率,而让图片的一些细节隐藏起来。

矩形马赛克

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

片元着色器代码:

precision highp float;
// 纹理坐标
varying vec2 varyTextureCoord;
// 纹理采样器
uniform sampler2D Texture;
// 纹理图片Size
const vec2 TexSize = vec2(400.0, 400.0);
// 马赛克Size
const vec2 mosaicSize = vec2(10.0, 10.0);

void main () {
    vec2 intXY = vec2(varyTextureCoord.x * TexSize.x, varyTextureCoord.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;
}
复制代码

上面代码中,首先计算出 intXY ,表示实际图像位置; floor(x) 内建函数,表示返回小于/等于x的最大整数值。 所以 XYMosaic 表示小马赛克的坐标,然后换算出马赛克的纹理坐标即可

六边形马赛克

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

片元着色器代码

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

const float mosaicSize = 0.03;

void main () {
    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;
}
复制代码

解析:

我们需要做的效果就是让一张图片,分割成由六边形组成,让每个六边形中的颜色相同,下面采用直接取六边形中心点像素来实现

OpenGL ES入门:滤镜篇 - 漩涡、马赛克

如上图,画出很多长和宽比例为3 :√3的矩形排列,然后对每一个点进行编号。 假如我们的屏幕的左上点为上图的(0,0)点,则屏幕上的任一点我们找到它所对应的那个矩形了了。

假定我们设定的矩阵比例为 3*LEN : √3*LEN ,那么屏幕上的任意 点(x, y)所对应的矩阵坐标为 (int(x/(3*LEN)), int(y/ (√3*LEN)))

(wx, wy) 表示纹理坐标在所对应的矩阵坐标为:

wx = int(x/(1.5 * length))
wy = int(y/(TR * length))
复制代码
OpenGL ES入门:滤镜篇 - 漩涡、马赛克

观察上图,标出了四个矩形,通过观察可以了解到,屏幕上的任意点(x, y),对应哪一个六边形,只要找到点,对应上面哪个矩形的关键点越近便可,而矩形坐标为(wx, wy),那么要判断(x, y)在那个矩形的关键点近取决于什么呢?

对于任何一个矩形,他们的四个顶点分别为:

  • 左上:vec2(length * 1.5 * float(wx), length * TR * float(wy));
  • 左下:vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
  • 右上:vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
  • 右下:vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));

上面片元着色器中的if判断,则是找到对应的那个矩形,然后找到关键的两个点v1,v2。 最后根据两点间距离公式,算出分别距离v1,v2的长度,距离那一个短,便属于那一个六边形。

文章写之不易,奈何数学功底有限,如有错误,希望大家指正,谢谢!!!

案例传送门:

github.com/SXDgit/Open…

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

人工智能基础

人工智能基础

汤晓鸥、陈玉琨 / 华东师范大学出版社 / 2018-4-1 / 35.00元

人工智能基础(高中版)》是面向高中学生的教材。讲授人工智能的发展历史、基本概念以及实际应用,使学生理解人工智能的基本原理,特别是数据、算法与应用之间的相互关系。并结合常见的应用场景,理解人工智能技术(包括感知与决策)的基本工作方式,通过动手实践,更深入地理解人工智能技术的原理、能力,以及在实用中面临的挑战。本书强调人工智能基本理念与原理的传递,注重创造力、想象力、整体思考,以及动手能力的提升。一起来看看 《人工智能基础》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试