一.概述
今天继续OpenGLES的学习
今天在之前博文
《OpenGLES:GLSurfaceView实现Android Camera预览》
的基础上,使用OpenGLES实现相机
四宫格滤镜
和
九宫格滤镜
。
二.四宫格
先定义几个名词:
- 之前博文中实现的相机普通预览叫:
“大宫格”
- 现在要实现的是:”
四宫格”+”滤镜”
- 四宫格的每一个小宫格叫:
“小宫格”
- 小宫格区域内的点记为:
(min_x, min_y)
- 小宫格原点记为:
(min_origin_x, min_origin_y)
- 大宫格区域内的点记为:
(max_x, max_y)
大宫格原点是(0,0)
,不用专门标记
先看怎么实现
四宫格
,再看怎样
+滤镜
小宫格
(min_x, min_y)是从属于
大宫格
(max_x, max_y)的,它只是(max_x, max_y)的一个
1/4子区域
我们要做的就是怎么把(min_x, min_y)通过换算对应到(max_x, max_y)
通过图示不难看出:
1.
大宫格
相当于
小宫格
沿x、y轴反方向移动(min_origin_x, min_origin_y)到(0,,0),再将长、宽乘以2
也可以反向认为:
2.
小宫格
相当于
大宫格
的长、宽都除以2,再沿x、y轴正向移动到(min_origin_x,min_origin_y)
以
“右上角小宫格”
为
例:
1.
大宫格
相当于
右上角小宫格
沿x,y轴反方向移动(0.5,0.5)到(0,0),再将长、宽都放大2倍
或者
2.
右上角小宫格
相当于
大宫格
长、宽都缩小1/2,再沿x、y轴正方向移动到(0.5,0.5)
我们要做的是怎么把纹理采样坐标中的
部分坐标
(
小宫格
)对应到
全部坐标
(
大宫格
)
所以我们要找到的是上述
“1.”
的对应关系,反向的
“2.”
是为了更形象的理解过程
按照这种方法,不管多复杂的宫格分割,只要推导出
小宫格
通过怎样的
平移和缩放
能够与
“大宫格”
重叠,就能找到对应关系
所以
“小宫格”
换算对应到
“大宫格”
的公式:
max_x = (min_x – min_origin_x) *2
max_y = (min_y – min_origin_y) *2
(min_origin_x, min_origin_y)可以再进一步通过要实现的
“几宫格”
的
“几”
进行公式化,这个不难,就不推导了。
三.滤镜
在片段着色器中,通过
纹理采样器
和
纹理坐标
创建出
纹理
创建的纹理其实就是要赋值给内建变量的
颜色值向量
这也是为什么要通过
纹理采样器
和
纹理坐标
,相当于纹理采样器根据纹理坐标获取这个坐标点的颜色值,所有颜色值形成一张五彩缤纷的图,就是
纹理
。
通过颜色向量可以拿到色值的
RGB分量
那么就可以对颜色做出各种
“滤镜”
效果了
如下是
黑白
滤镜代码:
//黑白
void blackAndWhite(inout vec4 color){
float threshold = 0.5;
float mean = (color.r + color.g + color.b) / 3.0;
color.r = color.g = color.b = mean >= threshold ? 1.0 : 0.0;
}
直接通过代码字面就能理解了:
纹理坐标点采样到的颜色值的(r、g、b)三个分量,如果平均值>0.5,就赋值为白,反之为黑。
如下就是最终实现四宫格的
黑白、灰度、反向、原图
实时滤镜着色器代码:
看字面都很好理解,不一一说明了
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
in vec2 texCoord;//纹理坐标,图片当中的坐标点
out vec4 outColor;
uniform samplerExternalOES s_texture;//图片,采样器
//黑白
void blackAndWhite(inout vec4 color){
float threshold = 0.5;
float mean = (color.r + color.g + color.b) / 3.0;
color.r = color.g = color.b = mean >= threshold ? 1.0 : 0.0;
}
//灰度
void grey(inout vec4 color){
float weightMean = color.r * 0.3 + color.g * 0.59 + color.b * 0.11;
color.r = color.g = color.b = weightMean;
}
//反向
void reverse(inout vec4 color){
color.r = 1.0 - color.r;
color.g = 1.0 - color.g;
color.b = 1.0 - color.b;
}
void main(){
float x = texCoord.x;
float y = texCoord.y;
//四宫格滤镜
if (x <= 0.5 && y <= 0.5){
x = x * 2.0;
y = y * 2.0;
outColor = texture(s_texture, vec2(x, y));
reverse(outColor);
} else if (x <= 0.5 && y >= 0.5){
x = x * 2.0;
y = (y-0.5) * 2.0;
outColor = texture(s_texture, vec2(x, y));
blackAndWhite(outColor);
} else if (x>0.5 && y > 0.5){
x = (x-0.5) * 2.0;
y = (y-0.5) * 2.0;
outColor = texture(s_texture, vec2(x, y));
grey(outColor);
} else if (x>0.5 && y < 0.5){
x = (x-0.5) * 2.0;
y = y * 2.0;
outColor = texture(s_texture, vec2(x, y));
}
}
实现效果:
四.九宫格滤镜
如果你跟着博文一直学习到这里,并且自己动手实现了四宫格滤镜,那么九宫格滤镜也只是顺理成章的事了。
九宫格示例代码中,我没有逐个宫格计算实现,而是通过x,y的等分范围来实现
所以也没有逐个添加滤镜效果,
重点在于
九宫格的实现
前文中也提到过,只要找到了
小宫格
和
大宫格
的对应关系,也完全可以根据
“几宫格”
的
“几”
,将着色器代码进一步精简,用循环语句来划分宫格。
理解透了原理就万变不离其宗了,代码实现只是形式
如下是九宫格着色器代码:
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
in vec2 texCoord;//纹理坐标,图片当中的坐标点
out vec4 outColor;
uniform samplerExternalOES s_texture;//图片,采样器
void main(){
float x = texCoord.x;
float y = texCoord.y;
//九宫格滤镜
float x = texCoord.x;
float y = texCoord.y;
if (x < 1.0 / 3.0) {
x = x * 3.0;
} else if (x < 2.0 / 3.0) {
x = (x - 1.0 / 3.0) * 3.0;
} else {
x = (x - 2.0 / 3.0) * 3.0;
}
if (y <= 1.0 / 3.0) {
y = y * 3.0;
} else if (y < 2.0 / 3.0) {
y = (y - 1.0 / 3.0) * 3.0;
} else {
y = (y - 2.0 / 3.0) * 3.0;
}
outColor = texture(s_texture, vec2(x, y));
}
实现效果:
五.结束