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);
很多都在程式碼裡進行了註釋,應該都可以看得懂,下面就看一下效果圖吧~~
效果圖
就差不多是這樣的效果,是因為我手機問題所有才模糊不清楚的,前置攝像頭完成沒有問題的。