OpenGL 12.1 - 案例:灰度马赛克滤镜

图片显示到屏幕上由CPU/GPU 协作完成:

CPU:计算视图 frame 、图片解码,需要绘制的图片通过数据总线交给 GPU。

GPU:纹理混合、顶点变换与计算、像素点的填充计算、渲染到帧缓冲区。

iOS双缓冲机制:2个帧缓冲区

纹理图片滤镜处理,通过修改片元着色器实现滤镜效果。

效果:

一、灰度

原理:对纹理RGB值权重修改并保持平衡,或只取绿色G值 --> 人眼对绿色是最敏感的,绿色值越大,眼睛看到图片越暗。

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

片元着色器代码:

 1 precision highp float;
 2 uniform sampler2D Texture;
 3 varying vec2 TextureCoordsVarying;
 4 const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);// 权重值取自GPUImage,RGB三值相加=1
 5 
 6 void main (void) {
 7     // 获取纹理图片当前纹理坐标下的 颜色值
 8     vec4 mask = texture2D(Texture, TextureCoordsVarying);
 9     float luminance = dot(mask.rgb, W);// 点乘 颜色 点乘 变换因子
10     gl_FragColor = vec4(vec3(luminance), 1.0);// 将灰度值转化并填充到像素中(luminance,luminance,luminance,mask.a) 
11    // gl_FragColor = vec4(vec3(mask.g), 1.0);// 只取绿色
12 }

翻转:

OpenGL 十 - 002、GLSL案例-纹理图片绘制与翻转 中 第三节 图片倒立翻转问题。

二、方形马赛克

马赛克原理:⻢赛克效果就是把图片中的⼀个相对⼤小的区域(设置每个马赛克的大小范围)取⽤同⼀个点的颜⾊来表示。可以认为是⼤规模的降低图像的分辨率,⽽让图像的⼀些细节隐藏起来。

每个小方块范围内取同一个色值,计算每个马赛克块的所在位置对应所取的色值 —>

1.对纹理图片进行放大(400,400),纹理坐标(400*x,400*y)

2.求出放大后纹理坐标下 对应的马赛克块(size:16,16)位置,向下取整 —> 400*x / 16 * 16 // 即:400*x  % 16

3.对放大坐标进行复原,相应缩小,取出位置对应颜色

—> 可理解为:纹理图片被分割成了N个小方块,每个小方块范围内取同一个色值填充,我们计算出方块应取得色值是什么即可。纹理大小除以方块大小得到个数,第n个方块取得色值都是(n,n)位置的色值。

片元着色器代码:

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

三、正六边形马赛克

1、目标:将 纹理图片 处理成 由 N 个六边形的图形马赛克 绘制而成。

2、方式:纹理像素取值:每个六边形范围内,取六边形的中心位置的纹理坐标值。

3、算法逻辑:每个六边形周边会有4个中心位置组成一个矩形。

当纹理坐标传入时,根据纹理坐标计算出六边形中心点位置 --> 矩形顶点位置 --> 上图中黄绿原点。

算法:

 

1)给定六边形边长 length,矩形的长宽比 = 3*length : √3*length 

2)上图 紫色位置处纹理坐标w(x,y),它所对应的矩形的位置 --> ( int(x / (3*length)),  int (y / (√3*length))

3)计算中心点坐标

3.1、六边形所在矩形分割成4个小矩形。

a、当 w 位置是 偶行偶列 时,如下图 --> 计算 1、5 两个中心点:

1:(3/2 * length * wx, √3/2 * length * wy)

5:(3/2 * length * (wx + 1), √3/2 * length * (wy + 1)) 

为何中心点5的 x、y 坐标值 +1 --> w 的位置对应计算出的值恰好是 1 的值,而 5 与 1 的跨度是一个小矩形的大小,所以坐标值需加 相应的宽高(矩形长宽)值。

 b、当 w 位置是 偶行奇列 时,如下图 --> 计算 2、5 两个中心点 -->

5:(3/2 * length * wx, √3/2 * length * (wy + 1))

2:(3/2 * length * (wx + 1), √3/2 * length * wy)

--> w 计算出的值是左上角 --> 中心点 2 的 x 位置多了一个宽度,中心点 5 的 y 多了一个高度。

 

c、当中心点位置是 奇行偶列 时,如下图 --> 4、5 两个中心点 -->

4:(3/2 * length * wx, √3/2 * length * (wy + 1))

5:(3/2 * length * (wx + 1), √3/2 * length * wy)

 

d、当 w 位置是 奇行奇列 时,如下图 --> 计算 3、5 两个中心点:

5:(3/2 * length * wx, √3/2 * length * wy)

3:(3/2 * length * (wx + 1), √3/2 * length * (wy + 1)) 

 4)计算出当前纹理坐标 w(x, y) 位置距两个中心点 v1、v2 位置的远近

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

比较大小,取距离近的,即取小值。

