美文网首页
五、滤镜

五、滤镜

作者: Mjs | 来源:发表于2021-02-03 14:17 被阅读0次

九分屏

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

void main() {
    vec2 uv = TextureCoordsVarying.xy;
    if (uv.x < 1.0 / 3.0) {
        uv.x = uv.x * 3.0;
    } else if (uv.x < 2.0 / 3.0) {
        uv.x = (uv.x - 1.0 / 3.0) * 3.0;
    } else {
        uv.x = (uv.x - 2.0 / 3.0) * 3.0;
    }
    if (uv.y <= 1.0 / 3.0) {
        uv.y = uv.y * 3.0;
    } else if (uv.y < 2.0 / 3.0) {
        uv.y = (uv.y - 1.0 / 3.0) * 3.0;
    } else {
        uv.y = (uv.y - 2.0 / 3.0) * 3.0;
    }
    gl_FragColor = texture2D(Texture, uv);
}

在x,y三等分时直接取全图值

灰度

1.浮点算法:Gray=R0.3+G0.59+B0.11
2.整数⽅法:Gray=(R
30+G59+B11)/100
3.移位⽅法:Gray =(R76+G151+B*28)>>8;
4.平均值法:Gray=(R+G+B)/3;
5.仅取绿⾊:Gray=G;
在人眼中绿色的权重最高

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);
}

马赛克

正方形
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);
//floor向下取整
    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;
}
六边形

六边形马赛克原理:将一张图片,分割成由六边形组成,再取每个六边形的中心点画出一个个的矩形,根据矩形的奇偶排列情况求出对应的2个中心点,并计算纹理坐标与两个中心点的距离,根据距离判断,采取就近原则,当前的六边形就采用近的中心点的颜色值。

将图片分割成六边形,六边形中心点画出矩形后的呈现如下所示

image

滤镜算法主要实现步骤有:

  • 设置矩形的长宽比例值TR、TB(TB:TR 符合比例 3:√3)
    其中长宽比为3:√3,计算过程如下:

    image
  • 获取纹理坐标的x,y

  • 根据纹理坐标计算对应的矩形坐标wx、wy
    假设矩阵的比例为3*len:√3*len,那么纹理坐标(x,y)对应的矩阵坐标为

    image
  • 根据行列的奇偶情况,求对应的中心点纹理坐标v1、v2

    • 偶行偶列:(0,0)(1,1)/,即左上、右下

    • 偶行奇列:(0,1)(1,0)\,即左下、右上

    • 奇行偶列:(0,1)(1,0)\,即左下、右上

    • 奇行奇列:(0,0)(1,1)/,即左上、右下
      最终汇总起来也只有2种情况,(0,0)(1,1) 和 (0,1)(1,0),如下图所示

      image

其中单个矩阵中,4个点的坐标计算公式如下:

  • 对于计算中的wx+1,拿(1,0)点来说,wx+1等同于(1,0)与(0,0)之间相差一个矩形的长,这个长度为1,为了得到(1,0)点的坐标,要在(0,0)点坐标的基础上,将wx增加一个长
  • 对于计算中的wy+1,拿(0,1)点来说,wy+1等同于(0,0)与(0,1)之间相差一个矩形的高,这个长度为1,为了得到(0,1)点的坐标,要在(0,0)点坐标的基础上,将wy增加一个高
image
  • 根据距离公式求像素点距离两个中心点的距离s1、s2

    • s1 = √((v1.x-x)² + (v1.y-y)²)

    • s2 = √((v2.x-x)² + (v2.y-y)²)
      如图所示,

      image
  • 根据求出的距离,判断离哪个中心点近,就取哪个六边形的中心点颜色值为六边形的颜色值

片元着色器代码

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;
}

三角形马赛克

原理:三角形马赛克是由六边形马赛克演变而来,得到三角形的前提,就是的先有六边形,然后将正六边形6等分,每个三角形都是正三角形,然后求出纹理坐标与中心点的夹角,同时求出三角形的中心点,根据夹角判断,夹角属于哪个三角形,就将该三角形的中心点颜色作为整个三角形的纹素

