相机之口红

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

口红效果

识别到嘴唇位置,在上下嘴唇处绘制两个三角形带,分别代表上下嘴唇,如果要做成渐变的,使用 varying 定义颜色即可

着色器

顶点着色器

attribute vec4 a_Position;
uniform mat4 u_Matrix;
attribute vec4 a_Color;

varying vec4 v_Color;
void main() {
    v_Color=a_Color;
    gl_Position=u_Matrix * a_Position;
    gl_PointSize = 8.0;
}

片段着色器

precision mediump float;

varying vec4 v_Color;

void main() {
    gl_FragColor=v_Color;
}

着色器程序

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

    private lateinit var matrix: FloatArray

    // 绘制嘴唇部分的顶点序号
    private val indexArray = intArrayOf(
        // 上嘴唇
        45, 51, 53, 49, 47, 46, 47, 48, 52, 50, 44,
        // 下嘴唇
        45, 61, 57, 60, 54, 55, 54, 59, 56, 58, 44
    )

    private val BYTES_PER_FLOAT = 4
    private val POSITION_COUNT = 2
    private val COLOR_COUNT = 4
    private val TOTAL_COUNT = POSITION_COUNT + COLOR_COUNT
    private val STRIDE = TOTAL_COUNT * BYTES_PER_FLOAT

    // 存储嘴唇部分的顶点,每个点保存位置(x,y)+颜色(r,g,b,a)信息
    private var faceArray = FloatArray(indexArray.size * TOTAL_COUNT)

    // 将Java堆内存的顶点数据复制到本地堆
    private var faceVertexArray: VertexArray = VertexArray(faceArray)

    // 利用顶点着色器和片段着色器生成OpenGL程序
    private val lipProgram = ProgramUtil.getProgram(context, R.raw.lip_vertex, R.raw.lip_frag)
    private val u_Matrix = GLES20.glGetUniformLocation(lipProgram, "u_Matrix")
    private val a_Color = GLES20.glGetAttribLocation(lipProgram, "a_Color")
    private val a_Position = GLES20.glGetAttribLocation(lipProgram, "a_Position")

    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)
        GLES20.glEnable(GL10.GL_MULTISAMPLE)
        GLES20.glUseProgram(lipProgram)

        // 设置关键点变换矩阵
        GLES20.glUniformMatrix4fv(u_Matrix, 1, false, matrix, 0)
        // 设置顶点数组
        faceVertexArray.setVertexAttribPointer(0, a_Position, POSITION_COUNT, STRIDE)
        faceVertexArray.setVertexAttribPointer(POSITION_COUNT, a_Color, COLOR_COUNT, STRIDE)
        // 通过三角形带绘制口红,分为上下两个三角形带
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, indexArray.size / 2)
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, indexArray.size / 2, indexArray.size / 2)

        GLES20.glDisable(GLES20.GL_BLEND)
    }

    /**
     * 更新人脸顶点位置
     * @param facePosition FloatArray
     */
    fun setFaceInfo(facePosition: FloatArray) {
        for (i in indexArray.indices) {
            // 位置xy
            faceArray[i * TOTAL_COUNT] = facePosition[indexArray[i] * 2]
            faceArray[i * TOTAL_COUNT + 1] = facePosition[indexArray[i] * 2 + 1]
            // 颜色rgba
            // 上嘴唇时, i 是偶数,当前顶点是嘴唇外侧,颜色浅一点
            // 下嘴唇时, i 是奇数,当前顶点是嘴唇外侧,颜色浅一点
            var parseColor =
                if ((i % 2 == 0 && i < indexArray.size / 2) || (i % 2 != 0 && i >= indexArray.size / 2)) {
                    Color.parseColor("#C62948")
                } else {
                    Color.parseColor("#DE596E")
                }
            faceArray[i * TOTAL_COUNT + 2] = Color.red(parseColor) / 255f
            faceArray[i * TOTAL_COUNT + 3] = Color.green(parseColor) / 255f
            faceArray[i * TOTAL_COUNT + 4] = Color.blue(parseColor) / 255f
            faceArray[i * TOTAL_COUNT + 5] = 0.3f
        }
        faceVertexArray.update(faceArray, 0, faceArray.size)
    }

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

你可能感兴趣的:(相机之口红)