4、片元着色器代码

 1 precision highp float;
 2 uniform sampler2D Texture;
 3 varying vec2 TextureCoordsVarying;
 4 
 5 const float mosaicSize = 0.03;
 6 
 7 void main (void) {
 8     
 9     float length = mosaicSize;
10     
11     float TR = 0.866025;// √3/2
12     float TB = 1.5;// 3/2
13     
14     float x = TextureCoordsVarying.x;
15     float y = TextureCoordsVarying.y;
16     
17     int wx = int(x / (TB*length));
18     int wy = int(y / (TR*length));
19     vec2 v1, v2, vn;
20     
21     if (wx/2 * 2 == wx) {// wx % 2 == 0
22         if (wy/2 * 2 == wy) {
23             // (0,0),(1,1)
24             v1 = vec2(length * TB * float(wx), length * TR * float(wy));
25             v2 = vec2(length * TB * float(wx + 1), length * TR * float(wy + 1));
26         } else {
27             // (0,1),(1,0)
28             v1 = vec2(length * TB * float(wx), length * TR * float(wy + 1));
29             v2 = vec2(length * TB * float(wx + 1), length * TR * float(wy));
30         }
31     }else {
32         if (wy/2 * 2 == wy) {
33             // (0,1),(1,0)
34             v1 = vec2(length * TB * float(wx), length * TR * float(wy + 1));
35             v2 = vec2(length * TB * float(wx + 1), length * TR * float(wy));
36         } else {
37             // (0,0),(1,1)
38             v1 = vec2(length * TB * float(wx), length * TR * float(wy));
39             v2 = vec2(length * TB * float(wx + 1), length * TR * float(wy + 1));
40         }
41     }
42     
43     float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));
44     float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));
45     if (s1 < s2) {
46         vn = v1;
47     } else {
48         vn = v2;
49     }
50     vec4 color = texture2D(Texture, vn);
51     
52     gl_FragColor = color;
53     
54 }

四、三角形马赛克

 三角形马赛克算法原理:

1、在正六边形算法的基础上,切割成三角形,计算每个三角形的弧度 --当前纹理坐标与正六边形中心点的夹角

2、对比夹角所在位置处在1~6哪个三角形中,取对应三角形的中心位置点的颜色

3、计算每个三角形的中心位置(此中心位置并非为准确的中心点,而是三角形中线的1/2,图中绿色线的1/2位置)

片元着色器代码

 1 precision highp float;
 2 uniform sampler2D Texture;
 3 varying vec2 TextureCoordsVarying;
 4 
 5 float mosaicSize = 0.03;
 6 
 7 void main (void) {
 8     
 9     const float TR = 0.866025;
10     const float PI6 = 0.523599;// π/6  30度
11     
12     float x = TextureCoordsVarying.x;
13     float y = TextureCoordsVarying.y;
14     
15  
16     int wx = int(x/(1.5 * mosaicSize));
17     int wy = int(y/(TR * mosaicSize));
18     
19     vec2 v1, v2, vn;
20     
21     if (wx / 2 * 2 == wx) {
22         if (wy/2 * 2 == wy) {
23             v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy));
24             v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * TR * float(wy + 1));
25         } else {
26             v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy + 1));
27             v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * TR * float(wy));
28         }
29     } else {
30         if (wy/2 * 2 == wy) {
31             v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy + 1));
32             v2 = vec2(mosaicSize * 1.5 * float(wx+1), mosaicSize * TR * float(wy));
33         } else {
34             v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy));
35             v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * TR * float(wy+1));
36         }
37     }
38 
39     float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));
40     float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));
41 
42     if (s1 < s2) {
43         vn = v1;
44     } else {
45         vn = v2;
46     }
47     
48     // 三角形处理
49     // 计算纹理坐标与六边形中心点夹角 - 
50     // atan 计算出的范围(-180~180),对应的值 -PI~PI
51     float a = atan((x - vn.x)/(y - vn.y));
52 
53     // 计算6个中心点位置
54     vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);
55     vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
56     vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
57     vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0);
58     vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
59     vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
60   
61     if (a >= PI6 && a < PI6 * 3.0) {// 30~90
62         vn = area1;
63     } else if (a >= PI6 * 3.0 && a < PI6 * 5.0) {// 90~150
64         vn = area2;
65     } else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0)|| (a<-PI6 * 5.0 && a>-PI6*6.0)) {// 150~180 -180~-150
66         vn = area3;
67     } else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0) {// -150~-90
68         vn = area4;
69     } else if (a <= -PI6 && a> -PI6 * 3.0) {// -90~-30
70         vn = area5;
71     } else if (a > -PI6 && a < PI6) {// -30~30
72         vn = area6;
73     }
74     
75     vec4 color = texture2D(Texture, vn);
76     gl_FragColor = color;
77 }

 

posted @ 2020-08-16 14:53  张张_z  阅读(731)  评论(0编辑  收藏  举报