相機之貼紙

相機之使用OpenGL預(yù)覽
相機之使用OpenGL拍照
相機之使用OpenGL錄像
相機之為錄像添加音頻
相機之大眼

相機貼紙效果

和大眼效果一樣,需要進行人臉識別。將貼紙圖片作為一個紋理Id加載,再通過相機數(shù)據(jù)的回調(diào),進行人臉識別,定位到人臉位置,將紋理Id畫在對應(yīng)位置就可以了,需要注意的是,需要開啟混合,將貼紙和相機畫面融合在一起

著色器

著色器部分沒有什么值得注意的,只是將之前的相機畫面采樣而已

頂點著色器

attribute vec4 a_Position;
attribute vec2 a_TextureCoord;
varying vec2 v_TextureCoord;

void main() {
    gl_Position=a_Position;
    v_TextureCoord=a_TextureCoord;
}

片段著色器

precision mediump float;

uniform sampler2D vTexture;
varying vec2 v_TextureCoord;

void main() {
    gl_FragColor=texture2D(vTexture, v_TextureCoord);
}

著色器封裝程序

class StickerFilter(val context: Context, width: Int, height: Int) :
    FboFilter(context, R.raw.base_vertex, R.raw.base_frag, width, height) {

    private lateinit var matrix: FloatArray
    private var faceRectF: RectF = RectF()

    // 貼紙紋理Id
    private val stickerTextureId = TextureUtil.loadTexture(context, R.drawable.ears)

    // 貼紙的寬高比例
    private val whRatio = BitmapFactory.Options().run {
        inJustDecodeBounds = true
        BitmapFactory.decodeResource(context.resources, R.drawable.ears, this)
        outWidth.toFloat() / outHeight.toFloat()
    }

    // 貼紙頂點信息
    private val stickerVertexArray = VertexArray(FloatArray(16))

    override fun onDrawInFBO(textureId: Int) {
        // 先將textureId對應(yīng)的畫面繪制到當前FBO中
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
        GLES20.glUniform1i(vTexture, 0)
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)

        // 開始繪制貼紙
        // 開啟混合模式
        GLES20.glEnable(GLES20.GL_BLEND)
        GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA)

        // 得到的人臉數(shù)據(jù)是向右側(cè)著的,因此需要將頂點逆時針旋轉(zhuǎn)90度
        val faceLeftTop = getRotatePos(faceRectF.right, faceRectF.top)
        val faceRightTop = getRotatePos(faceRectF.right, faceRectF.bottom)

        // 按照人臉框頂部兩個點和圖片比例決定耳朵貼紙的高度
        val stickerHeight = abs(faceRectF.right - faceRectF.left) / whRatio
        // 人臉框上面的兩個頂點,是貼紙底部的兩個頂點
        val stickerRightTop = floatArrayOf(faceRightTop[0], faceRightTop[1] + stickerHeight)
        val stickerLeftTop = floatArrayOf(faceLeftTop[0], faceLeftTop[1] + stickerHeight)

        // 繪制貼紙,如果貼紙方向不對,按照之前組裝三角形的順序,例如Z字形,對比著錯誤的貼紙界面,按Z字形的順序依次擺放上正確的頂點位置
        val rotatedVertex = floatArrayOf(
            faceLeftTop[0], faceLeftTop[1],
            faceRightTop[0], faceRightTop[1],
            stickerLeftTop[0], stickerLeftTop[1],
            stickerRightTop[0], stickerRightTop[1]
        )
        stickerVertexArray.update(rotatedVertex, 0, rotatedVertex.size)
        stickerVertexArray.setVertexAttribPointer(0, aPosition, POSITION_COUNT, 0)
        
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stickerTextureId)
        GLES20.glUniform1i(vTexture, 0)
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)

        GLES20.glDisable(GLES20.GL_BLEND)
    }

    /**
     * 獲取旋轉(zhuǎn)后的人臉框位置
     * @param index Int
     * @return FloatArray
     */
    private fun getRotatePos(x: Float, y: Float): FloatArray {
        val vertex = floatArrayOf(x, y, 0f, 1f)
        val vertexResult = FloatArray(4)
        // 因為發(fā)現(xiàn)人臉數(shù)據(jù)是向左側(cè)著的,因此位置信息需要逆時針旋轉(zhuǎn)90度
        Matrix.multiplyMV(vertexResult, 0, matrix, 0, vertex, 0)
        return vertexResult
    }

    /**
     * 設(shè)置人臉框
     * @param faceRectF RectF
     */
    fun setFaceRectF(faceRectF: RectF) {
        this.faceRectF.set(faceRectF)
    }

    /**
     * 設(shè)置變換矩陣,否則人臉位置是旋轉(zhuǎn)90的
     * @param matrix FloatArray
     */
    fun setUniforms(matrix: FloatArray) {
        this.matrix = matrix
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容