三角形滤镜算法步骤是在六边形滤镜算法的步骤上增加以下步骤:

  • 求出当前像素点与纹理中心点的夹角
    如下图所示,纹理坐标为(x,y),中心点为vn,求夹角

    image
  • 计算6个三角形的中心点

    image
  • 判断夹角属于哪个三角形,则获取哪个三角形的中心点坐标
    其中,不同三角形的夹角范围如图所示

    image

片元着色器代码:在六边形滤镜算法(即 vn = (s1 < s2) ? v1 : v2;)后增加如下代码

    //获取像素点与中心点的角度
   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;

atan是GLSL中的内建函数,有两种计算方式
1、atan(y,x) 值域是[0,π],
2、atan(y/x),值域是[-π/2, π/2]

放大

*********vsh**********
attribute vec4 Position;
attribute vec2 TextureCoords;
varying vec2 TextureCoordsVarying;
//时间戳(随着定时器的方法调用及时更新):从0开始一直递增
uniform float Time;
const float PI = 3.1415926;

void main(){
    //一次缩放效果的时长
    float duration = 0.6;
    //最大缩放幅度
    float maxAmplitude = 0.3;
    
    //表示传入的事件周期,即time的范围被控制在0.0~0.6
    //mod(a, b),求模运算 等价于 a%b,GLSL中不支持%求模
    float time = mod(Time,duration);
    
    //amplitude表示振幅,引入PI的目的是为了使用sin函数,将amplitude的范围控制在1.0 ~ 1.3之间,并随着时间变化
    //这里可以不用取绝对值,因为角度的范围是【0,π】,不会出现负数的情况
    float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI / duration)));
    
    //放大关键代码:将顶点坐标的x和y分别乘以一个放大系数,即振幅,在纹理坐标不变的情况下,就达到了拉伸的效果
    //xy放大,zw保持不变
    gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);
    
    //纹理坐标传递给TextureCoordsVarying
    TextureCoordsVarying = TextureCoords;
}

主要是顶点的数据处理,随着时间,重复放大缩小

灵魂出窍

原理:通过在原图上叠加一个图层,将两个图层进行颜色混合实现,且上面的叠加的层随着时间推移逐渐放大且透明度降低至0,以此循环往复。需要在片元着色器中进行滤镜算法

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//时间戳
uniform float Time;

void main (void) {
    //一次灵魂出窍效果的时长
    float duration = 0.7;
    //透明度上限值
    float maxAlpha = 0.4;
    //图片放大的上限
    float maxScale = 1.8;
    
    //当前进度(时间戳与时长使用mod取模),再除以时长 得到【0, 1】,即百分比
    float progress = mod(Time, duration) / duration; // 0~1
    //当前透明度 【0.4, 0】
    float alpha = maxAlpha * (1.0 - progress);
    //当前缩放比例 【1.0, 1.8】
    float scale = 1.0 + (maxScale - 1.0) * progress;
    
    //获取放大后的纹理坐标
    //将顶点坐标对应的纹理坐标的x/y值到中心点的距离,缩小一定的比例,仅仅只是改变了纹理坐标,而保持顶点坐标不变,从而达到拉伸效果
    float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale;
    float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale;
    vec2 weakTextureCoords = vec2(weakX, weakY);
    
  //获取当前像素点纹理坐标,放大后的纹理坐标  
    vec4 weakMask = texture2D(Texture, weakTextureCoords);
    
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    //2、颜色混合 内建函数mix / 混合方程式
    gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;
}

抖音抖动滤镜

原理:颜色偏移 + 微弱的放大效果,即先将纹理放大,然后将放大后的纹理坐标的纹素进行颜色偏移

抖动滤镜的算法步骤:

  • 根据当前时间戳计算当前进度的百分比
  • 计算当前进度对应的颜色偏移
  • 计算当前进度对应的缩放比例
  • 获取放大后的纹理坐标
  • 将放大后的纹理纹素进行颜色偏移,获得3组颜色
  • 从3组颜色中分别获取RGBA的值

