通过前几篇的博客,咱们应该队 OpenGL 有了根底的意识,那么咱们就该来点实际了,它来了,它来了,真正的滤镜它来了
先放效果图
滤镜的学习是个循序渐进的过程,咱们本章先说一下简略的滤镜,也让读者揭开一角相机滤镜神秘面纱
OpenGL ES – 简略滤镜
通过前几篇的博客,咱们应该能实现如上图中的原图的滤镜了吧,我对后面的工程也做了一些代码重构,类图如下所示,残缺代码请查看文末链接。
咱们前面的滤镜代码将只关注于外围的 Shader Code 局部,因为 GL 环境局部咱们后面几篇博客曾经做了十分具体的解说了。如果有读者不太熟悉,请查看后面的博客。
默认滤镜
vertex shader
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main() {
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
}
fragment shader
precision mediump float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
};
默认的滤镜咱们后面也说过了,应用 texture2D 函数对纹理进行取样,既 OpenGL 展现图片
反相
对于图片反相的定义还是很容易了解的,咱们曾经晓得在 GL 中色彩是用 r,g,b,a 示意的,r,g,b,a 的范畴是 0.0f~1.0f,若染色 color = vec4(r,g,b,a), 则反相的色彩
invert_color = vec4((1.0-color.rgb), color.a);
所以反相的 fragment shader 自然而然就是上面了
fragment shader
precision mediump float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
// textureColor.w 其实就是 textureColor.a 透明度,这里不参加反相,因为咱们透明度原本是 1(齐全不通明),参加反相的话就是 0 了,齐全就是通明了,gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);
};
反相效果图如下
亮度(luminance)
PS
亮度(luminance)是示意人眼对发光体或被照耀物体外表的发光或反射光强度理论感触的物理量,亮度和光强这两个量在个别的日常用语中往往被混同应用。简而言之,当任两个物体外表在照相时被拍摄出的最终后果是一样亮、或被眼睛看起来两个外表一样亮,它们就是亮度雷同。
亮度 (明度) 反馈了色调的明暗水平,它和色相 (H)、饱和度(S) 独特形成 HSL 色调空间。调整亮度只须要 RGB 色调空间外面同时加上一个水平值。
fragment shader
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float brightness;
void main()
{lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);
};
这里咱们定义了一个 uniform lowp float brightness
,咱们通过界面 Seekbar 的拖动来动静批改这个值,还记得 GLSL 中 uniform 润饰的变量该如何赋值嘛,glUnifrom* 系列函数
效果图来了,
曝光度(Exposure)
PS
曝光度和亮度的原理基本上是统一的,亮度是全方位的线性减少色值,而曝光度是基于原色值的指数型叠加(红的会更红,绿的会更绿,蓝的会更蓝,白光的会更光)
fragment shader
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float exposure;
void main()
{lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);// rgb * 2^ 成果值
};
这里用到了 GLSL 的内置函数 pow 进行指数运算
对比度(Contrast)
接触了下面的 亮度 (luminance), 曝光度 (Exposure),咱们来看一个略微简单点的 对比度(Contrast)
PS
对比度是画面黑与白的比值,也就是从黑到白的突变档次。比值越大,从黑到白的突变档次就越多,从而色调体现越丰盛。对比度对视觉效果的影响十分要害,一般来说对比度越大,图像越清晰醒目,色调也越显明鲜艳;而对比度小,则会让整个画面都灰蒙蒙的。
简略的说,对比度是像素色彩和某个中值的差,它能够让亮堂的色彩更亮堂,让灰暗的色彩更灰暗。
这里实现个简略的线性对比度算法:
后果 = 中值差 * 对比度 + 中值
fragment shader
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float m_contrast;
void main()
{lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * m_contrast + vec3(0.5)), textureColor.w);
}
饱和度(Saturation)
PS
色调饱和度:饱和度是指色调的娇艳水平,也称色调的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例。含色成分越大,饱和度越大;消色成分越大,饱和度越小。纯的色彩都是高度饱和的,如鲜红,鲜绿。混淆上红色,灰色或其余色调的色彩,是不饱和的色彩,如绛紫,粉红,黄褐等,齐全不饱和的色彩基本没有色调,如黑白之间的各种灰色。
概念总结:饱和度 = X·原色 + Y·灰度值,其中(x+y=1)
其中应用 Luma 算法求算灰度:Gray = R0.2125 + G0.7154 + B*0.0721
fragment shader
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
uniform lowp float saturation;
void main()
{lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp float luminance = dot(textureColor.rgb, luminanceWeighting);
lowp vec3 greyScaleColor = vec3(luminance);
gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);
// GLSL 内置函数 mix(x,y,a) = x*(1-a)+y*a,刚好满足饱和度的公式定义。}
色相(色调)
咱们最初来解释调整图像色调,算是入门以上的图像处理手法。实践局部较多且深,要缓缓了解。先来理解什么是色调,如何定义色调。
PS
色调不是指色彩的性质,是对一幅绘画作品的整体评估。一幅绘画作品尽管可能用了多种色彩,但总体有一种色调,是偏蓝或偏红,是偏暖或偏冷等等。
色调模型
在了解色调之前,咱们先来解释一下色调模型,数字图像处理当中的 三大色调模型:RGB、HSI、CMYK(留神!这里不是格局,是色调模型)
(1)最罕用的 RGB 色调模型。
RGB 是根据人眼辨认的色彩定义出的空间,可示意大部分色彩。是图像处理中最根本、最罕用、面向硬件的色彩空间,是一种光混合的体系。
能够看到 RGB 色彩模式用三维空间中的一个点示意一种色彩,每个点有三个重量,别离示意红、绿、蓝的亮度值,亮度值限定为【0,1】。在 RGB 模型的立方体中,原点对应的色彩为彩色,它的三个重量值都为 0;间隔原点最远的顶点对应的色彩为红色,它的三个重量值都为 1。从彩色到红色的灰度值散布在这两个点的连线上,该虚线称为灰度线;立方体的其余各点对应不同的色彩,即三原色红、绿、蓝及其混合色黄、品红、青色。
(2)HSI 色调模型,视觉传输流传应用
HSI 色调空间是从人的视觉零碎登程,用色调 (Hue)、饱和度(Saturation 或 Chroma) 和亮度 (Intensity 或 Brightness)来形容色调。
H——示意色彩的相位角。红、绿、蓝别离相隔 120 度;互补色别离相差 180 度,即色彩的类别。
S——示意成所选色彩的纯度和该色彩最大的纯度之间的比率,范畴:[0, 1],即色彩的深浅水平。
I——示意色调的亮堂水平,范畴:[0, 1],人眼对亮度很敏感!
能够看到 HSI 色调空间和 RGB 色调空间只是同一物理量的不同表示法,因此它们之间存在着转换关系:HSI 色彩模式中的色调应用色彩类别示意,饱和度与色彩的白光光亮亮度刚好成反比,代表灰色与色调的比例,亮度是色彩的绝对明暗水平。
(3)CMYK 模型,用于印刷品依附反光的色调模式
CMYK 是一种依附反光的色调模式,咱们是怎么浏览报纸的内容呢?是由阳光或灯光照射到报纸上,再反射到咱们的眼中,才看到内容。它须要有外界光源,如果你在光明房间内是无奈浏览报纸的。只有在屏幕上显示的图像,就是 RGB 模式体现的。只有是在印刷品上看到的图像,就是 CMYK 模式体现的。大多数在纸上沉积黑白颜料的设施,如彩色打印机和复印机,要求输出 CMY 数据,在外部进行 RGB 到 CMY 的转换。
青色 Cyan、品红色 Magenta、黄色 Yellow 是光的二次色,是颜料的色彩。而 K 取的是 black 最初一个字母,之所以不取首字母,是为了防止与蓝色 (Blue) 混同。当红绿蓝三原色被混合时,会产生红色,当混合青色、品红色、黄色三原色时会产生彩色。从实践上来说,只须要 CMY 三种油墨就足够了,然而因为目前制作工艺还不能造出高纯度的油墨,CMY 相加的后果理论是一种暗红色。
色调 (Hue) 形容的是整体的色彩成果,在三大色调模型当中,HSI 色调模型是能较好靠近直觉概念上的色调,饱和度,亮度的分割。在这方面 rgb 格局不不便计算,这里倡议切换到 YIQ 色调空间进行计算
fragment shader
precision highp float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform mediump float hueAdjust;
const highp vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
const highp vec4 kRGBToI = vec4 (0.595716, -0.274453, -0.321263, 0.0);
const highp vec4 kRGBToQ = vec4 (0.211456, -0.522591, 0.31135, 0.0);
const highp vec4 kYIQToR = vec4 (1.0, 0.9563, 0.6210, 0.0);
const highp vec4 kYIQToG = vec4 (1.0, -0.2721, -0.6474, 0.0);
const highp vec4 kYIQToB = vec4 (1.0, -1.1070, 1.7046, 0.0);
void main ()
{
// Sample the input pixel
highp vec4 color = texture2D(inputImageTexture, textureCoordinate);
// Convert to YIQ
highp float YPrime = dot (color, kRGBToYPrime);
highp float I = dot (color, kRGBToI);
highp float Q = dot (color, kRGBToQ);
// Calculate the hue and chroma
highp float hue = atan (Q, I);
highp float chroma = sqrt (I * I + Q * Q);
// Make the user's adjustments
hue += (-hueAdjust);
// Convert back to YIQ Q = chroma * sin (hue);
I = chroma * cos (hue);
// Convert back to RGB
highp vec4 yIQ = vec4 (YPrime, I, Q, 0.0);
color.r = dot (yIQ, kYIQToR);
color.g = dot (yIQ, kYIQToG);
color.b = dot (yIQ, kYIQToB);
// Save the result
gl_FragColor = color;
};
小结
Github 代码