前面我们学习了大眼和瘦脸技巧,接下来我们来学习口红和腮红。口红和腮红实现原理是一致的。下边只会对口红的实现进行分析。
借鉴博客:《Android 美颜类相机开发汇总》第六章 Android OpenGLES 美妆定制实现
借鉴项目:AwemeLike
效果展示:
实现的方法,我们很容易想到就是把唇印纹理绘制到嘴巴的位置就可以了。
那么实现的步骤就像下面一样:
第一步不做详细介绍就是视频帧的渲染而已。重点是第二步的实现。接下来我们进行第二步的解析。
- 1. 如何定位嘴巴的位置?
//手动对齐
tCoordinates[i2+0] = GLfloat((faceTextureCoordinates[i2+0] * 1280 - Float(imageBounds.origin.x)) / Float(imageBounds.size.width))
这里相当于嘴巴纹理在1280x1280,对应的纹理坐标转换//x = (1280 - 262.5) / 2 = 508.75 - 7.5(由于图片中心向右偏移6px = 3pt * 2.5) = 501.25
//y = (1280 - 167.5) / 2 = 556.25� //手动对齐
let mouthImageBounds = CGRect(x: 501.25, y: 710, width: 262.5, height: 167.5) //w/h = 1.567164 scale = 2.5 原图:105/67
部分核心代码
let size = 111 * 2
var tempPoint: [GLfloat] = [GLfloat].init(repeating: 0, count: size)
var index = 0
for i in 0..
人脸识别SDK会返回面部特征顶点后,然后绘制出人脸的位置和口红的位置。
这里会看见脸的部分还是黑色的,我们还要绘制上摄像头。如下着色器代码:
precision mediump float;
varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
uniform float intensity;
uniform int blendMode;
float blendHardLight(float base, float blend) {
return blend<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));
}
vec3 blendHardLight(vec3 base, vec3 blend) {
return vec3(blendHardLight(base.r,blend.r),blendHardLight(base.g,blend.g),blendHardLight(base.b,blend.b));
}
float blendSoftLight(float base, float blend) {
return (blend<0.5)?(base+(2.0*blend-1.0)*(base-base*base)):(base+(2.0*blend-1.0)*(sqrt(base)-base));
}
vec3 blendSoftLight(vec3 base, vec3 blend) {
return vec3(blendSoftLight(base.r,blend.r),blendSoftLight(base.g,blend.g),blendSoftLight(base.b,blend.b));
}
vec3 blendMultiply(vec3 base, vec3 blend) {
return base*blend;
}
float blendOverlay(float base, float blend) {
return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));
}
vec3 blendOverlay(vec3 base, vec3 blend) {
return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b));
}
vec3 blendFunc(vec3 base, vec3 blend, int blendMode) {
if (blendMode == 0) {
return blend;
} else if (blendMode == 15) {
return blendMultiply(base, blend);
} else if (blendMode == 17) {
return blendOverlay(base, blend);
} else if (blendMode == 22) {
return blendHardLight(base, blend);
}
return blend;
}
void main()
{
vec4 fgColor = texture2D(inputImageTexture2, textureCoordinate);//这里是口红/腮红纹理
fgColor = fgColor * intensity;
vec4 bgColor = texture2D(inputImageTexture, vec2(textureCoordinate2.x, textureCoordinate2.y));//视频帧纹理
if (fgColor.a == 0.0) {//透明的部分返回视频帧纹理
gl_FragColor = bgColor;
return;
}
//两种纹理混合叠加后的效果
vec3 color = blendFunc(bgColor.rgb, clamp(fgColor.rgb * (1.0 / fgColor.a), 0.0, 1.0), blendMode);
// color = color * intensity;
//
gl_FragColor = vec4(bgColor.rgb * (1.0 - fgColor.a) + color.rgb * fgColor.a, 1.0);
}
AwemeLike项目,作者使用的是OpenGL Blend方式 + shader Blend方式
进行实现。在GPUImageFaceMarkupFilter
文件中, 如下图
但是在实现中发现只有人脸部分,而其他都是黑色。后面不行。想到用之前的方式:前一个的输出作为下一个的输入,和之前大眼瘦脸一样的方式。如下图
但是这里绘制同样的套路绘制腮红的时候。图像反了。。修改了一下片元着色器代码。有两份差不多一样的代码,就是做了一个翻转操作。
到此就结束。难点在于位置对应和混合操作
本文Demo: 码云、GitHub