其中获取时间戳、计算当前进度值的图示与灵魂出窍中一致,下图是颜色偏移&缩放比例的计算,以及在时间戳递增过程中,抖动滤镜的变化过程

片元着色器

void main(){
    //一次抖动效果的时长
    float duration = 0.7;
    //放大图片的上限
    float maxScale = 1.1;
    //颜色偏移的步长
    float offset = 0.02;

    //进度 0 ~ 1
    float progress = mod(Time, duration) / duration;
    //颜色偏移值0 ~ 0.02
    vec2 offsetCoords = vec2(offset, offset) * progress;
    //缩放比例 1.0 ~ 1.1
    float scale = 1.0 + (maxScale - 1.0) * progress;

    //放大后的纹理坐标 
    //下面这种向量相加减的方式 等价于 灵魂出窍滤镜中的单个计算x、y坐标再组合的为纹理坐标的方式
    vec2 ScaleTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;

    //获取三组颜色:颜色偏移计算可以随意,只要偏移量很小即可
    //原始颜色 + offset
    vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);
    //原始颜色 - offset
    vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);
    //原始颜色
    vec4 mask = texture2D(Texture, ScaleTextureCoords);

    //从3组颜色中分别取出 红色R,绿色G,蓝色B,透明度A填充到内置变量gl_FragColor内
    gl_FragColor = vec4(maskR.r, maskB.g, mask.b, mask.a);
}

毛刺滤镜

原理:撕裂+微弱的颜色偏移,即设定一个阈值,当像素点的偏移值小于阈值时,才进行偏移,反之,则乘以一个缩小系数,最终的呈现效果就是 绝大部分都会进行微小的偏移,只有少量的行会进行较大偏移

毛刺滤镜算法:

  • mod函数计算时间周期
  • 计算振幅,范围是「0, 1]
  • 获取像素点随机偏移值,范围是[-1,1]
  • 判断是否需要偏移 & 计算纹理的x坐标
    • 需要偏移,撕裂较大,即x的颜色偏移较大
    • 不需要,撕裂较小,即x的颜色偏移值很微小
  • 获取撕裂后的纹理坐标
  • 计算撕裂后的3组纹素,并获取不同组中的RGBA值

片元着色器

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//时间戳
uniform float Time;
//PI常量
const float PI = 3.1415926;
//随机数
float rand(float n){
    //fract(x)返回x的小数部分
    //返回 sin(n) * 43758.5453123
    //sin(n) * 极大值,带小数点,想要随机数算的比较低,乘的数就必须较大,噪声随机
    //如果想得到【0,1】范围的小数值,可以将sin * 1
    //如果只保留小数部分,乘以一个极大值
    return fract(sin(n) * 43758.5453123);
}

void main(){
    //最大抖动上限
    float maxJitter = 0.06;
    //一次毛刺效果的时长
    float duration = 0.3;
    //红色颜色偏移
    float colorROffset = 0.01;
    //绿色颜色偏移
    float colorBOffset = -0.025;

    //表示将传入的事件转换到一个周期内,范围是 0 ~ 0.6,抖动时长变成0.6
    float time = mod(Time, duration * 2.0);
    //振幅,随着时间变化,范围是[0, 1]                                                                             
    float amplitude = max(sin(time * (PI / duration)), 0.0);

    //像素随机偏移范围 -1 ~ 1,* 2.0 - 1.0是为了得到【-1,1】范围内的随机值
    float jitter = rand(TextureCoordsVarying.y) * 2.0 - 1.0;
    //判断是否需要偏移,如果jitter范围 < 最大范围*振幅
    // abs(jitter) 范围【0,1】
    // maxJitter * amplitude 范围【0, 0.06】
    bool needOffset = abs(jitter) < maxJitter * amplitude;

    //获取纹理x坐标,根据needOffset来计算它的x撕裂
    //needOffset = YES,则撕裂大
    //needOffset = NO,则撕裂小,需要降低撕裂 = *振幅*非常细微的数
    float textureX = TextureCoordsVarying.x + (needOffset ? jitter : (jitter * amplitude * 0.006));
    //获取纹理撕裂后的x、y坐标
    vec2 textureCoords = vec2(textureX, TextureCoordsVarying.y);

    //颜色偏移:获取3组颜色
    //撕裂后的原图颜色
    vec4 mask = texture2D(Texture, textureCoords);
    //根据撕裂计算后的纹理坐标,获取纹素
    vec4 maskR = texture2D(Texture, textureCoords + vec2(colorROffset * amplitude, 0.0));
    //根据撕裂计算后的纹理坐标,获取纹素
    vec4 maskB = texture2D(Texture, textureCoords + vec2(colorBOffset * amplitude, 0.0));

    //颜色主要撕裂,红色和蓝色部分,所以只调整红色
    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}

