Android OpenGLES滤镜开发之贴纸效果

前言

上一篇中写到了如何实现放大眼睛的效果,这一篇实现贴纸效果,就像那个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);

很多都在代码里进行了注释,应该都可以看得懂,下面就看一下效果图吧~~

效果图

就差不多是这样的效果,是因为我手机问题所有才模糊不清楚的,前置摄像头完成没有问题的。

猜你喜欢

转载自blog.csdn.net/YuQing_Cat/article/details/84070361