相机之使用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
}
}