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 }