在这篇文章主要用到的知识点有如下,建议先看一下:
有一个渲染流数据的相关的示例,也可以看一下,这样对本篇理解就会很简单 :
Android OpenGLES渲染MediaCodec解码数据
原理
利用
OpenGL
生成纹理并绑定到SurfaceTexture
,然后把camera
的预览数据设置显示到SurfaceTexture
中,这样就可以在OpenGL
中拿到摄像头数据并显示了。
主要步骤
1.
OpenGL ES
生成纹理
2.OpenGL ES
创建SurfaceTexture
并绑定
3.OpenGL ES
摄像头预览
比如美颜相机那些,处理摄像头数据展示出来,为了提高预览的效率,所以这里使用了VBO
和FBO
,如果不知道这个,请看上面的文章。
FBO
所需的glsl
:
vertex_shader.glsl
attribute vec4 av_Position;//顶点位置
attribute vec2 af_Position;//纹理位置
varying vec2 v_texPo;//纹理位置 与fragment_shader交互
uniform mat4 u_Matrix;//矩阵变换
void main() {
v_texPo = af_Position;
gl_Position = av_Position * u_Matrix;
}
fragment_shader.glsl
#extension GL_OES_EGL_image_external : require //申明使用扩展纹理
precision mediump float;//精度 为float
varying vec2 v_texPo;//纹理位置 接收于vertex_shader
uniform samplerExternalOES sTexture;//加载流数据(摄像头数据)
void main() {
gl_FragColor=texture2D(sTexture, v_texPo);
}
这里使用FBO
里面使用samplerExternalOES
是为了加载流数据,摄像头数据属于流数据,所以这里需要用这个。
创建相机预览扩展纹理:
/**
* 创建摄像头预览扩展纹理
*/
private void createCameraRenderTexture() {
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
cameraRenderTextureId = textureIds[0];
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraRenderTextureId);
//环绕(超出纹理坐标范围) (s==x t==y GL_REPEAT 重复)
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//过滤(纹理像素映射到坐标点) (缩小、放大:GL_LINEAR线性)
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
surfaceTexture = new SurfaceTexture(cameraRenderTextureId);
surfaceTexture.setOnFrameAvailableListener(this);
if (onSurfaceListener != null) {
//这里相机拿到surfaceTexture绑定
onSurfaceListener.onSurfaceCreate(surfaceTexture);
}
// 解绑扩展纹理
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
}
camera绑定SurfaceTexture:
camera.setPreviewTexture(surfaceTexture);
预览画面,先通过fbo
处理,然后拿到fbo
的纹理id
渲染即可:
@Override
public void onDrawFrame() {
//调用触发onFrameAvailable
surfaceTexture.updateTexImage();
//清空颜色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//设置背景颜色
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
//使用程序
GLES20.glUseProgram(program);
//绑定fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//摄像头预览扩展纹理赋值
GLES20.glActiveTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraRenderTextureId);
GLES20.glUniform1i(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
//给变换矩阵赋值
GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glEnableVertexAttribArray(afPosition);
//使用VBO设置纹理和顶点值
useVboSetVertext();
//绘制 GLES20.GL_TRIANGLE_STRIP:复用坐标
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(avPosition);
GLES20.glDisableVertexAttribArray(afPosition);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
//渲染显示
cameraRender.onDraw(fboTextureId);
}
摄像头方向调整
默认的摄像头预览不同的角度预览出来效果是不同的,我们需要把它给矫正,一般通常是在camera
里面设置parms
,这里通过OpenGLES
自己矫正,通过变换矩阵实现即可:
//变换矩阵 location
private int uMatrix;
//变换矩阵
private float[] matrix = new float[16];
public void onSurfaceCreated() {
//...
uMatrix = GLES20.glGetUniformLocation(program, "u_Matrix");
}
@Override
public void onDrawFrame() {
//...
//给变换矩阵赋值
GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);
}
/**
* 初始化矩阵
*/
public void resetMatirx() {
//初始化
Matrix.setIdentityM(matrix, 0);
}
/**
* 旋转
*
* @param angle
* @param x
* @param y
* @param z
*/
public void setAngle(float angle, float x, float y, float z) {
//旋转
Matrix.rotateM(matrix, 0, angle, x, y, z);
}
在外层调用:
public void previewAngle(Context context) {
int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
render.resetMatirx();
switch (angle) {
case Surface.ROTATION_0:
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
render.setAngle(90, 0, 0, 1);
render.setAngle(180, 1, 0, 0);
} else {
render.setAngle(90f, 0f, 0f, 1f);
}
break;
case Surface.ROTATION_90:
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
render.setAngle(180, 0, 0, 1);
render.setAngle(180, 0, 1, 0);
} else {
render.setAngle(90f, 0f, 0f, 1f);
}
break;
case Surface.ROTATION_180:
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
render.setAngle(90f, 0.0f, 0f, 1f);
render.setAngle(180f, 0.0f, 1f, 0f);
} else {
render.setAngle(-90, 0f, 0f, 1f);
}
break;
case Surface.ROTATION_270:
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
render.setAngle(180f, 0.0f, 1f, 0f);
} else {
render.setAngle(0f, 0f, 0f, 1f);
}
break;
}
}