相机之贴纸

相机之使用OpenGL预览
相机之使用OpenGL拍照
相机之使用OpenGL录像
相机之为录像添加音频
相机之大眼

相机贴纸效果

和大眼效果一样,需要进行人脸识别。将贴纸图片作为一个纹理Id加载,再通过相机数据的回调,进行人脸识别,定位到人脸位置,将纹理Id画在对应位置就可以了,需要注意的是,需要开启混合,将贴纸和相机画面融合在一起

着色器

着色器部分没有什么值得注意的,只是将之前的相机画面采样而已

顶点着色器

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对应的画面绘制到当前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)

        // 得到的人脸数据是向右侧着的,因此需要将顶点逆时针旋转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)
    }

    /**
     * 获取旋转后的人脸框位置
     * @param index Int
     * @return FloatArray
     */
    private fun getRotatePos(x: Float, y: Float): FloatArray {
        val vertex = floatArrayOf(x, y, 0f, 1f)
        val vertexResult = FloatArray(4)
        // 因为发现人脸数据是向左侧着的,因此位置信息需要逆时针旋转90度
        Matrix.multiplyMV(vertexResult, 0, matrix, 0, vertex, 0)
        return vertexResult
    }

    /**
     * 设置人脸框
     * @param faceRectF RectF
     */
    fun setFaceRectF(faceRectF: RectF) {
        this.faceRectF.set(faceRectF)
    }

    /**
     * 设置变换矩阵,否则人脸位置是旋转90的
     * @param matrix FloatArray
     */
    fun setUniforms(matrix: FloatArray) {
        this.matrix = matrix
    }
}

你可能感兴趣的:(相机之贴纸)