前言
上一篇中写到了如何实现放大眼睛的效果,这一篇实现贴纸效果,就像那个faceu相机,b612相机,还有抖音都会有的这种贴纸效果。
思路
1、贴纸肯定也是需要定位到人脸的
2、找到贴纸需要放置的位置
3、将贴纸纹理和人本身纹理进行融合
实现
人脸定位啥的,我就不说了,不清楚的可以去前面的文章看看,主要来看看贴纸是如何贴上去的
1. 创建贴纸的纹理
//OpenGL 纹理
mTextureId = new int[1];
//OpenGLUtils是个OpenGL的工具类,具体的前面也有写
OpenGLUtils.glGenTextures(mTextureId);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureId[0]);
//将bitmap与纹理的id绑定起来
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,mBitmap,0);
//解绑
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
2. 画贴纸
在画贴纸之前,是已经将之前摄像头那些纹理已经画上去过了,然后再来画贴纸。
//开启混合模式:将多张图片进行混合(贴图)
GLES20.glEnable(GLES20.GL_BLEND);
//设置贴图模式
//1.src 源图因子 要画的是源(耳朵)
//2.dst 已经画好的目标,也就是我们要往哪儿画,也就是其他filter的图像
//GLES20.GL_ONE 原本是什么样子,就画成什么样子
//GLES20.GL_ONE_MINUS_SRC_ALPHA,表示用1.0减去源颜色alpha的值来作为因子
GLES20.glBlendFunc(GLES20.GL_ONE,GLES20.GL_ONE_MINUS_SRC_ALPHA);
因为在OpenGL中如果不开启混合模式,就会把之前的纹理覆盖掉,这里就不会显示上一个纹理了。
什么是混合?混合就是把某一个像素点的位置原来的颜色与将要画上去的颜色,以某种方式混合在一起,从而达到某种特殊的效果。我们这里就需要将贴纸的纹理和人脸的纹理进行一个混合。
glBlendFunc的参数设置有多种模式,第一个参数表示的是源图因子,也就是我们要画上去的贴纸,第二个参数是目标因子,也就是我们要把贴纸画到哪儿去。这两个参数有多种值:
GL_ZERO:表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
GL_ONE: 表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。
这个源图因子使用的是完全使用,也就是贴纸是完全展示出来的,目标因子是用1.0 - 贴纸的alpha值来作为因子的。
下面就是计算出贴纸所要显示的位置,然后将坐标信息传递给着色器
//画耳朵,是需要往人脸上画,不是全屏画
float x = mFace.landmarks[0];
float y =mFace.landmarks[1];
//转换为要画到屏幕上的宽、高
x = x / mFace.imgWidth * mOutputWidth;
y = y/ mFace.imgHeight * mOutputHeight;
//贴纸需要显示的位置
//1.需要显示到屏幕上的x坐标,2.y坐标
//3.需要显示的贴纸的宽 4.高,这两个参数需要不断的调试,然后获得比较合适的
GLES20.glViewport((int)x, (int)y-mBitmap.getHeight()/2,(int) ((float)mFace.width /mFace.imgWidth * mOutputWidth)
,mBitmap.getHeight());
LES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,mFrameBuffers[0]);
GLES20.glUseProgram(mGLProgramId);
//传递坐标
mGLVertexBuffer.position(0);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGLVertexBuffer);
GLES20.glEnableVertexAttribArray(vPosition);
mGLTextureBuffer.position(0);
GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGLTextureBuffer);
GLES20.glEnableVertexAttribArray(vCoord);
//激活纹理,传递纹理给着色器
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureId[0]);
GLES20.glUniform1i(vTexture,0);
//画画
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);
// 解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
//解绑FRAMEBUFFER
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
//最后使用完毕以后需要关闭这个融合
GLES20.glDisable(GLES20.GL_BLEND);
很多都在代码里进行了注释,应该都可以看得懂,下面就看一下效果图吧~~
效果图
就差不多是这样的效果,是因为我手机问题所有才模糊不清楚的,前置摄像头完成没有问题的。