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