在這篇文章主要用到的知識點(diǎn)有如下儒喊,建議先看一下:
有一個(gè)渲染流數(shù)據(jù)的相關(guān)的示例疤剑,也可以看一下无拗,這樣對本篇理解就會很簡單 :
Android OpenGLES渲染MediaCodec解碼數(shù)據(jù)
原理
利用
OpenGL
生成紋理并綁定到SurfaceTexture
,然后把camera
的預(yù)覽數(shù)據(jù)設(shè)置顯示到SurfaceTexture
中茎用,這樣就可以在OpenGL
中拿到攝像頭數(shù)據(jù)并顯示了绝淡。
主要步驟
1.
OpenGL ES
生成紋理
2.OpenGL ES
創(chuàng)建SurfaceTexture
并綁定
3.OpenGL ES
攝像頭預(yù)覽
比如美顏相機(jī)那些,處理攝像頭數(shù)據(jù)展示出來锄贷,為了提高預(yù)覽的效率译蒂,所以這里使用了VBO
和FBO
,如果不知道這個(gè),請看上面的文章谊却。
FBO
所需的glsl
:
vertex_shader.glsl
attribute vec4 av_Position;//頂點(diǎn)位置
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 //申明使用擴(kuò)展紋理
precision mediump float;//精度 為float
varying vec2 v_texPo;//紋理位置 接收于vertex_shader
uniform samplerExternalOES sTexture;//加載流數(shù)據(jù)(攝像頭數(shù)據(jù))
void main() {
gl_FragColor=texture2D(sTexture, v_texPo);
}
這里使用FBO
里面使用samplerExternalOES
是為了加載流數(shù)據(jù)柔昼,攝像頭數(shù)據(jù)屬于流數(shù)據(jù),所以這里需要用這個(gè)炎辨。
創(chuàng)建相機(jī)預(yù)覽擴(kuò)展紋理:
/**
* 創(chuàng)建攝像頭預(yù)覽擴(kuò)展紋理
*/
private void createCameraRenderTexture() {
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
cameraRenderTextureId = textureIds[0];
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraRenderTextureId);
//環(huán)繞(超出紋理坐標(biāo)范圍) (s==x t==y GL_REPEAT 重復(fù))
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);
//過濾(紋理像素映射到坐標(biāo)點(diǎn)) (縮小捕透、放大: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) {
//這里相機(jī)拿到surfaceTexture綁定
onSurfaceListener.onSurfaceCreate(surfaceTexture);
}
// 解綁擴(kuò)展紋理
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
}
camera綁定SurfaceTexture:
camera.setPreviewTexture(surfaceTexture);
預(yù)覽畫面,先通過fbo
處理,然后拿到fbo
的紋理id
渲染即可:
@Override
public void onDrawFrame() {
//調(diào)用觸發(fā)onFrameAvailable
surfaceTexture.updateTexImage();
//清空顏色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//設(shè)置背景顏色
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
//使用程序
GLES20.glUseProgram(program);
//綁定fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//攝像頭預(yù)覽擴(kuò)展紋理賦值
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設(shè)置紋理和頂點(diǎn)值
useVboSetVertext();
//繪制 GLES20.GL_TRIANGLE_STRIP:復(fù)用坐標(biāo)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(avPosition);
GLES20.glDisableVertexAttribArray(afPosition);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
//渲染顯示
cameraRender.onDraw(fboTextureId);
}
攝像頭方向調(diào)整
默認(rèn)的攝像頭預(yù)覽不同的角度預(yù)覽出來效果是不同的乙嘀,我們需要把它給矯正末购,一般通常是在camera
里面設(shè)置parms
,這里通過OpenGLES
自己矯正虎谢,通過變換矩陣實(shí)現(xiàn)即可:
//變換矩陣 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);
}
/**
* 旋轉(zhuǎn)
*
* @param angle
* @param x
* @param y
* @param z
*/
public void setAngle(float angle, float x, float y, float z) {
//旋轉(zhuǎn)
Matrix.rotateM(matrix, 0, angle, x, y, z);
}
在外層調(diào)用:
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;
}
}