GLSL中没有随机值的函数,需要通过自定义函数,利用fract函数获得随机数

幻觉滤镜

原理:残影+颜色偏移的叠加

  • 残影:是每隔一段时间,就会新建一个图层,且该图层以红色为主,随着时间推移透明度逐渐降低,于是可以在一个周期时长内看到很多不同透明度的层叠加在一起,从而形成残影,让图片随着时间做圆周运动
  • 颜色偏移:图片在移动的过程中是蓝色在前,红色在后,即在移动的过程中,每间隔一段时间,遗失了一部分红色通道的值在原来的位置,并且这部分红色通道的值,随着时间偏移,会逐渐恢复。

幻觉滤镜算法:

  • 通过mod函数计算当前时间周期
  • 设置放大倍数
  • 计算放大后的纹理坐标
  • 获取转全过程中像素点的纹素
  • 通过for循环来新建图层,即幻影颜色
  • 获取由原始图层和新建层叠加的颜色

片元着色器

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

uniform float Time;

const float PI = 3.1415926;
//一次幻觉效果的时长,即周期
const float duration = 2.0;

//这个函数可以计算出,在某个时刻图片的具体位置,通过它可以每经过一段时间,去生成一个新的mask
//转圈产生幻影的单个像素点的颜色值
vec4 getMask(float time, vec2 textureCoords, float padding) {
   //圆心坐标
    vec2 translation = vec2(sin(time * (PI * 2.0 / duration)),cos(time * (PI * 2.0 / duration)));

    //新的纹理坐标 = 原始纹理坐标 + 偏移量 * 圆周坐标(新的图层与图层之间是有间距的,所以需要偏移)
    vec2 translationTextureCoords = textureCoords + padding * translation;

    //根据新的纹理坐标获取新图层的纹素
    vec4 mask = texture2D(Texture, translationTextureCoords);

    return mask;
}

//这个函数可以计算出,某个时刻创建的层,在当前时刻的透明度
//进度:
float maskAlphaProgress(float currentTime, float hideTime, float startTime) {
//mod(时长+持续时间 - 开始时间,时长)得到一个周期内的time
    float time = mod(duration + currentTime - startTime, duration);
    //如果小于0.9,返回time,反之,返回0.9
    return min(time, hideTime);
}

