iOS视觉-- (12) OpenGL ES+GLSL实现口红和腮红

前面我们学习了大眼和瘦脸技巧,接下来我们来学习口红和腮红。口红和腮红实现原理是一致的。下边只会对口红的实现进行分析。
借鉴博客:《Android 美颜类相机开发汇总》第六章 Android OpenGLES 美妆定制实现
借鉴项目:AwemeLike
效果展示:

效果图.gif

实现的方法,我们很容易想到就是把唇印纹理绘制到嘴巴的位置就可以了。
那么实现的步骤就像下面一样:

图1

第一步不做详细介绍就是视频帧的渲染而已。重点是第二步的实现。接下来我们进行第二步的解析。

  • 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

你可能感兴趣的:(iOS视觉-- (12) OpenGL ES+GLSL实现口红和腮红)