- iOS视觉-- (12) OpenGL ES+GLSL实现口红和
- iOS视觉-- (11) OpenGL ES+GLSL实现大眼和
- iOS视觉-- (10) OpenGL ES+GLSL实现YUV
- iOS视觉-- (08) OpenGL ES+GLSL实现分屏滤
- iOS视觉-- (06) OpenGL ES+GLSL实现灰度滤
- iOS视觉-- (07) OpenGL ES+GLSL实现多滤镜
- iOS视觉-- (09) OpenGL ES+GLSL实现摄像头
- iOS视觉-- (04) OpenGL ES+GLSL实现金字塔
- iOS视觉-- (05) OpenGL ES+GLSL实现正方体
- iOS视觉-- (03) OpenGL ES+GLSL如何渲染图
前面我们学习了大眼和瘦脸技巧,接下来我们来学习口红和腮红。口红和腮红实现原理是一致的。下边只会对口红的实现进行分析。
借鉴博客:《Android 美颜类相机开发汇总》第六章 Android OpenGLES 美妆定制实现
借鉴项目:AwemeLike
效果展示:
![](https://img.haomeiwen.com/i6454145/295cb35395bec92f.gif)
实现的方法,我们很容易想到就是把唇印纹理绘制到嘴巴的位置就可以了。
那么实现的步骤就像下面一样:
![](https://img.haomeiwen.com/i6454145/0ee7d8830ef5b241.png)
第一步不做详细介绍就是视频帧的渲染而已。重点是第二步的实现。接下来我们进行第二步的解析。
- 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..<faceInfo.landmarks.count {
let point = faceInfo.landmarks[i].cgPointValue
tempPoint[i*2+0] = GLfloat(point.x * 2 - 1)
tempPoint[i*2+1] = GLfloat(point.y * 2 - 1)
index += 2
if (index == size) {
break
}
}
//注意:第二参数字符串必须和shaderv.vsh中的输入变量:position保持一致
let position = glGetAttribLocation(mouthFaceMarkupProgram, "position")
glEnableVertexAttribArray(GLuint(position))
glVertexAttribPointer(GLuint(position), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, tempPoint)
//----处理纹理数据-------
let tSize = 111 * 2
let pointCount = faceTextureCoordinates.count / 2
var tCoordinates: [GLfloat] = [GLfloat].init(repeating: 0, count: tSize)
for i in 0..<pointCount {
//手动对齐
tCoordinates[i*2+0] = GLfloat((faceTextureCoordinates[i*2+0] * 1280 - Float(imageBounds.origin.x)) / Float(imageBounds.size.width))
tCoordinates[i*2+1] = GLfloat((faceTextureCoordinates[i*2+1] * 1280 - Float(imageBounds.origin.y)) / Float(imageBounds.size.height))
}
let textCoord = glGetAttribLocation(mouthFaceMarkupProgram, "inputTextureCoordinate")
//设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(GLuint(textCoord))
glVertexAttribPointer(GLuint(textCoord), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, tCoordinates)
glActiveTexture(GLenum(GL_TEXTURE0))
glBindTexture(GLenum(GL_TEXTURE_2D), bgTexture)//mouthFaceMarkupTexture CVOpenGLESTextureGetName(texture!)
glUniform1i(glGetUniformLocation(self.mouthFaceMarkupProgram, "inputImageTexture"), 0) //单个纹理可以不用设置
glActiveTexture(GLenum(GL_TEXTURE3))
glBindTexture(GLenum(GL_TEXTURE_2D), effectTexture)
glUniform1i(glGetUniformLocation(self.mouthFaceMarkupProgram, "inputImageTexture2"), 3) //单个纹理可以不用设置
glDrawElements(GLenum(GL_TRIANGLES), GLsizei(faceIndexs.count), GLenum(GL_UNSIGNED_INT), faceIndexs)
人脸识别SDK会返回面部特征顶点后,然后绘制出人脸的位置和口红的位置。
![](https://img.haomeiwen.com/i6454145/a5dc6a766a000723.png)
这里会看见脸的部分还是黑色的,我们还要绘制上摄像头。如下着色器代码:
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
文件中, 如下图
![](https://img.haomeiwen.com/i6454145/5654cb7d81d187de.png)
但是在实现中发现只有人脸部分,而其他都是黑色。后面不行。想到用之前的方式:前一个的输出作为下一个的输入,和之前大眼瘦脸一样的方式。如下图
![](https://img.haomeiwen.com/i6454145/1b8c98abe8a77c9b.png)
但是这里绘制同样的套路绘制腮红的时候。图像反了。。修改了一下片元着色器代码。有两份差不多一样的代码,就是做了一个翻转操作。
到此就结束。难点在于位置对应和混合操作
网友评论