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