OpenGL ES 分屏滤镜
想要绘制滤镜首先我们需要清楚如何绘制纹理,如果不了解的可以参考 OpenGL ES 纹理绘制。
这篇文章中绘制的图片是倒立的,我们需要将图片反转过来。方法很多,这里就简单介绍一种常用的方法,反转坐标系:
CGContextRef contextRef = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorRGBSpaceRef, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGRect rect = CGRectMake(0, 0, width, height);
CGContextTranslateCTM(contextRef, rect.origin.x, rect.origin.y);
CGContextTranslateCTM(contextRef, 0, rect.size.height);
CGContextScaleCTM(contextRef, 1.0, -1.0);
CGContextTranslateCTM(contextRef, -rect.origin.x, -rect.origin.y);
0、先将图片contextRef
先移动到它的位置坐标rect.origin.x
、rect.origin.y
1、将图片contextRef
沿Y
轴方向移动图片的高度的距离height
。
2、沿Y
轴缩放-1.0
,这时图片已经翻转过来了,只是位置不对。
3、将图片contextRef
恢复到原来的位置坐标rect.origin.x
、rect.origin.y
下面我们开始讲解下滤镜的处理。
一、分屏滤镜
我们这里做了一分屏、二分屏、三分屏、四分屏、六分屏、九分屏的效果,当然一分屏其实就是正常的绘制。
这里分屏效果的代码全部都是在片元着色程序.fsh
中修改的,只需要处理纹理坐标即可。
- 一分屏(正常绘制)
.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
void main()
{
vec4 mask = texture2D(texture, textureCoords);
gl_FragColor = vec4(mask.rgb, 1.0);
}
正常效果:
- 二分屏
.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
void main()
{
vec2 uv = textureCoords.xy;
if (uv.y <= 0.5 && uv.y >= 0.0) {
uv.y += 0.25;
} else {
uv.y -= 0.25;
}
gl_FragColor = texture2D(texture, uv);
}
这里将纹理坐标在Y
方向上分成了两个区域:[0.0, 0.5]
和 [0.5, 1.0]
。
这里我们要求尽可能取图片中间位置的纹理。所以为了将屏幕铺满,就需要取在Y
方向上[0.25, 0.75]
之间的位置。
所以,在[0.0, 0.5]
区域时,Y
方向上需要加上0.25
。在[0.5, 1.0]
区域时,Y
方向上需要减去0.25
。
二分屏效果:
- 三分屏
.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
void main()
{
vec2 uv = textureCoords.xy;
float detal = 1.0/3.0;
if (uv.y <= detal && uv.y >= 0.0) {
uv.y += detal;
} else if (uv.y > detal * 2.0) {
uv.y -= detal;
}
gl_FragColor = texture2D(texture, uv);
}
三分屏将纹理坐标在Y
方向上分成了三个区域:[0.0, 1.0/3.0]
、 [1.0/3.0, 2.0/3.0]
、[2.0/3.0, 1.0]
。
这里同样要求尽可能取图片中间位置的纹理。所以为了将屏幕铺满,就需要取在Y
方向上[1.0/3.0, 2.0/3.0]
之间的位置。
所以,在[0.0, 1.0/3.0]
区域时,Y
方向上需要加上1.0/3.0
。在[1.0/3.0, 2.0/3.0]
正好对应上中间的纹理,所以不用修改。在[2.0/3.0, 1.0]
区域时,Y
方向上需要减去1.0/3.0
。
- 四分屏
.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
void main()
{
vec2 uv = textureCoords.xy;
float detal = 0.5;
if (uv.x <= detal && uv.x >= 0.0) {
uv.x *= 2.0;
} else if (uv.x > detal) {
uv.x = (uv.x - detal) * 2.0;
}
if (uv.y <= detal && uv.y >= 0.0) {
uv.y *= 2.0;
} else if (uv.y > detal) {
uv.y = (uv.y - detal) * 2.0;
}
gl_FragColor = texture2D(texture, uv);
}
四分屏需要将纹理坐标分成四个区域:
Y方向[0.0, 0.5]
和 [0.5, 1.0]
。
X方向[0.0, 0.5]
和 [0.5, 1.0]
。
两两结合构成四个区域。
这里要求每个区域都展示完整的图片。
所以无论X、Y
,范围在[0.0, 0.5]
时坐标都需要乘以2.0
范围在[0.5, 1.0]
时,坐标都需要减去先0.5
再乘以2.0
四分屏效果:
- 六分屏
.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
void main()
{
vec2 uv = textureCoords.xy;
float detal = 1.0/3.0;
if (uv.x <= detal && uv.x >= 0.0) {
uv.x += detal;
} else if (uv.x >= detal * 2.0) {
uv.x -= detal;
}
if (uv.y <= 0.5 && uv.y >= 0.0) {
uv.y += 0.25;
} else {
uv.y -= 0.25;
}
gl_FragColor = texture2D(texture, uv);
}
六分屏将纹理坐标分成六个区域:
X方向[0.0, 1.0/3.0]
、 [1.0/3.0, 2.0/3.0]
、 [2.0/3.0, 1.0]
。
Y方向[0.0, 0.5]
和 [0.5, 1.0]
。
两两结合构成六个区域。
这里同样要求尽可能取图片中间位置的纹理。所以为了将屏幕铺满,就需要取在X
方向上[1.0/3.0, 2.0/3.0]
之间的位置。在Y
方向上取[0.25, 0.75]
之间的位置。
所以
X
方向上:在[0.0, 1.0/3.0]
区域时,X
方向上需要加上1.0/3.0
。在[1.0/3.0, 2.0/3.0]
正好对应上中间的纹理,所以不用修改。在[2.0/3.0, 1.0]
区域时,X
方向上需要减去1.0/3.0
。
Y
方向上:在[0.0, 0.5]
区域时,Y
方向上需要加上0.25
。在[0.5, 1.0]
区域时,Y
方向上需要减去0.25
。
- 九分屏
.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
void main()
{
vec2 uv = textureCoords.xy;
float detal = 1.0/3.0;
if (uv.x <= detal && uv.x >= 0.0) {
uv.x *= 3.0;
} else if (uv.x > detal && uv.x < detal * 2.0) {
uv.x = (uv.x - detal) * 3.0;
} else {
uv.x = (uv.x - detal * 2.0) * 3.0;
}
if (uv.y <= detal && uv.y >= 0.0) {
uv.y *= 3.0;
} else if (uv.y > detal && uv.y < detal * 2.0) {
uv.y = (uv.y - detal) * 3.0;
} else {
uv.y = (uv.y - detal * 2.0) * 3.0;
}
gl_FragColor = texture2D(texture, uv);
}
九分屏需要将纹理坐标分成九个区域:
Y方向[0.0, 1.0/3.0]
、 [1.0/3.0, 2.0/3.0]
、 [2.0/3.0, 1.0]
。
X方向[0.0, 1.0/3.0]
、 [1.0/3.0, 2.0/3.0]
、 [2.0/3.0, 1.0]
。
两两结合构成九个区域。
这里要求每个区域都展示完整的图片。
所以无论X、Y
:
范围在[0.0, 1.0/3.0]
时坐标需要乘以3.0
。
范围在[1.0/3.0, 2.0/3.0]
时,坐标需要先减去1.0/3.0
再乘以3.0
。
范围在[2.0/3.0, 1.0]
时,坐标需要先减去2.0/3.0
再乘以3.0
。