1. 程式人生 > >Android OpenGLES濾鏡開發之貼紙效果

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);

很多都在程式碼裡進行了註釋,應該都可以看得懂,下面就看一下效果圖吧~~

效果圖

就差不多是這樣的效果,是因為我手機問題所有才模糊不清楚的,前置攝像頭完成沒有問題的。