void main(){
    //将传入的时间戳转换到一个周期内,time的范围是【0,2】
    //获得时间周期
    float time = mod(Time, duration);
    //放大后的倍数
    float scale = 1.2;
    //偏移量 = 0.083
    float padding = 0.5 * (1.0 - 1.0 / scale);
    //放大后的纹理坐标
    vec2 textureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;

    //新建层的隐藏时间 即新建层什么时候隐藏 
    float hideTime = 0.9;
    //时间间隔:隔0.2s创建一个新层
    float timeGap = 0.2;

    //注意:只保留了红色的透明的通道值,因为幻觉效果残留红色
    //幻影残留数据
    //max RGB alpha
    //新图层的 R透明度
    float maxAlphaR = 0.5;
    //新图层的 G透明度
    float maxAlphaG = 0.05;
    //新图层的 B透明度
    float maxAlphaB = 0.05;

    //获取新的图层的坐标,需要传入时间、纹理坐标、偏移量
    vec4 mask = getMask(time, textureCoords, padding);
    //RGB :for循环中使用
    float alphaR = 1.0;
    float alphaG = 1.0;
    float alphaB = 1.0;

    //最终图层颜色:初始化
    vec4 resultMask = vec4(0, 0, 0, 0);

    //循环:每一层循环都会得到新的图层的颜色,即幻影颜色
    //一次循环只是计算一个像素点的纹素,需要在真机运行。模拟器会卡,主要是模拟器上是CPU模拟GPU的
    for (float f = 0.0; f < duration; f += timeGap) {
        float tmpTime = f;
        //获取到【0,2】s内所获取的运动后的纹理坐标
        //获得幻影当前时间的颜色值
        vec4 tmpMask = getMask(tmpTime, textureCoords, padding);

        //某个时刻创建的层,在当前时刻的红绿蓝的透明度
        //临时的透明度 = 根据时间推移RGB的透明度发生变化
        //获得临时的红绿蓝透明度
        float tmpAlphaR = maxAlphaR - maxAlphaR * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaG = maxAlphaG - maxAlphaG * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaB = maxAlphaB - maxAlphaB * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;

        //累计每一层临时RGB * RGB的临时透明度
        //结果 += 临时颜色 * 透明度,即刚产生的图层的颜色
        resultMask += vec4(tmpMask.r * tmpAlphaR,tmpMask.g * tmpAlphaG,tmpMask.b * tmpAlphaB,1.0);

        //透明度递减
        alphaR -= tmpAlphaR;
        alphaG -= tmpAlphaG;
        alphaB -= tmpAlphaB;
    }

    //最终颜色 += 原始纹理的RGB * 透明度
    resultMask += vec4(mask.r * alphaR, mask.g * alphaG, mask.b * alphaB, 1.0);

    //将最终颜色填充到像素点里
    gl_FragColor = resultMask;
}

代码中的getMask函数中,由于sin和cos的都是取值范围是[-1, 1],其中对纹理坐标的偏移,可以理解为某一个像素点围绕原纹理坐标在做圆周运动,即效果图中的转圈

相关文章

  • 五、滤镜

    九分屏 在x,y三等分时直接取全图值 灰度 1.浮点算法:Gray=R0.3+G0.59+B0.112.整数⽅法:...

  • 移动端滤镜开发(五)普通滤镜开发

    写在前面的话 上一篇文章对简单滤镜实现有一定的讲解,那么这一篇则是对图像处理更加深层次的说明,对于一张图片怎么处理...

  • GPUImage学习

    前言 一些自带滤镜 支持滤镜摄像,滤镜相机,滤镜图片,滤镜视频 一.使用滤镜生成UIImage对象 创建一个滤镜类...

  • 最全GPUImage 滤镜详解,附Demo以及效果图(五)

    (五)其他 除了以上四种类别的滤镜之外,Demo中还演示了以下几个功能: 用户自定义滤镜 GPUImage 支持用...

  • ——滤镜滤镜加滤镜——

    每次发图片都要加滤镜,和同桌曾经很认真的争论过,加滤镜算不算p图,她说一张图加了滤镜就像变了另一张图。她喜欢日漫喜...

  • 图录游记

    图/三线五花肉 无滤镜效果 实拍 01

  • 2019-08-27

    滤镜分类 根据滤镜的功能对滤镜进行了如下分类,方便大家查阅—— 柔化系列效果滤镜 光学系列滤镜 颜色系列滤镜 灰...

  • 照片处理-滤镜分类

    滤镜最开始指的是相机镜头上的光学滤镜,即硬件滤镜,IT技术的发展,现在的滤镜主要指软件滤镜,通过算法对硬件滤镜进行...

  • 利用OpenGL ES实现灰度滤镜及马赛克滤镜

    实现效果 灰度滤镜 实现灰度滤镜的方式有很多,但原理都大同小异,提高绿色的权重,或者只保留绿色值。大致分为以下五种...

  • OpenGL ES之滤镜处理(2)_灰度滤镜与马赛克滤镜

    上文 OpenGL ES之滤镜处理(1)_分屏滤镜 - 简书讲述了分屏滤镜的处理,本文介绍灰度滤镜与马赛克滤镜的处...

网友评论

      本文标题:五、滤镜

      本文链接:https://www.haomeiwen.com/subject/hryptltx.html